|
Chatbot::TalkerBot - A robot client for a talker, with command handling |
Chatbot::TalkerBot - A robot client for a talker, with command handling
use Chatbot::TalkerBot;
my $talker = new Chatbot::TalkerBot( $alreadyConnectedSocket, {
Username => 'bot', Password => 'bot',
UsernamePrompt => 'Enter username', PasswordPrompt => 'Enter password',
UsernameResponse => '<USER>', PasswordResponse => '<PASS>',
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;
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.
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.
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 <HOST> <PORT>' - 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...
You need to make a new talkerbot object with that socket. UsernameResponse and PasswordResponse will
be strings like '<USER>' and '<PASS>' 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 '<USER> <PASS>' (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:
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
In all of these cases, the bot will supply the newline after each item of text.
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->installCommandHelp( 'foo', ['Syntax: foo <number>', '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).
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.
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.
$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'}
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.
P Kent pause@selsyn.co.uk
$Id: TalkerBot.pm,v 1.8 2001/12/19 04:53:01 piers Exp $
|
Chatbot::TalkerBot - A robot client for a talker, with command handling |