Jump to content

User:AntiSpamBot/Source

From Wikipedia, the free encyclopedia

A slightly cleaned-up (read: scrubbed passwords) version of the Shadowbot source. Please don't leave me messages about my coding/database design or style, I hear it enough. If you don't like it, then fix the code and give me a *nix-style .patch file. Shadow1 (talk) 19:37, 4 February 2007 (UTC)

#First, let's declare our modules.
use Net::IRC;
use Shadowbot;
use Perlwikipedia;
use DBI;
use threads;
use threads::shared;

my $page_grabber=Perlwikipedia->new;

#Now we'll set up the mechanism we'll use for editing/retrieving pages
my $editor=Shadowbot->new;
my $username='Shadowbot';
my $password='*************';
my $login_status=$editor->login($username,$password); #Log us in, cap'n!
my %info : shared;
my $number_of_jobs : shared=0;
$info{timeon}=localtime();
$info{users}=0;

unless ($login_status eq 'Success') { #Darn, we didn't log in
        die "Failed to log into Wikipedia.\n";
}
if ($login_status eq 'Success') { #We've logged in.
        print "Logged into Wikipedia.\n";
}



#Declare all sorts of IRC-related goodness
my $nickname   = 'shadowbot';
my $username   = 'Shadowbot';
my $ircname    = 'Shadowbot anti-spam bot';
my $ircserver  = 'niven.freenode.net';
my $port       = 8001;
my @channels   = ( '#wikipedia-en-spam', '#wikipedia-en-spam-bot', '#wikipedia-spam-t' );
#Set up various data structures
my $do_i_revert : shared=1;
my $revert_once : shared=1;
my $detect_refs : shared=1;
my $sizelimit : shared =500;

my $irc=new Net::IRC;
my $freenode=$irc->newconn(Nick         =>$nickname,
                           Username     =>$username,
                           Ircname      =>$ircname,
                           Server       =>$ircserver,
                           Port         =>$port
);

$freenode->add_handler('376',\&connect);
$freenode->add_handler('public',\&public_message_handler);
$irc->start;



sub connect {
        print "Connected to $ircserver.\n";
        $freenode->privmsg("NickServ","identify *******");
        sleep 5;
        foreach $channel (@channels) {
                $freenode->join($channel);
        }
}

sub public_message_handler {
        my ($self,$event) = @_;
        my $handler_thread=threads->new(\&irc_public, $self, $event)->detach;
}


sub irc_public {
        my ($self)    = shift;
        my ($event)   = shift;
        my ($message) = ($event->args);
        my ($nick)    = ($event->nick);
        my ($cloak)=($event->userhost);
        my $channel   = ($event->to)[0];
        $cloak=(split /@/, $cloak)[1];
        $message =~ s/\cC\d{1,2}(?:,\d{1,2})?|[\cC\cB\cI\cU\cR\cO]//g; #Kill any color codes.

        if ( $channel eq '#wikipedia-en-spam-bot' ) { #Are we talking in the bot channel?
                if (check_mysql("authorized_bots",$nick)) { #Yep, it's a bot
                        $number_of_jobs++;
                        processalert($message); #Process the message.
                        --$number_of_jobs;
                } #It's a bot
        } #It's a bot channel
        if ( $channel eq '#wikipedia-en-spam' | $channel eq '#wikipedia-spam-t') { #Command channel?
                if ($message=~m/^!shadowbot activate override (.+)/) {
                        my $rule=$1;
                        my $verified=authenticate($cloak);
                        if ($verified) {
                                set_override($rule,1);
                                $freenode->privmsg($channel,"$rule override turned on.");
                        }
                        else {
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }
                }
                if ($message=~m/^!shadowbot deactivate override (.+)/) {
                        my $rule=$1;
                        my $verified=authenticate($cloak);
                        if ($verified) {
                                set_override($rule,0);
                                $freenode->privmsg($channel,"$rule override turned off.");
                        }
                        else {
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }
                }

                if ($message=~m/^!shadowbot status/) {
                        my $message_to_send="I've been active since $info{timeon}. I've reverted $info{users} times. ";
                        if ($revert_once) {
                                $message_to_send.="I'm currently in calm mode. ";
                        }
                        else {
                                $message_to_send.="I'm currently in angry mode. ";
                        }
                        $message_to_send.="I have $number_of_jobs active jobs running.";
                        $freenode->privmsg($channel,$message_to_send);
                }
                if ($message=~m/^!shadowbot linksearch (.+)/) {
                        my $number=do_linksearch($1);
                        if ($number) {
                                $freenode->privmsg($channel,"$1 appears in $number article(s).");
                        }
                        else{$freenode->privmsg($channel,"$1 does not appear in any articles."); }
                }

                if ($message=~m/^!shadowbot mode (.+)/) {
                        my $mode=$1;
                        $verified=authenticate($cloak);
                        if ($verified) {
                                if ($mode=~/angry/) {
                                        $revert_once=0;
                                        $freenode->privmsg($channel,"Safety check disabled.");
                                }
                                elsif($mode=~/calm/) {
                                        $revert_once=1;
                                        $freenode->privmsg($channel,"Safety check enabled.");
                                }
                                else {
                                        $freenode->privmsg($channel,"Mode $mode not recognized.");
                                }
                        }
                        else {
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }
                }

                if ($message=~m/^!shadowbot size (\d+)/) { #It's a size command
                        my $verified=0;
                        my $newsize=$1;
                        $verified=authenticate($cloak); #Authenticate the user
                        if ($verified==1) { #Are they supposed to do this?
                                $sizelimit=$newsize; #Change the size
                                $freenode->privmsg($channel,"Size limit changed to $sizelimit."); #Report back
                        } #They're supposed to do this
                        elsif ($verified==0) { #They're not supposed to do this
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users."); #Tell them they've been naughty
                        } #They're not supposed to do this
                }
                if ( $message =~ m/^!shadowbot references/ ) { #Checking refs?
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified==1) { #Authed?
                                $detect_refs=1;
                                $freenode->privmsg($channel,"Now detecting references.");
                        }#Authed
                        elsif ($verified==0) { #Not authed?
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }#Not authed
                }#References command
                if ( $message =~ m/^!shadowbot noreferences/ ) { #Checking refs?
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified==1) { #Authed?
                                $detect_refs=0;
                                $freenode->privmsg($channel,"References detection disabled.");
                        }#Authed
                        elsif ($verified==0) { #Not authed?
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }#Not authed
                }#NoReferences command
                if ($message=~m/^!shadowbot add authorized (.+)/) { #Adding a bot
                        my $verified=0;
                        my $botname=$1;
                        $verified=authenticate($cloak);
                        if ($verified==1) {
                                $exists=check_mysql("authorized_bots",$botname);
                                unless ($exists) { #Make sure it doesn't exist
                                        $authorized_bots{$botname}=''; #Add the bot
                                        $freenode->privmsg($channel,"New authorized bot \"$botname\" added.");
                                        return;
                                }
                                if ($exists) {
                                        $freenode->privmsg($channel,"Bot already exists in my database.");
                                        return;
                                }
                        }
                        elsif ($verified==0) {
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }
                }
                if ($message=~m/^!shadowbot remove authorized (.+)/) {
                        my $verified=0;
                        my $botname=$1;
                        $verified=authenticate($cloak);
                        if ($verified==1) {
                                $exists=check_mysql("authorized_bots",$botname);
                                unless ($exists) {
                                        $freenode->privmsg($channel,"Bot does not exist in my database.");
                                        return;
                                }
                                if ($exists) {
                                        delete $authorized_bots{$botname};
                                        $freenode->privmsg($channel,"Authorized bot \"$botname\" deleted.");
                                        return;
                                }
                        }
                        elsif ($verified==0) {
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }
                }
                if ($message=~m/^!shadowbot depth (.+)/) { #It's a depth command
                        my $verified=0;
                        my $newdepth=$1;
                        $verified=authenticate($cloak); #Authenticate the user
                        if ($verified==1) { #Are they supposed to do this?
                                $searchdepth=$newdepth; #Change the depth
                                $freenode->privmsg($channel,"Search depth changed to $searchdepth."); #Report back
                        } #They're supposed to do this
                        elsif ($verified==0) { #They're not supposed to do this
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users."); #Tell them they've been naughty
                        } #They're not supposed to do this
                } #It's a depth command
                if ( $message =~ m/^!shadowbot revert/ ) { #Are we going to go into revert mode?
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified==1) { #Verified?
                                $freenode->privmsg($channel,"Reverting enabled.");
                                $do_i_revert=1; #Activate reverting
                        } #Verified?
                        elsif ($verified==0) { #Not verified.
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        } #Not verified
                } #Revert command
                if ( $message =~ m/^!shadowbot norevert/ ) { #Turning off reversion?
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified==1) { #Authed?
                                $do_i_revert=0;
                                $freenode->privmsg($channel,"Reverting disabled.");
                        }#Authed
                        elsif ($verified==0) { #Not authed?
                                $freenode->privmsg($channel,"User $nick not in my list of trusted users.");
                        }#Not authed
                }#Norevert command
                if ( $message =~ m/^!link bl add (.+)/ ) { #adding to linkwatcher blacklist?
                        my $toadd = $1;
                        add_mysql("quarantine",'single',$toadd);#Initialize the key with a blank def.
                        $freenode->privmsg($channel,"Item \"$toadd\" has been added to my quarantine list." ); #Report back
                } #adding to lw blacklist
                if ( $message =~ m/^!link bl del (.+)/ ) { #deleting from lw blacklist?
                        my $todel = $1;
                        delete_mysql("quarantine",$todel);; #delete the key
                        $freenode->privmsg($channel,"Item \"$todel\" has been removed from my quarantine list." );
                }#deleting from lw blacklist
                if ( $message =~ m/^!shadowbot blacklist search (.+)/ ) { #Search the blacklist
                        my $search = $1;
                        my $exists=check_mysql("blacklist",$search);
                        unless($exists) { #Darn, didn't find it
                                $freenode->privmsg($channel,"$search was not found in my blacklist." );
                        } #Didn't find string
                        if ($exists) { #Hoorah, we found it
                                $freenode->privmsg($channel,"$search was found in my blacklist." );
                        } #Found string
                } #Searching blacklist
                if ( $message =~ m/^!shadowbot quarantine add (.+)/ ) { #Add to quarantine
                        my $toadd = $1;
                        my $exists=check_mysql("quarantine",$toadd);
                        if ($exists) { #Rule is already there
                                $freenode->privmsg($channel,"Item $toadd is already in my quarantine.");
                        } #Already there
                        unless ($exists) {
                                add_mysql("quarantine",'single',$toadd);
                                $freenode->privmsg($channel,"Item $toadd has been added to my quarantine.");
                        }
                }
                }
                if ( $message =~ m/^!shadowbot approve (.+)/ ) {
                        my $toapprove = $1;
                        my $verified  = 0;
                        $verified = authenticate($cloak);
                        if ( $verified == 1 ) {
                                my $exists=check_mysql("quarantine",$toapprove);
                                unless ($exists) {
                                        $freenode->privmsg($channel,"Item $toapprove does not exist in my quarantine list."
                                        );
                                }
                                if ($exists) {
                                        add_mysql("blacklist",'single',$toapprove);
                                        delete_mysql("quarantine",$toapprove);
                                        $freenode->privmsg($channel,"Item $toapprove has been approved and added to my blacklist."
                                        );
                                        logit("$nick added blacklist rule $toapprove");
                                        my $number=do_linksearch($toapprove);
                                        $freenode->privmsg($channel,"$toapprove appears in $number articles.");
                                }
                        }
                        elsif ( $verified == 0 ) {
                                $freenode->privmsg($channel,"User $nick is not in my list of trusted users." );
                        }
                }
                if ( $message =~ m/^!shadowbot deny (.+)/ ) {
                        my $todeny = $1;
                        if ( $message =~ m/!shadowbot deny (.+)/ ) {
                                my $verified = 0;
                                $verified = authenticate($cloak);
                                if ( $verified == 1 ) {
                                        my $exists=check_mysql("quarantine",$todeny);
                                        unless ($exists) {
                                                $freenode->privmsg($channel,"Item $todeny does not exist in my quarantine list.");
                                        }
                                        if ($exists) {
                                                delete_mysql("quarantine",$todeny);
                                                $freenode->privmsg($channel,"Item $todeny has been denied and has been removed from my quarantine list.");
                                        }
                                }
                                elsif ( $verified == 0 ) {
                                        $freenode->privmsg($channel,"User $nick is not in my list of trusted users." );
                                }
                        }
                }
                if ( $message =~ m/^!shadowbot blacklist add (.+)/ ) {
                        my $toadd = $1;
                        if ( $cloak eq 'wikimedia/shadow42' ) {
                                my $exists=check_mysql("blacklist",$toadd);
                                if ($exists) {
                                        $freenode->privmsg($channel,"Item $toadd already exists in my blacklist.");
                                }
                                unless ($exists) {
                                        add_mysql("blacklist",'single',$toadd);
                                        $freenode->privmsg($channel,"Item $toadd has been added to my blacklist.");
                                }
                        }
                        elsif ( $cloak ne 'wikimedia/shadow42' ) {
                                $freenode->privmsg($channel,"Operation \"add\" is restricted to Shadow42." );
                        }
                }
                if ( $message =~ m/^!shadowbot blacklist remove (.+)/ ) {
                        my $toremove = $1;
                        if ( $cloak eq 'wikimedia/shadow42' ) {
                                my $exists=check_mysql("blacklist",$toremove);
                                unless ($exists) {
                                        $freenode->privmsg($channel,"Item $toremove does not exist in my blacklist."
                                        );
                                }
                                if ($exists) {
                                        delete_mysql("blacklist",$toremove);
                                        $freenode->privmsg($channel,"Item $toremove has been removed from my blacklist.");
                                }
                        }
                        elsif ( $cloak ne 'wikimedia/shadow42' ) {
                                $freenode->privmsg($channel,"Operation \"remove\" is restricted to Shadow42." );
                        }
                }
                if ( $message =~ m/^!shadowbot add trusted (.+)/ ) {
                        my $totrust = $1;
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified == 1) {
                                my $exists=check_mysql("trusted_users",$totrust);
                                if ($exists) {
                                        $freenode->privmsg( $channel => "User cloak $totrust is already in my list of trusted users.");
                                }
                                unless ($exists) {
                                add_mysql("trusted_users","single",$totrust);
                                $freenode->privmsg( $channel => "User cloak $totrust has been added to my list of trusted users.");

                        }
                        elsif ($verified == 0) {
                                $freenode->privmsg( $channel => "User $nick not in my list of trusted users.");
                        }
                }
                if ( $message =~ m/^!shadowbot remove trusted (.+)/ ) {
                        my $todelete = $1;
                        my $verified = 0;
                        $verified=authenticate($cloak);
                        if ($verified == 1) {
                                delete_mysql("trusted_users",$todelete);
                                $freenode->privmsg( $channel => "User cloak $todelete has been removed from my list of trusted users.");
                        }
                        elsif ($verified == 0) {
                                $freenode->privmsg( $channel => "User $nick not in my list of trusted users.");
                        }
                }
        }
}

sub parsealert {
        my $alert    = shift;
        my $pagename;
        my $user;
        my $diffurl;
        my $size;
        my @rules=();
                if ( $alert =~ m/diff=<(.+?)> user=<(.+?)> title=<(.+?)>/ ) {
                        $diffurl =$1;
                        $user    =$2;
                        $pagename=$3;

                }
                if ($alert=~m/size=<(.+?)>/) {
                         $size=$1;
                }
                while ($alert=~m/rule=<(.+?)>/) {
                        my $rule=$1;
                        push(@rules,$rule);
                        $alert=~s/rule=<.+>//;
                }
                if ($size=~m/-(\d+)/) {
                        $size=-$1;
                }
                elsif ($size=~m/(\d+)/) {
                        $size=$1;
                }
                print "Received an alert for $user on $pagename.\n";
                return $pagename, $user, $diffurl, $size, @rules;
}

sub authenticate {
        my $cloak = shift;
        my $exists=check_mysql("trusted_users",$cloak);
        if ($exists) {
                return 1;
        }
        else { return 0; }
}

sub processalert {

        my $message=shift;
        my ( $pagename, $user, $diff, $size, @rules ) = parsealert($message);
        my @page_revids=$editor->get_history($pagename,'revids');
        my $revisionid=$page_revids[1];
        my $pagecontents=$editor->grab_text($pagename);
        my $override=0;
        foreach $rule (@rules) {
                $override=check_mysql("overrides",$rule);
        }

        if ($size>$sizelimit) {
                unless ($override) {
                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to revert to $revisionid on $pagename: Edit larger than $sizelimit ($diff).");

                        return;
                }
        }
        foreach $rule (@rules) {
                my $exists=check_mysql("blacklist",$rule);
                unless ($exists) {
                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Rule \"$rule\" was not found in my blacklist, so I can't revert on diff \"$diff\".");

                return;
                }
        }
        foreach $rule (@rules) {
                unless ($pagecontents=~m/$rule/) {
                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Rule \"$rule\" was not found in the on-top version of $pagename ($diff).");
                        return;
                }
        }
        foreach $rule (@rules) {
                if ($pagecontents=~m/\{\{.+?$rule.+?\}\}/ | $pagecontents=~m/\<ref.+?\>.+?$rule.+?\<\/ref\>/) {
                        if ($detect_refs==1) {
                                $freenode->privmsg("#wikipedia-en-spam","ERROR: Detected \"$rule\" inside of a reference or template, please manually check ($diff).");
                                $freenode->privmsg("#wikipedia-spam-t","ERROR: Detected \"$rule\" inside of a reference or template, please manually check ($diff).");
                                return;
                        }
                }
        }
                if ($do_i_revert==1) {
                        my $rule=$rules[0];
                        my $edit_summary="BOT--Reverting edits by $user to revision $revisionid ($rule)";
                        unless ($revisionid==0) {
                                my $safety_status=check_if_safe($pagename,$user,$override);
                                if ($safety_status eq 'Pass') {
                                        $editor->revert($pagename,$edit_summary,$revisionid);
                                        $freenode->privmsg("#wikipedia-en-spam","Reverted to $revisionid on $pagename ($diff)($rule).");
                                        logit("Reverted to $revisionid on $pagename.");
                                        warn_user($user,$diff,$pagename,$rule);
                                        $info{users}++;
                                        update_rules_stats(@rules);
                                        return;
                                }
                                elsif ($safety_status eq 'Fail') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to revert to $revisionid on $pagename: Someone has edited since the spammer ($diff).");
                                        logit("ERROR: Failed to revert to $revisionid on $pagename: Someone has edited since the spammer.");
                                        return;
                                }
                                elsif ($safety_status eq 'Alreadydid') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to revert to $revisionid on $pagename: I've already reverted ($diff).");
                                        $freenode->privmsg("#wikipedia-spam-t","ERROR: Failed to revert to $revisionid on $pagename: I've already reverted ($diff).");
                                        logit("ERROR: Failed to revert to $revisionid on $pagename: I've already reverted.");
                                }
                                elsif ($safety_status eq 'Reversion') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to revert to $revisionid on $pagename: Detected reversion ($diff).");
                                        logit("ERROR: Failed to revert to $revisionid on $pagename: Detected reversion.");
                                }
                        }
                        if ($revisionid==0) {
                                $freenode->privmsg("#wikipedia-en-spam","ERROR: An unknown error occurred in my revid retrieval function while reverting $user ($diff).");
                                logit("ERROR: An unknown error occurred in my revid retrieval function.");
                                return;
                        }
                }
                elsif ($do_i_revert==0) {
                        unless ($revisionid==0) {
                                my $safety_status=check_if_safe($pagename,$user);
                                if ($safety_status eq 'Pass') {
                                        $freenode->privmsg("#wikipedia-en-spam","Fake-reverted to $revisionid on $pagename ($diff).");
                                        return;
                                }
                                elsif ($safety_status eq 'Fail') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to fake-revert to $revisionid on $pagename: Someone has edited since the spammer ($diff).");
                                        return;
                                }
                                elsif ($safety_status eq 'Alreadydid') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to fake-revert to $revisionid on $pagename: I've already reverted ($diff).");
                                }
                                elsif ($safety_status eq 'Reversion') {
                                        $freenode->privmsg("#wikipedia-en-spam","ERROR: Failed to fake-revert to $revisionid on $pagename: Detected reversion ($diff).");
                                }
                        }
                }

        }

sub check_if_safe {
        my $pagename = shift;
        my $user     = shift;
        my $override = shift;
        my @users=$editor->get_history($pagename,'users');
        my @comments=$editor->get_history($pagename,'comments');
        print "Comparing $users[0] to $user.\n";
        unless ($users[0] eq $user) {
                print "Not equal.\n";
                return 'Fail';
        }
        foreach $user (@users[1..2]) {
                if ($user eq $username) {
                        unless($revert_once==0 || $override==1) {return 'Alreadydid';}
                }
        }
        foreach $comment (@comments[1..2]) {
                if ($comment=~m/\b(rvv|rv|revert|reverting|VP2|rvt|reverted|reversion|robot|bot|rollback|roll back|rolling back|rolled back)\b/) {
                        return 'Reversion';
                }
        }
        return 'Pass';
}

sub warn_user {
        my $user=shift;
        my $diffurl=shift;
        my $pagename=shift;
        my $rule=shift;
        my $time=time();
        my $summary='';
        my $exists=check_mysql("users",$user);
        unless ($exists) {
                add_mysql("users","multiple",$user,$time,"1");
        }
        my $lasttime=query_mysql("users",$user,"2");
        my $warninglevel=query_mysql("users",$user,"3");
        if (($time-$lasttime)>14400) {
                $warninglevel=1;
        }
        if ($warninglevel==1) {
                $talk_page="Thank you for contributing to Wikipedia, $user! However, your edit [$diffurl here] was reverted by an automated bot that attempts to remove [[WP:SPAM|spam]] from Wikipedia. If you were trying to insert a good link, please accept my creator's apologies, but note that the link you added, matching rule $rule, is on my list of links to remove and probably shouldn't be included in Wikipedia. Please read [[WP:EL|Wikipedia's external links policy]] for more information. If the link was to an image, please read [[WP:IMAGE|Wikipedia's image tutorial]] on how to use a more appropriate method to insert the image into an article. If your link was intended to promote a site you own, are affiliated with, or will make money from inclusion in Wikipedia, please note that inserting spam into Wikipedia is against policy. For more information about me, see [[User:Shadowbot/FAQ|my FAQ page]]. Thanks! ~~~~";
        $summary="Regarding edits made to [[$pagename]]";
        }
        elsif ($warninglevel==2) {
                $talk_page="Please stop adding inappropriate links to Wikipedia. It is considered [[WP:SPAM|spamming]] and will be removed. Thanks. ~~~~";
        }
        elsif ($warninglevel==3) {
                $talk_page="Please stop [[WP:SPAM|spamming]] Wikipedia. If you continue, you will be [[WP:BLOCK|blocked]] from editing. ~~~~";
        }
        elsif ($warninglevel==4) {
                $talk_page="[[Image:Stop hand.svg|30px|left]] This is your '''last warning'''. The next time you insert a [[WP:SPAM|spam]] link, you will be '''blocked''' from editing Wikipedia. ~~~~";
        }
        elsif ($warninglevel>4) {
                $freenode->privmsg("#wikipedia-spam-t","!admin ALERT: [[User:$user]] has a warning level of $warninglevel.");
                $freenode->privmsg("#wikipedia-spam","!admin ALERT: [[User:$user]] has a warning level of $warninglevel.");
        }
        if ($warninglevel==5) {
                my $message;
                if ($user=~m/\d+\.\d+\.\d+\.\d+/) {
                        $message="{{IPvandal|$user}} Has spammed after final warning. ~~~~";
                } else {
                        $message="{{vandal|$user}} Has spammed after final warning. ~~~~";
                }

                my $text=$editor->grab_text("Wikipedia:Administrator intervention against vandalism/TB2");
                $text .="\n$message";
                $editor->edit("Wikipedia:Administrator intervention against vandalism/TB2","Reporting persistent spammer",$text);
                $freenode->privmsg("#wikipedia-spam","Reported $user to AIV.");
                $freenode->privmsg("#wikipedia-spam-t","Reported $user to AIV.");
        }

        unless ($warninglevel>4) {$editor->edit_talk($user,$summary,$talk_page);}

        $warninglevel++;
        update_mysql("users",$user,$time,$warninglevel);
}

sub do_linksearch {
        $number_of_jobs++;
        my $link_pre=shift;
        $link_pre=~s/\\//g;
        my $linksearch_page=$page_grabber->{mech}->get("http://wiki.riteme.site/w/index.php?title=Special:Linksearch&target=$link_pre&limit=5000&offset=0")->content;
        my @links=$linksearch_page=~m{<li>.+\slinked from.+?title="(.+)".+</li>}g;
        while (my $res = $page_grabber->{mech}->follow_link(text => 'next 5,000')) {
                my $contents=$res->content;
                my @new_links=$linksearch_page=~m{<li>.+\slinked from.+title="(.+)".+</li>}g;
                foreach(@new_links) {push(@links,$_);}
                sleep 3;
        }
        my @links_final;
        foreach(@links) {unless (/^.+:.+$/) {push (@links_final,$_);} }
        $number_of_jobs--;
        return (scalar @links_final);
}

sub query_mysql {
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","********");
        my $table=shift;
        my $data=shift;
        my $field_number=shift;
        my @results;
        my $query;
        $data=$mysql_handle->quote($data);
        if ($table eq 'users') {
                $query="SELECT * FROM $table WHERE user_name=$data";
        }
        my $query_handle=$mysql_handle->prepare($query);
        print "Executing query $query\n";
        $query_handle->execute;
        $query_handle->bind_columns(\$user_name,\$user_time,\$user_level);
        while ($query_handle->fetch) {
                push(@results,{1=>$user_name,2=>$user_time,3=>$user_level});
        }
        $query_handle->finish;
        my $result=$results[0]->{$field_number};
        return $result;
}

sub add_mysql {
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","********");
        my $table=shift;
        my $type=shift;
        if ($type eq 'single') {
                my $value=shift;
                $value=$mysql_handle->quote($value);
                my $query="INSERT INTO $table VALUES ($value)";
                my $query_handle=$mysql_handle->prepare($query);
                print "Executing query $query\n";
                $query_handle->execute;
                $query_handle->finish;
        }
        elsif ($type eq 'multiple') {
                my $user_name=shift;
                my $user_time=shift;
                my $user_level=shift;
                $user_name=$mysql_handle->quote($user_name);
                my $query="INSERT INTO $table VALUES ($user_name,$user_time,$user_level)";
                my $query_handle=$mysql_handle->prepare($query);
                print "Executing query $query\n";
                $query_handle->execute;
                $query_handle->finish;
        }
}

sub update_mysql {
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","******");
        my $table=shift;
        my ($user_name,$user_time,$user_level)=@_;
        $user_name=$mysql_handle->quote($user_name);
        my $query="UPDATE $table SET user_time=$user_time,user_level='$user_level' WHERE user_name=$user_name";
        my $query_handle=$mysql_handle->prepare($query);
        print "Executing query $query\n";
        $query_handle->execute;
        $query_handle->finish;
}

sub check_mysql {
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","*******");
        my $table=shift;
        my $value=shift;
        my $query="SELECT * FROM $table WHERE ";
        if ($table eq 'authorized_bots') {
                $query.="bot_name=";
        }
        elsif ($table eq 'trusted_users') {
                $query.="user_cloak=";
        }
        elsif ($table eq 'blacklist' || $table eq 'quarantine' || $table eq 'overrides' || $table eq 'rules_stats') {
                $query.="rule=";
        }
        elsif ($table eq 'users') {
                $query.="user_name=";
        }
        $value=$mysql_handle->quote($value);
        $query.="$value";
        my $query_handle=$mysql_handle->prepare($query);
        print "Executing query $query.\n";
        $query_handle->execute;
        my $result=0;
        if ($query_handle->rows > 0) {
                $result=1;
        }
        return $result;
}

sub delete_mysql {
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","*******");
        my $table=shift;
        my $value=shift;
        $value=$mysql_handle->quote($value);
        my $query="DELETE FROM $table WHERE ";
        if ($table eq 'authorized_bots') {
                $query.="bot_name=";
        }
        elsif ($table eq 'trusted_users') {
                $query.="user_cloak=";
        }
        elsif ($table eq 'blacklist' || $table eq 'quarantine') {
                $query.="rule=";
        }
        elsif ($table eq 'users') {
                $query.="user_name=";
        }
        $query.="$value";
        my $query_handle=$mysql_handle->prepare($query);
        print "Executing query $query\n";
        $query_handle->execute;
        $query_handle->finish;
}

sub set_override {
        my $rule=shift;
        my $option=shift;
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","*******");
        $rule=$mysql_handle->quote($rule);
        my $query;
        if ($option==1) { $query="INSERT INTO overrides VALUES($rule)"; }
        else {$query="DELETE FROM overrides WHERE rule=$rule";}
        my $query_handle=$mysql_handle->prepare($query);
        print "Executing query $query\n";
        $query_handle->execute;
        $query_handle->finish;
}

sub update_rules_stats {
        my @rules=shift;
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","******");
        foreach $rule (@rules) {
                my $query;
                my $exists=check_mysql("rules_stats",$rule);
                $rule=$mysql_handle->quote($rule);
                if (!$exists) {
                        $query="INSERT INTO rules_stats VALUES($rule,1)";
                }
                elsif ($exists) {
                        $query="UPDATE rules_stats SET count=count + 1 WHERE rule=$rule";
                }
                my $query_handle=$mysql_handle->prepare($query);
                print "Executing query $query\n";
                $query_handle->execute;
                $query_handle->finish;
        }
}

sub logit {
        my $message = shift;
        my $mysql_handle=DBI->connect("dbi:mysql:shadowbot;localhost","shadowbot","*******");
        $message=$mysql_handle->quote($message);
        my $query="INSERT INTO log VALUES(DEFAULT,$message)";
        my $query_handle=$mysql_handle->prepare($query);
        $query_handle->execute;
        $query_handle->finish;
}