How to create an interactive Twitter bot with Grails in 10 minutes
Tuesday, March 18th, 2008A while ago I read this interesting post from Glen Smith about connecting to Google Talk with Grails. And I thought, “Wow, that’s really easy!” With merely a dozen lines of code you can already send messages via XMPP to Google Talk clients! And the other day I stumbled upon the Timer bot on Twitter. I thought, with Grails I really can do that in 10 minutes! So here it is, and you can do it in 10 minutes as well!
The way a twitter bot works
If you are already a Twitter geek, you can well skip to the next part. But if not, let’s first understand how an interactive bot on Twitter like the timer bot works.
Timer is a bot that can remind you with your message in a pre-defined amount of time. e.g. You send a direct message over Twitter to Timer like “d timer 45 call mom”, and it will send you a direct message back 45 minutes later, telling you “call mom”.
There are actually two ways to interact with Twitter.
- Using the REST API
- Talking to the Twitter IM bot directly over one of the supported IM networks.
We are talking about “interactive” bots here, which means that you can send some commands to it, and it reacts to your command. So we need to use the second approach. And since one can connect to Google Talk using the open XMPP protocol, talking to Twitter over Google Talk is apparently the better choice.
And here are the steps for some paperwork.
- Register a new user on Twitter to act as the bot account. Let’s name it “mytimer”.
- Log on as “mytimer”, and in the settings, add the Google Talk account which you are going to use for the bot to connect to Google Talk and “chat” with the Twitter IM bot. You need to log on to GTalk using this account and send the supplied verification code to the Twitter IM bot and get your GTalk account confirmed.
OK. Now we have our GTalk account bound to the Twitter bot account. We are going to write the code to make the bot alive.
The plan
Here’s how we are gonna do it.
- Connect to Google Talk service using the Smack XMPP library
- Listen to direct messages coming from the Twitter GTalk account twitter@twitter.com.
- When a direct message comes in, parse it and send the message back in the pre-defined amount of time.
A Twitter direct message sent over Google Talk looks like this:
Direct from {Sender_User_Name}:
{Message Body}Reply with ‘d {Sender_User_Name} hi.’
A Twitter user name can only consist of letters, numbers, and “_”. So we can safely tokenize the message using spaces and newlines as separators. Also, the first and last line of the message are of the fixed format.
Our timer command spec is, described in regular expression:
^\d+(s|m|h)*\s.*$
e.g. 5m buy the milk
This command means to remind in 5 minutes with the reminder “buy the milk”. Accordingly, appended after the first number, “s” means seconds and “h” means hours.
The code monkey job
Now let’s see the code. First define the service settings in Config.groovy.
//File: Config.groovy
chat {
serviceName = "gmail.com"
host = "talk.google.com"
port = 5222
username = "bot@your.domain" //This is the Google Talk account you prepare for the bot
password = "your_password" //The Google Talk password
}
Then create a service to hold the code, say, TwitBotService. It looks like this.
//File: TwitBotService.groovy
import org.codehaus.groovy.grails.commons.ConfigurationHolder as C
import org.jivesoftware.smack.Chat
import org.jivesoftware.smack.ConnectionConfiguration
import org.jivesoftware.smack.Roster
import org.jivesoftware.smack.XMPPConnection
import org.jivesoftware.smack.packet.Message
import org.jivesoftware.smack.PacketListener
import org.jivesoftware.smack.filter.PacketFilter
import org.jivesoftware.smack.filter.PacketTypeFilter
import org.jivesoftware.smack.packet.Message
import org.jivesoftware.smack.packet.Packet
import org.jivesoftware.smack.PacketListenerclass
TwitBotService {
boolean transactional = false
XMPPConnection connection
def connect() {
ConnectionConfiguration cc = new ConnectionConfiguration(
C.config.chat.host,
C.config.chat.port,
C.config.chat.serviceName)
connection = new XMPPConnection(cc)
PacketFilter msgFilter = new PacketTypeFilter(Message.class)
def myListener = [processPacket:{ packet ->
log.debug "Received message from ${packet.from}, subject: ${packet.subject}, body: ${packet.body}"
def msg = packet.body
if(!msg) return
def words = msg.tokenize(' \n')
//Direct message body
def body = words[3..-6]
//The Twitter user who sent the command, and we are going to reply to
def to = words[2][0..-2]
//The amount of time delay
def delay = words[3]
switch(delay[-1]){
case 's':
log.debug "Delay in seconds"
delay = delay[0..-2].toInteger()
break
case 'm':
log.debug "Delay in minutes"
delay = delay[0..-2].toInteger()*60
break
case 'h':
log.debug "Delay in hours"
delay = delay[0..-2].toInteger()*3600
break
default:
//default unit is minute
delay = delay.toInteger()*60
}
//The reminder text to send back
def reminder = body[1..-1].join(' ')
if(delay instanceof Integer){
new Timer().runAfter(delay*1000){
//Send back direct message 'd user message'
def cmd = 'd ' << to << ' ' << reminder
sendChat(packet.from, cmd.toString())
}
}
}] as PacketListener
try {
log.debug "Connecting to server..."
connection.connect()
connection.login(C.config.chat.username,C.config.chat.password)
connection.addPacketListener(myListener, msgFilter)
log.debug "Connected to server"
} catch (Exception e){
log.error "Connection failed: $e.message"
}
}
def disconnect(){
connection.disconnect()
}
def sendChat(String to, String msg) {
try{
Chat chat = connection.chatManager.createChat(to, null)
def msgObj = new Message(to, Message.Type.chat)
msgObj.body = msg
chat.sendMessage(msgObj)
} catch (Exception e) {
log.error "Failed to send message"
}
}
}
And finally, to start the bot with your grails app, simply add a call to TwitBotService#connect() in BootStrap.groovy.
//File:BootStrap.groovy
def gTalkService
def init = { servletContext ->
gTalkService.connect()
}
def destroy = {
gTalkService.disconnect()
}
So here it is, your own timer bot recreated (with a bit more features) in 10 minutes, quick and dirty. And with the same approach, you can create all sorts of interactive Twitter bots.
P.S. Why would you create a Twitter bot while you can create a Google Talk bot in the same way?
Well, a GTalk bot can only interact with online GTalk users. But by leveraging the Twitter platform, users can interact with your bot either online with GTalk, or offline with mobile SMS. And that all comes with no cost at all. Ain’t that cool?