App-news

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

            "ExtUtils::MakeMaker" : "0"
         }
      },
      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : "0"
         }
      },
      "runtime" : {
         "requires" : {
            "DateTime::Format::Mail" : "0",
            "Modern::Perl" : "1.20180701",
            "Mojolicious" : "9",
            "perl" : "5.026000"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "repository" : {
         "type" : "git",

META.yml  view on Meta::CPAN

license: open_source
meta-spec:
  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: '1.4'
name: App-news
no_index:
  directory:
    - t
    - inc
requires:
  DateTime::Format::Mail: '0'
  Modern::Perl: '1.20180701'
  Mojolicious: '9'
  perl: '5.026000'
resources:
  repository: https://alexschroeder.ch/cgit/news
version: 1.09
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

Makefile.PL  view on Meta::CPAN

  ABSTRACT         => 'a web front-end for a news server',
  AUTHOR           => 'Alex Schroeder',
  LICENSE          => 'agpl_3',
  MIN_PERL_VERSION => '5.26.0', # Modern::Perl '2018'
  EXE_FILES        => [
    'script/news',
  ],
  PREREQ_PM => {
    'Mojolicious' => 9.00,
    'Modern::Perl' => 1.20180701, # for '2018'
    'DateTime::Format::Mail' => 0,
  },
  META_MERGE => {
    'meta-spec' => { version => 2 },
    resources => {
      repository => {
	type => 'git',
	url  => 'https://alexschroeder.ch/cgit/news',
	web  => 'https://alexschroeder.ch/cgit/news/about/',
      },
    },

script/news  view on Meta::CPAN

GNU Affero General Public License

=cut

# corelist
use Net::NNTP;
use Encode qw(encode decode);
# not core
use Mojolicious::Lite;      # Mojolicious
use Mojo::Cache;
use DateTime::Format::Mail;
use List::Util qw(first);
use utf8;
# our own
use App::news qw(wrap html_unwrap ranges);

my $cache = Mojo::Cache->new;

get '/' => sub {
  shift->redirect_to('index');
};

script/news  view on Meta::CPAN

    my $last_page = int($last / $per_page) + 1;
    $page ||= $last_page;
    my $to = $page * $per_page;
    $to = $last if $to > $last;
    my $from = ($page - 1) * $per_page;
    $from = $first if $from < $first;
    my $fmt = $nntp->overview_fmt;
    app->log->debug("Getting $group $from-$to");
    my $messages = $nntp->xover("$from-$to");
    my $articles = [];
    my $parser = DateTime::Format::Mail->new->loose;
    for my $num (sort { $b <=> $a } keys %$messages) {
      my ($subject, $from, $date, $id, $references) = @{$messages->{$num}};
      $subject = decode("MIME-Header", $subject) || "?";
      my ($tag) = $subject =~ /\[(.*?)\]/;
      $from = no_email(decode("MIME-Header", $from));
      my $dt = $parser->parse_datetime($date);
      my $url = $c->url_for('article', group => $group, id => $num);
      $url = $url->query(edit => $edit) if $edit;
      push(@$articles, {
        id => $id,

script/news  view on Meta::CPAN

      while (@nums > $per_page and not grep { $_ == $include } @page) {
        @page = splice(@nums, -$per_page);
        $page++;
      }
      @nums = @page if @page;
    }
    my $ranges = ranges(@nums);
    my $fmt = $nntp->overview_fmt;
    my $re = quotemeta($tag);
    my $articles = [];
    my $parser = DateTime::Format::Mail->new->loose;
    for my $range (@$ranges) {
      app->log->debug("Getting $group " . (ref $range ? join("-", @$range) : $range));
      my $messages = $nntp->xover($range);
      app->log->debug("Received " . scalar(keys %$messages) . " messages");
      for my $num (sort keys %$messages) {
        my ($subject, $from, $date, $id, $references) = @{$messages->{$num}};
        $subject = decode("MIME-Header", $subject) || "?";
        $subject =~ s/\[$re\]\s*//;
        $from = no_email(decode("MIME-Header", $from));
        my $dt = $parser->parse_datetime($date);

script/news  view on Meta::CPAN

    # $article is header lines, an empty line, and body lines
    my $headers = Mojo::Headers->new;
    while ($_ = shift(@$article)) {
      $headers->parse("$_\r\n");
      last unless /\S/;
    }
    my $id = $headers->header("message-id");
    my $subject = decode("MIME-Header", $headers->header("subject")) || "?";
    my $from = no_email(decode("MIME-Header", $headers->header("from")));
    my $date = $headers->header("date");
    my $dt = DateTime::Format::Mail->new->loose->parse_datetime($date);
    $date = [$dt->ymd, sprintf("%02d:%02d", $dt->hour, $dt->minute)];
    my $newsgroups = [split(/\s*,\s*/, decode("MIME-Header", $headers->header("newsgroups")) || "")];
    $group ||= "@$newsgroups";
    my $references = [split(/\s+/, decode("MIME-Header", $headers->header("references")) || "")];
    my $body = join("", @$article);
    $body =~ s/\s*<\S*?@\S*?>//g; # remove email addresses
    $body =~ s/\s*"\S*?@\S*?"//g; # remove email addresses
    if ($headers->header('content-type')) {
      my ($charset) = $headers->header('content-type') =~ /charset=['"]?([^;'"]*)/;
      $body = decode($charset, $body) if $charset;

script/news  view on Meta::CPAN

} => 'post';

get '/latest' => sub {
  my $c = shift;
  my $list = cached("news " . ($ENV{NEWS_GROUPS} || "*"), sub {
    my $nntp = Net::NNTP->new() or return 'error';
    my $since = time() - 7 * 24 * 60 * 60; # one week
    my $ids = $nntp->newnews($since, $ENV{NEWS_GROUPS} || "*");
    $ids = [@$ids[$#$ids - $per_page .. $#$ids]] if @$ids > $per_page;
    my $articles = [];
    my $parser = DateTime::Format::Mail->new->loose;
    for my $id (@$ids) {
      my $head = $nntp->head($id);
      next unless $head;
      my $headers = Mojo::Headers->new;
      for my $line (@$head) {
        $headers->parse("$line\r\n");
      }
      $headers->parse("\r\n"); # make sure it finishes correctly
      my $subject = decode("MIME-Header", $headers->header("subject")) || "?";
      my ($tag) = $subject =~ /\[(.*?)\]/;



( run in 0.420 second using v1.01-cache-2.11-cpan-05444aca049 )