commit 16d0d03f980f192b1d5fbb3efbce9ed1aaf2f1ed from: noodle date: Thu Jan 15 18:05:11 2026 UTC add RSS feature pointing to analognowhere.com commit - 3af4a1f76a207c8113cce7539b513a3af26dff8d commit + 16d0d03f980f192b1d5fbb3efbce9ed1aaf2f1ed blob - 9579989b001f202a240eeb511659dd4d28608520 blob + 7ae4d09317a961dd6028db9384ccd6f4abd07b84 --- mata_bot.pl +++ mata_bot.pl @@ -13,6 +13,7 @@ use constant { CONNECT_TIMEOUT => 60, CRLF => 2, # RFC 2812 DEFAULT_PORT => 6667, + DEFAULT_RSS => 0, DEFAULT_TLS => 0, HOSTMAX => 63, # RFC 2812 IRCMAX => 512, @@ -21,6 +22,7 @@ use constant { NBIBLE => 31102, NQURAN => 6348, RECONN_SLEEP => 60, + RSS_CHECK_TIME => 3600, SOCK_TIMEOUT => 10, }; my $DEFAULT_CHAN = '#testmatabot'; @@ -39,6 +41,8 @@ my $MYNICK = 'mata_bot'; my $MYREAL = 'death to technomage!!'; my $MYUSER = 'mata_bot_beta4'; my $NICKRE = qr/mata_?bo[ity]+/i; +my $RSSLINK = 'https://analognowhere.com/feed/rss.xml'; +my $RSSMEM_PATH = 'rss'; sub randint { my($min, $max) = @_; @@ -294,15 +298,64 @@ sub recvmsg { } } +sub sendnews { + state $lastlink; + my $rssmem = shift; + my ($homepage, $r); + my (@matches, @replies); + + $r = HTTP::Tiny->new->get($RSSLINK); + unless ($r->{success}) { + logger(LOG_WARN, 'GET ${RSSLINK}: $r->{status} $r->{reason}'); + return (); + } + unless (length $r->{content}) { + logger(LOG_WARN, 'GET ${RSSLINK}: empty HTTP response'); + return (); + } + unless (defined($lastlink)) { + seek($rssmem, 0, 0); + chomp($lastlink = <$rssmem>); + } + $homepage = ($r->{content} =~ m,.*?([^<]+),s) ? $1 : 'website'; + while ($r->{content} =~ m,\G.*?.*?([^<]+).*?([^<]+).*?,gs) { + push @matches, {title => $1, link => $2}; + } + if (@matches) { + my $i; + + if (defined($lastlink)) { + for ($i = 0; $i < @matches; $i++) { + last if $matches[$i]->{'link'} eq $lastlink; + } + } else { + $i = @matches; + } + for ($i--; $i >= 0; $i--) { + push @replies, "RSS: $matches[$i]->{'link'} | $matches[$i]->{'title'}"; + if ($i > 2) { + push @replies, "RSS: found " . ($i-1) . " new items in between. Visit ${homepage} for more..."; + $i = 1; + } + } + truncate($rssmem, 0); + seek($rssmem, 0, 0); + say $rssmem ($lastlink = $matches[0]->{'link'}); + $rssmem->flush; + } + return @replies; +} + sub evasdrop { - my ($s, $host, $chan, $lists) = @_; - my ($msgtime, $msgmax, $pingsent, $pingtime, $prefixlen, $priv); + my ($s, $rssmem, $lists, ($chan, $host, $rss)) = @_; + my ($firstrss, $msgmax, $msgtime, $pingsent, $pingtime, $priv, $rsstime); + $firstrss = 1; $pingsent = 0; $priv = "PRIVMSG ${chan} :"; $msgmax = IRCMAX - length(":${MYNICK}!~${MYUSER}\@ ${priv}") - HOSTMAX - CRLF; # conservative max message length heuristic - $msgtime = $pingtime = time; + $rsstime = $msgtime = $pingtime = time; while (1) { if ($pingsent) { if (time - $pingtime > MAX_LAG) { @@ -316,6 +369,13 @@ sub evasdrop { $pingsent = 1; $pingtime = time; } + if ($rss and ($firstrss || time - $rsstime > RSS_CHECK_TIME)) { + foreach (sendnews($rssmem)) { + sendmsg($s, $priv . substr($_,0,$msgmax)); + } + $rsstime = time; + $firstrss = 0 if $firstrss; + } defined($_ = recvmsg($s)) or return; next if not length; $msgtime = time; @@ -343,18 +403,20 @@ sub evasdrop { } sub usage { - say STDERR "usage: ${0} [-d|-v] [-t] [-b path] [-e path] [-h host] [-j join] [-p port] [-q path]"; + say STDERR "usage: ${0} [-d|-v] [-r] [-t] [-b path] [-e path] [-h host] [-j join] [-p port] [-q path]"; exit 1; } sub init { my ($path_ball, $path_helo, $path_quot); + my $rssmem; my %opts; @_ = @ARGV; while (@_) { $_ = shift; if (/^-d$/) { logger(LOG_DEBUG) } + elsif (/^-r$/) { $opts{'rss'} = 1 } elsif (/^-t$/) { $opts{'tls'} = 1 } elsif (/^-v$/) { logger(LOG_WARN) } elsif (@_ < 1) { usage() } @@ -369,6 +431,7 @@ sub init { $opts{'chan'} //= $DEFAULT_CHAN; $opts{'host'} //= $DEFAULT_HOST; $opts{'port'} //= DEFAULT_PORT; + $opts{'rss'} //= DEFAULT_RSS; $opts{'tls'} //= DEFAULT_TLS; open(my $ball_file, '<', $path_ball // $DEFAULT_PATH_BALL) or die "couldn't open ${path_ball}: $!"; @@ -382,14 +445,18 @@ sub init { or die "couldn't open ${path_quot}: $!"; chomp(my @quot = <$quot_file>); close $quot_file or die "${quot_file}: $!"; - return \%opts, [ + if ($opts{'rss'}) { + open($rssmem, '+>>', $RSSMEM_PATH) + || die "couldn't open ${RSSMEM_PATH}: $!"; + } + return $rssmem, \%opts, [ \@ball, \@helo, \@quot, ]; } -my ($opts, $lists) = init(); +my ($rssmem, $opts, $lists) = init(); while (1) { my ($sock, $addr); @@ -410,7 +477,7 @@ while (1) { sendmsg($s, "NICK ${MYNICK}"); sendmsg($s, "WHO ${MYNICK}"); sendmsg($s, "JOIN $opts->{'chan'}"); - evasdrop($s, $opts->{'host'}, $opts->{'chan'}, $lists); + evasdrop($s, $rssmem, $lists, @$opts{'chan', 'host', 'rss'}); sendmsg($s, 'QUIT'); $sock->close(); } else {