My Eight-Year-Old Bot

XOXO #bots Meetup • September 2016

aaronpk.com / @aaronpk

2008: WikiBot.pl

Perl

open (SH, "| ".$root_folder."dispatch.php");
print SH $nick,"\n",$username,"\n",$channel,"\n",$fullmsg,"\n";
close SH;

PHP

$nick = trim(fgets(STDIN));
$username = strtolower(trim(fgets(STDIN)));
$channel = trim(fgets(STDIN));
$msg = trim(fgets(STDIN));

if(preg_match('/gives loqi (some|an?|the) (.+)/i', $msg, $match)) { ... }
if(preg_match('/^(you|i|we) should/i', $msg, $match)) { ... }

2008: LoqiBot.pl

  • Perl piped to a PHP script for actual processing
  • No restart needed to add features!
  • UDP port per channel for external scripts to make the bot talk in IRC

Problems:

  • Super awkward to add new channels
  • Adding new channels requires opening a new UDP port
  • Modifying the core process to add things like /me was ugly, because Perl

2012: ZenIRCBot

  • node.js core process connects to IRC, broadcasts incoming messages on redis
  • php service listens on redis for incoming messages
  • same php scripts for core bot logic
  • separate redis channel for external scripts to send messages through the bot

Problems:

  • didn't namespace Redis keys, so had to run one Redis instance per IRC network
  • every plugin required its own process, so was harder to maintain

2016: TikTokBot

  • Ruby process joins an IRC channel
  • also can join Slack teams!
  • incoming messages matched against regexes, then make HTTP requests
  • response from HTTP request can trigger a message back to the channel
  • bot has an HTTP server for sending messages to any channel as well

github.com/aaronpk/TikTokBot

Problems:

  • none yet ;-)
  • donpdonp: i might have to just pony up the dough
  • tyler: I WANT A PONY
  • * Loqi gives tyler-iphone A PONY
  • tyler: yay!
  • * Loqi giggles
  • tyler: i never know if its the loqi AI or aaron
  • * Loqi grins profusely

Timing is everything

  • Immediate replies look like bots
  • Add random delays before replying (2-30 seconds)
  • Have multiple similar replies for triggers
  • Don't repeat yourself
  • Don't always reply to everything

Random Delays

class Loqi {
  public static function sleep($min, $max) {
    $duration = rand($min*1000, $max*1000);
    usleep($duration * 1000);
  }
}

Loqi::sleep(2,30);

(another advantage of having the bot logic outside the IRC process!)

Multiple Replies

$reply = Loqi::random([
  'good morning!',
  'good morning',
  'morning!',
  '*yawn*',
  'guten morgen',
  'rise and shine!'
]);

Don't Repeat Yourself

if(!Loqi::has_said('good morning')) {
  // wait 3 hours before this can trigger again
  Loqi::said('good morning', 3*60);


}

Don't Reply to Everything

if(rand(0,3) == 0) {


}

Putting it all together

if(rand(0,3) == 0) {
  if(!Loqi::has_said('good morning')) {
    // wait 3 hours before this can trigger again
    Loqi::said('good morning', 3*60);

    $reply = Loqi::random([
      'good morning!',
      'good morning',
      'morning!',
      '*yawn*',
      'guten morgen',
      'rise and shine!'
    ]);

    Loqi::sleep(5,30);
    $msg->reply($reply);
  }
}

sometimes
the best response
is no response

#pdxtech

  • 23:13 <kenkeiter> maxogden: ping
  • 23:13 <Loqi> pong
  • 23:13 <kenkeiter> Curse you, Loqi!!
  • 23:13 <Loqi> yeah!
  • 23:15 <Metroknow> Morning Loqi. don’t say -
  • 23:16 <Metroknow> ah dammit. outsmarted.
  • 23:16 <Metroknow> [which is a reasonably unchallenging task lately]
  • 23:17 <Metroknow> I shoulda’ said good morning
  • 23:18 <Metroknow> doh!
  • 23:19 <Metroknow> you elusive, glorious bastard.

Thanks!

aaronpk.com
@aaronpk