// IRC Bot 
// Karl L. Barrus <karl at klbarrus dot com>
// January 9, 2005
//
// Copyright (c) 2005 Karl L. Barrus
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
// 
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 675 Mass Ave, Cambridge, MA 02139, USA.

// simple IRC bot based on the PircBot Java framework
// see http://www.jibble.org/ for info about the PircBot Java API
//
// public commands supported:
// @help
// @8ball question
// @spin
// @addquote user quote
// @quotelist
// @quote [user]
// @seen user
//
// private commands supported:
// save
// print [user]
// delquotes password user [index]

import org.jibble.pircbot.*;
import java.util.*;
import java.io.*;

public class MyBot extends PircBot
{
    private String channel_;
    private String chan_password_;
    private String priv_password_;

    private ArrayList quotedUsers_;
    private ArrayList seenUsers_;
    private HashMap lastSeenDate_;
    private HashMap lastSeenQuote_;

    private boolean connected_;
    private boolean joined_;
    private boolean quotesChanged_;
    private boolean seenChanged_;

    private int BOT_HELP      = 0;
    private int BOT_8BALL     = 1;
    private int BOT_SPIN      = 2;
    private int BOT_ADDQUOTE  = 3;
    private int BOT_QUOTELIST = 4;
    private int BOT_QUOTE     = 5;
    private int BOT_SEEN      = 6;

    private int BOT_PRIV_SAVE      = 0;
    private int BOT_PRIV_PRINT     = 1;
    private int BOT_PRIV_DELQUOTES = 2;
    private int BOT_PRIV_SEENLIST  = 3;

    String botAttention_ = "@";
    
    private int SEEN_LIST_USERS = 10;

    String botPublicCommands_[] =
    {
        botAttention_ + "help",
        botAttention_ + "8ball",
        botAttention_ + "spin",
        botAttention_ + "addquote",
        botAttention_ + "quotelist",
        botAttention_ + "quote",
        botAttention_ + "seen"
    };

    String botPrivateCommands_[] =
    {
        "save",
        "print",
        "delquotes",
		"seenlist"
    };

    String ircServers[] =
    {
        "irc.choopa.net",
        "irc.easynews.com",
        "irc.servercentral.net",
        "irc.prison.net",
        "irc.efnet.pl",
//        "irc.rt.ru"
    };

    String eightBallReplys[] =
    {
        "Signs point to yes.",
        "Yes.",
        "Reply hazy, try again.",
        "Without a doubt.",
        "My sources say no.",
        "As I see it, yes.",
        "You may rely on it.",
        "Concentrate and ask again.",
        "Outlook not so good.",
        "It is decidedly so.",
        "Better not tell you now.",
        "Very doubtful.",
        "Yes - definitely.",
        "It is certain.",
        "Cannot predict now.",
        "Most likely.",
        "Ask again later.",
        "My reply is no.",
        "Outlook good.",
        "Don't count on it."
    };

    public MyBot( String[] args )
    {
        // args[ 0 ] = name
        // args[ 1 ] = channel password (to get ops)
        // args[ 2 ] = private password (to delete quotes)
        // args[ 3 ] = channel to join
        
        setName( args[ 0 ] );
        setLogin( args[ 0 ] );
        chan_password_ = args[ 1 ];
        priv_password_ = args[ 2 ];
        channel_ = args[ 3 ];
        
        quotedUsers_ = new ArrayList();
        seenUsers_ = new ArrayList();
        lastSeenDate_ = new HashMap();
        lastSeenQuote_ = new HashMap();
        
        connected_ = false;
        joined_ = false;
        quotesChanged_ = false;
        seenChanged_ = false;

        readQuoteList();
        readSeenList();

        saveThread st = new saveThread( this );
        st.start();
    }

    public void onMessage( String channel, String sender, String login, String hostname, String message )
    {
        lastSeenDate_.put( sender.toLowerCase(), new Date() );
        lastSeenQuote_.put( sender.toLowerCase(), message );
        updateSeenList( sender );
        seenChanged_ = true;

        if ( message.startsWith( botAttention_ ) )
        {
            String args[] = parseLine( message );

            if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_HELP ] ) )
                doHelp( channel, sender, args );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_8BALL ] ) )
                do8Ball( channel, sender, args );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_SPIN ] ) )
                doSpin( channel, sender, args  );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_ADDQUOTE ] ) )
                doAddQuote( channel, sender, args );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_QUOTELIST ] ) )
                doQuoteList( channel, sender, args );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_QUOTE ] ) )
                doQuote( channel, sender, args );
            else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_SEEN ] ) )
                doSeen( channel, sender, args );
        }
    }

    public void onPrivateMessage( String sender, String login, String hostname, String message )
    {
        String args[] = parseLine( message );

        if ( args[ 0 ].startsWith( botPrivateCommands_[ BOT_PRIV_SAVE ] ) )
        {
            saveQuoteList();
            saveSeenList();
        }
        else if ( args[ 0 ].startsWith( botPrivateCommands_[ BOT_PRIV_PRINT ] ) )
            printQuoteList( sender, args );
        else if ( args[ 0 ].startsWith( botPrivateCommands_[ BOT_PRIV_DELQUOTES ] ) )
            doDelQuote( sender, args );
        else if ( args[ 0 ].startsWith( botPrivateCommands_[ BOT_PRIV_SEENLIST ] ) )
            printSeenList( sender );
        else if ( args[ 0 ].startsWith( botPublicCommands_[ BOT_QUOTE ] ) )
            doQuote( null, sender, args );
    }

    public void onJoin( String channel, String sender, String login, String hostname )
    {
    	joined_ = true;    	
    }

    public void onMode( String channel, String sourceNick, String sourceLogin, String sourceHostName, String mode )
    {
    }
    
    public void onKick( String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason )
    {
        System.out.println( "*** kicked out" );
    	joined_ = false;
        saveQuoteList();
        saveSeenList();

        // try to rejoin 10 times

        for ( int i = 0; i < 10; ++i )
        {
            try
            {
                Thread.sleep( 10 * 1000 );
            }
            catch ( InterruptedException ie )
            {
                System.out.println( "*** run interrupted exception" );
            }
            
            joinChannel( channel_ );

            if ( joined_ )
            	break;
        }
        
        // join didn't work - now what?
    }

    public void onConnect()
    {
        System.out.println( "*** connected" );
        connected_ = true;
        joinChannel( channel_ );
    }

    public void onDisconnect()
    {
        System.out.println( "*** disconnected" );
        connected_ = false;
        saveQuoteList();
        saveSeenList();

        // try to reconnect 10 times

        for ( int i = 0; i < 10; ++i )
        {
            try
            {
                Thread.sleep( 60 * 1000 );
            }
            catch ( InterruptedException ie )
            {
                System.out.println( "*** run interrupted exception" );
            }

            try
            {
                reconnect();
            }
            catch ( Exception e )
            {
                System.out.println( "*** reconnect exception" );
            }

            if ( connected_ )
                break;
        }
        
        // reconnect didn't work - now what?
    }

    // @help
    private void doHelp( String channel, String sender, String[] args )
    {
        StringBuffer reply = new StringBuffer();

        reply.append( "I respond to: " );

        for ( int i = 0; i < botPublicCommands_.length; ++i )
        {
            reply.append( botPublicCommands_[ i ] );
            reply.append( " " );
        }

        sendMessage( channel, reply.toString() );
    }

    // @8ball question
    private void do8Ball( String channel, String sender, String[] args )
    {
        String command = botPublicCommands_[ BOT_8BALL ];
        StringBuffer reply = new StringBuffer();

        if ( args[ 1 ] != null )
        {
            // the parsing splits the message into 5 arguments
            // for the 8ball question, we want to recombine them back into the original question
            StringBuffer question = new StringBuffer();

            for ( int i = 1; i < 5 && args[ i ] != null; ++i )
            {
                question.append( args[ i ] );
                
                if ( i != 4 )
                    question.append( " " );
            }
            
            int replyIndex = ( int ) ( Math.random() * eightBallReplys.length );

            reply.append( sender );
            reply.append( " asks " );
            reply.append( question );
            reply.append( " ... " );
            reply.append( eightBallReplys[ replyIndex ] );
        }
        else
        {
            reply.append( "usage: " );
            reply.append( command );
            reply.append( " question" );
        }

        sendMessage( channel, reply.toString() );
    }

    // @spin
    private void doSpin( String channel, String sender, String[] args )
    {
        StringBuffer reply = new StringBuffer();

        int flirtIndex = ( int ) ( Math.random() * seenUsers_.size() );
        String flirt = ( String ) seenUsers_.get( flirtIndex );

        reply.append( sender );
        reply.append( " spins the bottle... and it points to " );
        reply.append( flirt );

        sendMessage( channel, reply.toString() );
    }

    // @addquote user quote
    private void doAddQuote( String channel, String sender, String[] args )
    {
        String command = botPublicCommands_[ BOT_ADDQUOTE ];
        StringBuffer reply = new StringBuffer();

        String userid = args[ 1 ];

        if ( userid != null && args[ 2 ] != null )
        {
            StringBuffer quote = new StringBuffer();

            for ( int i = 2; i < 5 && args[ i ] != null; ++i )
            {
                quote.append( args[ i ] );
                
                if ( i != 4 )
                    quote.append( " " );
            }

            ircUser user = lookupUser( userid );

            if ( user != null )
            {
                // existing quotedUser, just add the quote

                user.addQuote( quote.toString() );
            }
            else
            {
                // new quotedUser, create user and add the quote

                user = new ircUser( userid );
                user.addQuote( quote.toString() );
                quotedUsers_.add( user );
            }
            
            sendNotice( sender, "quote added" );

            quotesChanged_ = true;
        }
        else
        {
            reply.append( "usage: " );
            reply.append( command );
            reply.append( " user quote" );
            sendMessage( channel, reply.toString() );
        }
    }

    // @quotelist
    private void doQuoteList( String channel, String sender, String[] args )
    {
        StringBuffer reply = new StringBuffer();

        if ( quotedUsers_.size() != 0 )
        {
            reply.append( "users with quotes:" );
            ircUser user = null;

            for ( int i = 0; i < quotedUsers_.size(); ++i )
            {
                user = ( ircUser ) quotedUsers_.get( i );
                reply.append( " " );
                reply.append( user.getNick() );
            }
        }
        else
        {
            reply.append( "No quotes stored.  Why not add one?" );
        }
        
        sendMessage( channel, reply.toString() );
    }

    // @quote [user]
    private void doQuote( String channel, String sender, String[] args )
    {
        String command = botPublicCommands_[ BOT_QUOTE ];
        StringBuffer reply = new StringBuffer();

        if ( quotedUsers_.size() > 0 )
        {
            ircUser user = null;
            String userid = args[ 1 ];

            if ( userid != null )
            {
                // user specified, extract userid and lookup

                user = lookupUser( userid );
            }
            else
            {
                // no user specified, pick random user

                int userIndex = ( int ) ( Math.random() * quotedUsers_.size() );
                user = ( ircUser ) quotedUsers_.get( userIndex );
            }

            if ( user != null )
            {
                // be sure the first character printed isn't an @
                // otherwise the bot may interpret it as a command
                // i.e. hostile user could do "@addquote @quote blah" and then "@quote @quote" will start a loop

                reply.append( "<" );
                reply.append( user.getNick() );
                reply.append( ">: " );
                reply.append( user.getAQuote() );
            }
            else
            {
                reply.append( "Sorry, I don't know any quotes from " );
                reply.append( userid );
                reply.append( ".  Why not add one?" );
            }
        }
        else
        {
            reply.append( "No quotes stored.  Why not add one?" );
        }
        
        if ( channel != null )
            sendMessage( channel, reply.toString() );
        else
            sendNotice( sender, reply.toString() );
    }

    // @seen user
    private void doSeen( String channel, String sender, String[] args )
    {
        String command = botPublicCommands_[ BOT_SEEN ];
        StringBuffer reply = new StringBuffer();
        String userid = args[ 1 ];

        if ( userid != null )
        {
            Date seenDate = ( Date ) lastSeenDate_.get( userid.toLowerCase() );
            String seenQuote = ( String ) lastSeenQuote_.get( userid.toLowerCase() );

            if ( seenQuote != null )
            {
                Date now = new Date();
                long timeDiff = now.getTime() - seenDate.getTime();
                String ago = getAgoString( timeDiff );

                reply.append( userid.toString() );
                reply.append( " last said '" );
                reply.append( seenQuote );
                reply.append( "'" );
                reply.append( ago );
            }
            else
            {
                reply.append( "I haven't heard " );
                reply.append( userid );
                reply.append( " say anything." );
            }
        }
        else
        {
            reply.append( "usage: " );
            reply.append( command );
            reply.append( " user" );
        }

        sendMessage( channel, reply.toString() );
    }
    
    // delquotes password user [index]
    private void doDelQuote( String sender, String[] args )
    {
        String command = botPrivateCommands_[ BOT_PRIV_DELQUOTES ];
        StringBuffer reply = new StringBuffer();
        String password = args[ 1 ];

        if ( password == null || !priv_password_.equals( password ) )
        {
            System.out.println( "*** wrong password" );
            return;
        }

        String userid = args[ 2 ];

        if ( userid != null )
        {
            ircUser user = lookupUser( userid );

            if ( user != null )
            {
                String indexString = args[ 3 ];

                if ( indexString != null )
                {
                    // index supplied - delete that particular quote from userid

                    Integer intClass = new Integer( indexString );
                    int index = intClass.intValue();
                    user.removeQuote( index );

                    reply.append( "quote deleted" );

                    if ( user.getNumQuotes() == 0 )
                    {
                        // no more quotes, so delete user

                        quotedUsers_.remove( user );
                    }
                }
                else
                {
                    // no index supplied - delete all quotes from userid

                    quotedUsers_.remove( user );

                    reply.append( "all quotes from user " );
                    reply.append( userid );
                    reply.append( " deleted" );
                }

                quotesChanged_ = true;
            }
            else
            {
                reply.append( "can't find user " );
                reply.append( userid );
            }
        }
        else
        {
            reply.append( "usage: " );
            reply.append( command );
            reply.append( " password user [index]" );
        }

        sendMessage( sender, reply.toString() );
    }
    
    private void updateSeenList( String userName )
    {
    	// keep an updated list of the SEEN_LIST_USERS most recently active users   	
		// delete all occurences of the user, then add
    	
    	boolean present;
    	
    	do
    	{
    		present = seenUsers_.remove( userName );    		
    	} 
    	while ( present );
    	
    	seenUsers_.add( userName );
    	
    	// trim list to SEEN_LIST_USERS users by removing the oldest (lowest index) entry
    	
    	if ( seenUsers_.size() > SEEN_LIST_USERS )
    	{
    		seenUsers_.remove( 0 );    		
    	}
    }
    
    private void printSeenList( String sender )
    {
        StringBuffer reply = new StringBuffer();
        
        if ( seenUsers_.size() == 0 )
        {
        	reply.append( "I haven't seen anybody yet." );
        }
        else
        {
            for ( int i = 0; i < seenUsers_.size(); ++i )
            {
            	reply.append( ( String ) seenUsers_.get( i ) );
            	reply.append( " " );
            }
            
        }
        
        sendMessage( sender, reply.toString() );
    }

    private ircUser lookupUser( String userid )
    {
        ircUser user = null;

        for ( int i = 0; i < quotedUsers_.size(); ++i )
        {
            ircUser tempUser = ( ircUser ) quotedUsers_.get( i );
            if ( userid.equalsIgnoreCase( tempUser.getNick() ) )
            {
                user = tempUser;
                break;
            }
        }

        return user;
    }

    private int getUserIndex( String userid )
    {
        int index = -1;

        for ( int i = 0; i < quotedUsers_.size(); ++i )
        {
            ircUser user = ( ircUser ) quotedUsers_.get( i );
            if ( userid.equalsIgnoreCase( user.getNick() ) )
            {
                index = i;
                break;
            }
        }

        return index;
    }

    private String getAgoString( long timeDiff )
    {
        int days, hours, minutes, seconds;

        seconds = ( int ) timeDiff / 1000;

        if ( seconds == 0 )
            seconds = 1;

        days = seconds / 86400;
        seconds -= days * 86400;

        hours = seconds / 3600;
        seconds -= hours * 3600;

        minutes = seconds / 60;
        seconds -= minutes * 60;

        StringBuffer ago = new StringBuffer();

        ago.append( " about" );

        if ( days > 0 )
            ago.append( " " + getTimeString( days, "day" ) );

        if ( hours > 0 )
            ago.append( " " + getTimeString( hours, "hour" ) );

        if ( minutes > 0 )
            ago.append( " " + getTimeString( minutes, "minute" ) );

        if ( seconds > 0 )
            ago.append( " " + getTimeString( seconds, "second" ) );

        ago.append( " ago." );

        return ago.toString();
    }

    private String getTimeString( int counter, String text )
    {
        StringBuffer reply = new StringBuffer();

        reply.append( counter );
        reply.append( " " );
        reply.append( text );
        if ( counter != 1 )
          reply.append( "s" );

        return reply.toString();
    }

    public void doServerList()
    {
        startIdentServer();

        while ( !connected_ )
        {
            try
            {
                int serverIndex = ( int ) ( Math.random() * ircServers.length );

                System.out.println( "*** connecting to " + ircServers[ serverIndex ] );

                connect( ircServers[ serverIndex ] );
            }
            catch( Exception e )
            {
                System.out.println( "*** connection exception" );

                try
                {
                    Thread.sleep( 60 * 1000 );
                }
                catch ( InterruptedException ie )
                {
                    System.out.println( "*** run interrupted exception" );
                }
            }
        }
    }

    public void printQuoteList( String sender, String[] args )
    {
        String command = botPrivateCommands_[ BOT_PRIV_PRINT ];
        StringBuffer reply = new StringBuffer();

        if ( quotedUsers_.size() > 0 )
        {
            String userid = args[ 1 ];
            ircUser user = null;

            if ( userid != null )
            {
                // userid supplied
                user = lookupUser( userid );

                if ( user != null )
                {
                    printQuotesListUser( sender, user );
                }
                else
                {
                    reply.append( "No quotes from user " );
                    reply.append( userid );
                    sendMessage( sender, reply.toString() );
                }
            }
            else
            {
                // print all quotes from all users
                for ( int i = 0; i < quotedUsers_.size(); ++i )
                {
                    user = ( ircUser ) quotedUsers_.get( i );
                    printQuotesListUser( sender, user );
                }
            }
        }
        else
        {
            reply.append( "No quotes stored." );
            sendMessage( sender, reply.toString() );
        }
    }

    private void printQuotesListUser( String sender, ircUser user )
    {
        StringBuffer reply = new StringBuffer();
        int numQuotes = user.getNumQuotes();

        reply.append( "I have " );
        reply.append( user.getNumQuotes() );
        reply.append( " quotes from " );
        reply.append( user.getNick() );
        reply.append( ":" );
        sendMessage( sender, reply.toString() );

        for ( int j = 0; j < numQuotes; ++j )
        {
            reply.delete( 0, reply.length() );
            reply.append( j );
            reply.append( ": " );
            reply.append( user.getQuote( j ) );
            sendMessage( sender, reply.toString() );
        }
    }

    public void saveQuoteList()
    {
        if ( quotesChanged_ )
        {
            try
            {
                String filename = "quotes-" + channel_;
                FileOutputStream fileOut = new FileOutputStream( filename );
                ObjectOutputStream out = new ObjectOutputStream( fileOut );
                out.writeObject( quotedUsers_ );
                out.flush();
                fileOut.close();

                System.out.println( "*** quotes saved" );
                quotesChanged_ = false;
            }
            catch ( IOException ioe )
            {
                System.out.println( "*** saveQuoteList exception " + ioe.getMessage() );
            }
        }
    }

    public void readQuoteList()
    {
        try
        {
            String filename = "quotes-" + channel_;
            FileInputStream fileIn = new FileInputStream( filename );
            ObjectInputStream in = new ObjectInputStream( fileIn );
            quotedUsers_ = ( ArrayList ) in.readObject();
            fileIn.close();

            System.out.println( "*** quotes read" );
        }
        catch ( IOException ioe )
        {
            System.out.println( "*** readQuoteList exception " + ioe.getMessage() );
        }
        catch ( ClassNotFoundException cnfe )
        {
            System.out.println( "*** readQuoteList exception " + cnfe.getMessage() );
        }
    }

    public void saveSeenList()
    {
        if ( seenChanged_ )
        {
            try
            {
                String filename = "seen-" + channel_;
                FileOutputStream fileOut = new FileOutputStream( filename );
                ObjectOutputStream out = new ObjectOutputStream( fileOut );
                out.writeObject( lastSeenDate_ );
                out.writeObject( lastSeenQuote_ );
                out.flush();
                fileOut.close();

                System.out.println( "*** lastseen saved" );
                seenChanged_ = false;
            }
            catch ( IOException ioe )
            {
                System.out.println( "*** saveSeenList exception " + ioe.getMessage() );
            }
        }
    }

    public void readSeenList()
    {
        try
        {
            String filename = "seen-" + channel_;
            FileInputStream fileIn = new FileInputStream( filename );
            ObjectInputStream in = new ObjectInputStream( fileIn );
            lastSeenDate_ = ( HashMap ) in.readObject();
            lastSeenQuote_ = ( HashMap ) in.readObject();
            fileIn.close();

            System.out.println( "*** lastseen read" );
        }
        catch ( IOException ioe )
        {
            System.out.println( "*** readSeenList exception " + ioe.getMessage() );
        }
        catch ( ClassNotFoundException cnfe )
        {
            System.out.println( "*** readSeenList exception " + cnfe.getMessage() );
        }
    }

    private String[] parseLine( String message )
    {
        // split into
        // command arg1 arg2 arg3 arg4
        // arg4 will contain the rest of the line
        // some of the args might be null

        int i, begin, end;
        String args[] = new String[ 5 ];

        for ( i = 0; i < 5; ++i )
            args[ i ] = null;

        for ( i = 0, begin = 0; i < 5; ++i )
        {
            end = message.indexOf( ' ', begin );

            if ( end == -1 || i == 4 )
            {
                args[ i ] = message.substring( begin );
                break;
            }
            else
                args[ i ] = message.substring( begin, end );

            end++;
            begin = end;
        }

        return args;
    }
}

Hosted by www.Geocities.ws

1