Born in 2008
- in a private IRC channel for my roommates
- mostly for home automation
- (formerly known as Nerdle)
!on kitchen
turned on the kitchen lights
15 minutes until !off kitchen
set a timer to turn off the lights
monitored the kitchen temperature...
mmm that smells delicious
with text-to-speech in the kitchen
- 2009: Joined Freenode
- 2011: Joined my startup's IRC server
- 2013: Joined Esri's IRC server
- 2016: Learned Slack
Karma Bot
Wiki Integration
Slack Reactions
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 difficult, and required a restart
2012: ZenIRCBot
- node.js core process connects to IRC, broadcasts incoming messages on redis
- php and other services listen 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 to other servers
- 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
- 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
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);
}
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!)
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);
}
}
Timing is everything
- Immediate replies look like bots
- Random delays before replying (2-90 seconds) hide the source of the keyword triggers
- Multiple similar replies for a trigger
- Don't always reply to everything
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.