README These modules allow you to build a client for a talker. If you don't know what a talker is, it's a chat system that requires nothing more complex than telnet access. You connect, log in, and whatever you type is what you say. Commands are prefixed by special characters or sequences (e.g. '.quit' logs you out, '> fred hello' whispers 'hello' to the person on the talker called fred, etc) examplebot.plx is a worked example of a bot. It assumes you have a NUTS-like talker running on localhost:5000 - which is what I have. The configuration is in examplebot.cfg Here comes the documentation bit... NAME Chatbot::TalkerBot - A robot client for a talker, with command handling SYNOPSIS use Chatbot::TalkerBot; my $talker = new Chatbot::TalkerBot( $alreadyConnectedSocket, { Username => 'bot', Password => 'bot', UsernamePrompt => 'Enter username', PasswordPrompt => 'Enter password', UsernameResponse => '', PasswordResponse => '', LoginSuccess => 'End of MOTD', LoginFail => 'Incorrect password' } ); $talker->setTalkerCommands( { Say => '', Shout => '! '} ); $talker->say( "Hello world" ); $talker->listenLoop( qr/TELL (\w+)\s*>(.*)/, \&callback, 10 ); $talker->whisper( 'david', 'bye bye' ); $talker->quit; DESCRIPTION Creates a TalkerBot object which can be used to say (or shout, or emote, etc.) things to a talker, it can listen to the talker, or it can sit in an event loop waiting to hear lines of a certain format which it will then act upon. Additionally it makes all talker traffic that it hears available for whatever purpose you want. It also incorporates detailed tracing and action logging should you require it. BACKGROUND The usual talker system is where you telnet in, authenticate with username and password, and simply type things to 'say' them. You prefix your speech with special characters in order to shout it, tell it to a single person, etc. Robot talker clients can be used to provide an interface to databases, other talkers, what's on TV now, or anything you can think of. This projects grew out of a live status-monitoring bot. STEP 1 - NETWORK CONNECTION You need to connect to the remote host somehow, and end up with an IO::Socket object connected to the talker host waiting to log in - i.e. the connection has _just_ opened. There are two static, exported functions that can help you with this: $socket = connect_directly( $talkerhost, $talkerport ); $socket = connect_through_firewall( $firehost, $fireport, $fireprompt, $firecommand, $talkerhost, $talkerport ); In the latter, $firecommand is a string such as 'connect ' - where the actual values are substituted in. These both die if connection is unsuccessful. Hence, if you get back an IO::Socket object from these then you must have connected to your destination. Or make a connected IO::Socket object yourself. Now that you've connected to the talker you need to go all object oriented... STEP 2 - TALKER OBJECT CREATION You need to make a new talkerbot object with that socket. UsernameResponse and PasswordResponse will be strings like '' and '' for talkers that want such things after individual prompts. If your talker just prompts for 'username and password on the same line' just set UsernamePrompt to whatever that last line is, set UsernameResponse to ' ' (with the pointy brackets) and ignore the Password(Prompt|Response) entries, as there is no Password prompt or response. Note that the login is done in line-oriented mode. If your talker login prompts do not end in a newline then the login stage will hang, trying to read a full line. See the additional options below for a solution. my $talker = new TalkerBot( $socket, { Username => $user, Password => $pass, UsernamePrompt => $TALK_USER_PROMPT, PasswordPrompt => $TALK_PASS_PROMPT, UsernameResponse => $TALK_USER_RESPONSE, PasswordResponse => $TALK_PASS_RESPONSE, LoginSuccess => $TALK_OK, LoginFail => $TALK_FAIL } ); ADDITIONAL OPTIONS: Aswell as those options you can add these if you want: * AlreadyLoggedIn => 1 - this indicates that the $socket is a socket that has ALREADY logged in to the talker - i.e. it is READY TO TALK. Use this if your talker has a login procedure that we can't handle using our *Prompt and *Response syntax - log in yourself and then pass us the socket. * NoCommands => 1 - in listenLoop() the robot should not respond to any commands. See the COMMANDS bit below. If your bot will be executing commands you may want to use the authentication features: $talker->setAuthentication( $cryptedpassword, { pkent => 1, johna => 1 }); Some commands require passwords or are only available to certain users. This method sets the password (you give it the crypted form) and a group of people who are also considered trusted. Some commands require passwords, whereas some will let people in the admin group get away with entering a wrong password and will still allow them access, and others may not take passwords at all, but will only work for admin users. You may want to tell the talkerbot about the commands it needs to use on this talker, although the defaults should be ok for many talkers. You use it like this: $talker->setTalkerCommands( { Say => '', Shout => '! '} ); The full command set is: Tell Say Emote PEmote REmote PREmote SayList EmoteList PEmoteList Shout Quit INTERACTION In all of these cases, the bot will supply the newline after each item of text. $talker->say( $text [, $text2 ... ] ); Says some text out loud, in whatever group you happen to be in. $talker->whisper( $person, $text [, $text2 ... ] ); Whisper/tell some text to somebody. $talker->emote( $text [, $text2 ... ] ); $talker->pemote( $text [, $text2 ... ] ); $talker->remote( $person, $text [, $text2 ... ] ); $talker->premote( $person, $text [, $text2 ... ] ); From the above, you can guess how these work. $talker->saylist( $list, $text [, $text2 ... ] ); $talker->emotelist( $list, $text [, $text2 ... ] ); $talker->pemotelist( $list, $text [, $text2 ... ] ); And similarly for these, for talking to lists on some talkers. $talker->shout( $text ); And note this only shouts one line of text. It just seemed wrong to allow arbitrarily long shouts. $talker->chant( $text ); The bot will send to the talker exactly what you tell it... useful for getting it to change groups, lock its group, change .info and that sort of thing that may require exact commands. $lineOfText = $talker->listenOnce; Tells the talker object to listen, and returns when we have a single line of text. Returns the line that was heard, with no newlines/carriage returns. $talker->listenLoop( $matchre, \&callback [, $interrupt ] ); Go into an event loop and listen out for lines matching the regexp $matchre. That regexp must have parentheses such that $1 is the speaker's name, and $2 is the full text of what they said. You will probably want to set the regexp so that the bot only listens to things whispered to it. You must provide a code reference for a subroutine that is called whenever a matching line is heard. The sub will be called with ($talker, $person, $command, @args) where @args is whatever they said to the bot split on whitespace. If you need a single string just join() the array back up. If you provide an $interrupt variable then the bot will _also_ call the callback with arguments of ( $talker, 'ALRM' ) after every $interrupt seconds of inactivity. NOTE that this usually means that the talker must be totally quiet for $interrupt seconds. $talker->authenticate( $password [, $person ] ); Returns 1 if the password is the right password, or if the person is on the admin group, 0 otherwise. Note that the methods in this package do not do any authentication - that's up to the controlling program. MISC $talker->installCommandHelp( $commandname, \@linesOfHelpText ); Installs a load of help text for the named command. Usual idiom is: $talker->installCommandHelp( 'foo', ['Syntax: foo ', 'Returns the foo function'] ); This method actually passes through to the talkerbot's Chatbot::TalkerBotCommands object, which registers the help internally for use by its builtin 'help' command handler. Only use this if you're using external commands (see below). TB_LOG( 'some text' ); - not autoexported, say: import Chatbot::TalkerBot qw/TB_LOG/; Logs whatever string you give it. Default action is to send to STDOUT with a timestamp, but you can override this sub. Important goings-on inside the bot are logged using this sub. TB_TRACE( 'debugging text' ); - not autoexported, say: import Chatbot::TalkerBot qw/TB_TRACE/; Logs whatever you give it only if $Chatbot::TalkerBot::TRACING is 1. Default action is to send to STDOUT with a timestamp, but you can override this sub. Pretty much everything that happens inside the bot is traced using this function - very useful in debugging and testing. NOTE that $TRACING is set to 0 by default. TB_TRAFFIC( 'blah blah' ); - not exported. This subroutine is called with every line of text (with no newline) that the talker hears, no matter what it is. By default this sub just calls TB_LOG, but you can override this sub. Not exported as it should just be used for talker traffic. OVERRIDING SUBROUTINES: In your controlling program you can add a line like this: *Chatbot::TalkerBot::TB_TRAFFIC = \&my_traffic_logger; which will make the bot send all traffic that it hears to your subroutine where you can do what you want with it. COMMANDS - INTERNAL AND EXTERNAL When the bot is in listenLoop() and it hears a line matching the regexp it splits whatever the person said into a list of words, and takes the first word to be the command name. Some commands are handled internally, but if the command given is not an internal command then the callback function is called. For example, the command 'help' is handled internally - you ask for help by, for example, whispering 'help' to the bot (which would mean typing '> statusbot help' on many talkers). A good default regexp should only listen to whispers, so any commands must be given to the bot via a whisper. A well-behaved callback will whisper any response to the person who originally whispered to the bot, and not say things out loud, and additionally it will say if a requested command doesn't exist. Internal commands are actually handled by a Chatbot::TalkerBotCommands object. See the docs for that module to find out what it can handle. If you want a bot that does not respond to ANY commands being whispered at it you can pass in NoCommands => 1 as part of that hash of options. If you only want your bot to respond to the internal commands, and not try to run a callback (you're probably using it to quietly log all talker traffic) then just pass in 'undef' instead of a callback ref to listenLoop. PERSISTENT DATA $talker->{'Prefs'} is a hashref that allows all commands to store their own persistent data, for whatever reason they like. To avoid namespace clashes commands MUST put their data under a key that is exactly the same as their command name, e.g. the die command puts some state data in $talker->{'Prefs'}->{'die'} BUGS Everything's done in line-oriented mode. This could be a problem if the login prompts for your talker do not end with a newline because the login part will hang. VERSION P Kent pause@selsyn.co.uk $Id: README,v 1.3 2001/12/19 05:57:03 piers Exp $ NAME Chatbot::TalkerBotCommands - Some builtin command handling for the TalkerBot to use SYNOPSIS my $handler = new Chatbot::TalkerBotCommands; $boolean = $handler->isCommand( 'help' ); $rv = $handler->doCommand( $talker, $person, $command, @args ); $handler->installCommandHelp( 'foo', ['Syntax: foo ', 'Returns the foo function'] ); $rv is 0 unless the command wants to stop the bot, in which case a return value of 1 will kill the bot. Hence, almost all commands should return 0. $talker in the above should be a Chatbot::TalkerBot object. DESCRIPTION This module is intended to be used from inside Chatbot::TalkerBot, and not directly by anyone. It has only three object methods - one that discovers whether a command is handled by this module, one actually runs the command, and one is used to install help information for use by the 'help' builtin command handler. See Chatbot::TalkerBot docs section COMMANDS - INTERNAL AND EXTERNAL, to see when this object is called upon to handle a command. BUILTIN COMMANDS help The most basic command. If called with no args (e.g. a user typed '> talkerbot help') it returns a minimal help syntax message; if called with the argument 'commands' if whispers back the full list of available commands (well, all commands that have been registered with installCommandHelp); else it tries to return help for the command whose name is the given argument. E.g. '> talkerbot help foo' returns help for the foo command. version Returns version number and some basic stats - age, I/O amounts - for the talkerbot object. process Return information about the talkerbot's controlling process - memory usage and pid. echo Requires that the first argument is a password, although if your talkerbot has an admin group defined members of that group can enter the wrong password. This command forces the bot to say something. die Requires a correct password. Makes the bot gracefully leave the event loop. VERSION $Id: README,v 1.3 2001/12/19 05:57:03 piers Exp $