package tasks::OEISUninterestingNumbers;
=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 AnomieBOT::Task;
use Date::Parse;
use HTTP::Response;
use IO::Uncompress::Gunzip qw/$GunzipError/;
use LWP::UserAgent;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;
use Data::Dumper;
sub new {
my $class=shift;
my $self=$class->SUPER::new;
$self->{'ua'}=LWP::UserAgent->new(
agent=>"AnomieBOT uninteresting number checker for en.wikipedia.org (https://en.wikipedia.org/wiki/User:AnomieBOT)",
keep_alive=>300,
);
bless $self, $class;
return $self;
}
=pod
=for info
Per [[WP:BOT#Approval]], any bot or automated editing process that only
affects only the operators' user and talk pages (or subpages thereof),
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;