package tasks::OEISUninterestingNumbers;
use parent 'AnomieBOT::Task';
=pod
=begin metadata
Bot: AnomieBOT
Task: OEISUninterestingNumbers
BRFA: N/A
Status: Begun 2025-07-13
Created: 2025-07-13
Create a page listing some [[uninteresting number]]s:
* Smallest number that does not appear in any (truncated) sequence in [[OEIS]].
=end metadata
=cut
use utf8;
use strict;
use Date::Parse;
use HTTP::Response;
use IO::Uncompress::Gunzip qw/$GunzipError/;
use LWP::UserAgent;
sub new {
my $class=shift;
my $self=$class->SUPER::new;
$self->{'ua'}=LWP::UserAgent->new(
agent=>"AnomieBOT/1.0 (uninteresting number checker for en.wikipedia.org; see https://en.wikipedia.org/wiki/User:AnomieBOT)",
keep_alive=>300,
);
bless $self, $class;
return $self;
}
=pod
=for info
Per [[WP:BOTUSERSPACE]], any bot or automated editing process that affects only
the operator's or their own userspace, and which are not otherwise disruptive,
may be run without prior approval.
=cut
sub approved {
return 999;
}
sub run {
my ( $self, $api ) = @_;
$api->task( 'OEISUninterestingNumbers', 0, 10, qw(d::IWNS) );
my $debugmode = ( $api->DEBUG & 4 ) == 4;
if ( $debugmode ) {
$api->log( "!!! DEBUG MODE ACTIVE !!!" );
}
my $t = ( $api->store->{'nextrun'} // 0 ) - time();
return $t if $t>0;
my $title = 'User:AnomieBOT/OEIS-uninteresting numbers';
my $tok = $api->edittoken( $title, EditRedir => 1 );
if ( $tok->{'code'} eq 'shutoff' ) {
$api->warn( "Task disabled: " . $tok->{'content'} . "\n" );
return 300;
}
if ( $tok->{'code'} ne 'success' ) {
$api->warn( "Failed to get edit token for $title: " . $tok->{'error'} . "\n" );
return 300;
}
my $url = 'https://oeis.org/stripped.gz';
my $r;
if ( $debugmode ) {
if ( -e "/tmp/oeis-stripped.gz" ) {
my $content;
$r = { is_success => 1 };
open X, "<", "/tmp/oeis-stripped.gz" or die "Failed to open /tmp/oeis-stripped.gz: $!\n";
{ local $/=undef; $content = <X>; }
close X;
$r = new HTTP::Response( 200 );
$r->content( $content );
$api->log( "DEBUG: Loaded downloaded data from /tmp/oeis-stripped.gz" );
} else {
$api->log( "Downloading data" );
$r = $self->{'ua'}->get( $url );
open X, ">", "/tmp/oeis-stripped.gz" or die "Failed to write /tmp/oeis-stripped.gz: $!\n";
{ local $/=undef; print X $r->decoded_content; }
close X;
$api->log( "DEBUG: Saved downloaded data to /tmp/oeis-stripped.gz for reuse on re-runs" );
}
} else {
$api->log( "Downloading data" );
$r = $self->{'ua'}->get( $url );
}
if( $r->code =~ /^4\d\d$/ && $r->code !~ /^(400|408|409)$/ ) {
$api->error( "Failed to fetch $url: " . $r->status_line . "\n" . $r->decoded_content );
$api->error( "Will not try again (until bot restart anyway)" );
return undef;
} elsif ( ! $r->is_success ) {
$api->error( "Failed to fetch $url: " . $r->status_line );
$api->error( "Will try again later" );
$api->store->{'nextrun'} = time + 3600;
return 3600;
}
$api->log( "Reading data" );
my $z = IO::Uncompress::Gunzip->new( $r->decoded_content( 'ref' => 1 ) );
if ( ! $z ) {
$api->error( "Failed to uncompress data: $GunzipError" );
return 300;
}
my $ts = undef;
my %inFile = ();
while ( <$z> ) {
if ( /^# Last Modified: (.*)/ ) {
$ts = str2time( $1, 'UTC' );
$api->error( "Failed to parse last-modified timestamp $1" ) unless defined( $ts );
}
next if /^#/;
for my $n ( split /,/ ) {
$inFile{$1}++ if $n =~ /^\s*(\d+)\s*$/;
}
}
close $z;
$z = undef;
if ( ! defined( $ts ) ) {
$api->error( "Did not find 'Last Modified' header in file" );
$api->store->{'nextrun'} = time + 3600;
return 3600;
}
if ( ! %inFile ) {
$api->error( "Did not find any integers in file" );
$api->store->{'nextrun'} = time + 3600;
return 3600;
}
$api->log( "Finding first unlisted number" );
my $number = 0;
while ( $inFile{$number} // 0 ) {
$number++;
}
$api->log( "First unlisted number is $number as of " . gmtime( $ts ) );
my $txt = qq(
{{ $title/{{{1|display}}}
| number = $number
| lastchange = $ts
| arg = {{{2|}}}
}}
);
$txt =~ s/^ //mg;
$txt =~ s/^\s+//;
$txt =~ s/\s*$/\n/;
my $intxt = $tok->{'revisions'}[0]{'slots'}{'main'}{'*'} // '';
$intxt =~ s/^\s+//;
$intxt =~ s/\s*$/\n/;
$intxt =~ s/\| lastchange = \d+/| lastchange = $ts/;
if ( $txt ne $intxt ) {
my $summary = "Updating OEIS-uninteresting number: $number";
$api->log( "$summary in $title" );
my $r = $api->edit( $tok, $txt, "$summary", 0, 1 );
if ( $r->{'code'} ne 'success' ) {
$api->warn( "Write error for $title: " . $r->{'error'} . "\n" );
return 60;
}
}
# OEIS seems to update just before 05:00 UTC. So aim for that.
$t = 86400 - ( ( time - 5 * 3600 ) % 86400 );
$api->store->{'nextrun'} = time + $t;
return $t;
}
1;