Posted: Wed Aug 17, 2005 10:58 pm Post subject: Sending messages with Net::OSCAR
I'd like to be able to send messages using Net::OSCAR that do not depend on responding to an incoming message. My simple "respond back to sender" code works fine, but "just send this message out" code does not. Any pointers would be appreciated.
Pete
Code:
#!/usr/bin/perl
use strict;
use Net::OSCAR qw(:standard);
my $screenname = 'bot_username';
my $password = 'bot_password';
my $destination = 'destination_username';
my $oscar = Net::OSCAR->new();
$oscar->set_callback_im_in(\&im_in);
$oscar->signon($screenname, $password);
print "Username $screenname logged on.\n";
# Bot never appears to be "online", though.
#auto_responder(); # This works fine when uncommented
send_one_message('hello'); # This does not work.
sub send_one_message {
my ($message) = @_;
print "$destination: $message\n"; # This prints out as expected
$oscar->send_im($destination, $message, 0); # nothing happens
}
sub auto_responder {
while (1) {
$oscar->do_one_loop();
}
}
sub im_in {
my($oscar, $sender, $message, $is_away) = @_;
print "Received $message from $sender. Sending it right back.\n";
print "$sender: $message\n";
$oscar->send_im($destination, $message, 0);
}
Your "auto_responder" code runs the oscar loop, which is required to process incoming messages from the server and by messages I don't just mean messages from users, but server messages (setting up buddy lists, pings maybe, etc). When you comment that code out, you effectively shut down the connection. Try something like this:
Code:
sub auto_responder {
my $i = 0;
while (1) {
$i++;
if( $i == 1 ) { &send_one_message('hello'); }
$oscar->do_one_loop();
}
}
I use $i so that we only send out one message. This is a sort of clunky way to do it, but it should work. A better solution is to tell us why you are trying to send messages that are NOT in response to an incoming message or some other event.
Your "auto_responder" code runs the oscar loop, which is required to process incoming messages from the server and by messages I don't just mean messages from users, but server messages (setting up buddy lists, pings maybe, etc). When you comment that code out, you effectively shut down the connection. Try something like this:
You nailed it. I'm trying to tail a file and when new lines come in, send out messages. I wasn't calling do_one_loop() enough to fully sign on. Debug mode helped a little, too. A followup:
If I want to be able to be more "botlike" and reply to messages etc. etc., this code won't work, since once it's signed on it will only do_one_loop() when a new line comes in in the log file. Is the solution to re-tail my file every time through my loop ($curr = `tail -n1 $log_file`), detecting changes (if $curr ne $last), so that do_one_loop() is executed many times? Or is there a smarter way?
Thanks for your help! Code below in case it helps someone else out.
Pete
Code:
#!/usr/bin/perl
use strict;
use Net::OSCAR qw(:standard);
my $log_file = './logfile.txt';
my $screenname = 'botusername';
my $password = 'botpass';
my $destination = 'enduser';
my $oscar = Net::OSCAR->new();
$oscar->set_callback_im_in(\&im_in);
$oscar->set_callback_signon_done(\&signon_done);
$oscar->loglevel(5);
my $signed_on = 0;
$oscar->signon($screenname, $password);
$oscar->set_visibility(VISMODE_PERMITALL);
$oscar->set_stealth(0);
while(!$signed_on) {
$oscar->do_one_loop();
}
open INFILE, "tail -n0 -f $log_file |" or die "Can't open $log_file: $!";
while (<INFILE>) {
$oscar->do_one_loop();
chomp;
print "$destination: $_\n";
$oscar->send_im($destination, $_, 0);
}
sub im_in {
my($oscar, $sender, $message, $is_away) = @_;
print "Received $message from $sender. Discarding it for now.\n";
}
OK, so what you're trying to do is be notified each time a new line is added to a log file? But with Oscar, you MUST let the loop run at all times. So you need to rework your code. Run the loop as normal with a while( 1 ) and inside that you have oscar's do loop and you check to see if the file has been updated, if it has do a tail and get only the last line and send it. Does that make sense?
You might want to see how fast ocsar's do loop is running and do the file check less often, say once every 15 seconds or so, depending on how fast you need the notification.
You might want to see how fast ocsar's do loop is running and do the file check less often, say once every 15 seconds or so, depending on how fast you need the notification.
That's exactly the conclusion I came to. I'm actually tailing enough lines so that I know that I'll get some overlap, and then storing those lines in a hash and checking to see whether I've sent those lines or not. That way if more than one line need to be sent, I'll know it.
Any suggestions on how to throttle outgoing messages? I think if I send too many at once, I'll get warned and shut down. I'm putting a sleep(1) after every message sent, but I don't know if that's sufficient or not.
Thanks a lot for the help! I'm afraid now that I understand what's going on I'm going to have feature creep.
You don't want to use sleep. Sleep freezes the program for that number of seconds, which also freezes the oscar loop. Many people fall into the trap of using sleep to slow things down. If this bot is only going to be used by one person, you might be able to get away with it, but if you have hundreds of users and say 20 of them are using your bot at the same time, you'll be sleeping for 20 seconds at a time, which is very bad.
AIM bots have an inherent problem due to rate limits. Unless you're willing to pay AOL $100k to turn off rate limits for your bot, you're out of luck. You need to wait a minimum of 3 seconds in between outgoing messages. If you're bot is warned, you need to wait longer depending on the warning rate (hmm, does AIM even have warnings anymore?). Anyhow, if you have 20 simultaneous users, that means that they will be waiting at least a minute to get a reply from the bot. This is why so many developers have dropped AIM bots and moved to MSN or Jabber, etc.
Anyhow, now that I've given this long boring lecture, how do you deal with it? You need a message queue. Instead of sending out a message immediately, you put the message in the queue. Then in your oscar loop, every 3+ seconds, you get the first message in the queue, send it and remove it from the queue. This is the best way to handle the situation.
And as you get more into feature creep, you'll find a message queue is a great thing, because you can control message priority, for example moving messages to bot admins higher in the queue, etc.