Wednesday, January 30, 2008

Jerkbot gets a Groovy makeover

Tonight I decided to rewrite my example bot for jerklib. I chose to do it in Groovy because i've had the urge to use it. The fact that it integrates well with java makes it a perfect candidate to write a bot (or even a client) using Jerklib! As I was writing it I felt like a kid in a candy store, oogling at the coolness that is Groovy. To run this code download the jerklib jar. Then simply type:
groovy -cp jerklib.jar:. filename.groovy
Or you're on windows then it's:
groovy -cp jerklib.jar;. filename.groovy

Of course you'll replace filename.groovy w/ the file you saved it under.

A Note: I primarily used this class to test my additions to the jerklib -- so you can call this a scratchpad of sorts (at least it was at the time.)


import jerklib.Profile
import jerklib.ProfileImpl
import jerklib.events.listeners.IRCEventListener
import jerklib.events.IRCEvent
import jerklib.events.JoinCompleteEvent
import jerklib.ConnectionManager
import jerklib.events.MessageEvent
import jerklib.events.MessageEvent
import jerklib.events.NickChangeEvent
import jerklib.events.AwayEvent



/**
* Created: Jan 29, 2008 11:42:23 PM
* @author Robert O'Connor
*/
class GroovyJerkbot implements IRCEventListener {
def manager

GroovyJerkbot(String nick, String username, String hostname, int port) {
def profile = new ProfileImpl(username, nick, nick + new Random().nextInt(42),
nick + new Random().nextInt(512))
manager = new ConnectionManager(profile)
manager.requestConnection(hostname, port).addIRCEventListener(this)

}


static void main(args) {
def bot = new GroovyJerkbot("jerkbot", "jerkbot", "irc.freenode.org", 6667)
}

@Override
void receiveEvent(IRCEvent e) {
if (e.getType() == IRCEvent.Type.CONNECT_COMPLETE) {
e.getSession().join("#jerklib")
e.getSession().who("mohadib")
e.getSession().setAway("FOO!")
}
else if (e.getType() == IRCEvent.Type.JOIN_COMPLETE) {
def JoinCompleteEvent event = (JoinCompleteEvent) e
event.getChannel().say("Hai2u")
} else if (e.getType() == IRCEvent.Type.CHANNEL_MESSAGE) {
MessageEvent event = (MessageEvent) e
// what does this is take the channel msg and match it to the pattern ~say foo
def matcher = event.getMessage() =~ /^~say\s+(.*)$/
if (matcher.matches())
{
e.getSession().channelSay(event.getChannel().getName(), matcher.group(1))
}else if(event.getMessage() ==~ /^~part.*$/) {
e.getSession().partChannel(event.getChannel(),"I was asked to leave")
} else {
def whoMatcher = event.getMessage() =~ /^~who\s+(.*)$/
if(whoMatcher.matches()) {
e.getSession().who(whoMatcher.group(1))
}
def awayMatcher = event.getMessage() =~ /^~away\s+(.*)$/
if(awayMatcher.matches()) {
e.getSession().setAway(m.group(1))
}else {
e.getSession().unsetAway()
}
}

} else if (e.getType() == IRCEvent.Type.PRIVATE_MESSAGE) {
MessageEvent event = (MessageEvent)e
if(event.getMessage() ==~ /^~quit.*$/) {
e.getSession().close("I was asked to leave.")
}
} else if (e.getType() == IRCEvent.Type.NICK_CHANGE) {
NickChangeEvent event = (NickChangeEvent) e
println event.getOldNick()
println event.getNewNick()
println event.getUserName()
println event.getHostName()
println e.getRawEventData()
} else {
if (e.getType() == IRCEvent.Type.AWAY_EVENT) {
AwayEvent event = (AwayEvent)e
println "Nick: "+event.getNick()
println "Event Type: "+event.getEventType()
println "Us?: "+event.isYou()
println "Away?: "+event.isAway()

}
}

}

}



As you can see, the import for Random is missing (no need with groovy, it's imported implicitly, whereas in java, only java.lang is imported implicitly! Also, semi-colons are OPTIONAL!! Groovy has native regex support! =~ means to create the matcher for this pattern. The ==~ operator checks if the text matches the pattern given after the ==~ operator.

Nobody would argue with me if I said the above example was verbose. Jason Davis wrote this the following gem:

 
import jerklib.*;
import jerklib.tasks.*;
import jerklib.events.listeners.*;
import jerklib.events.IRCEvent.Type;

def stratMap =[:]
stratMap[ Type.CONNECT_COMPLETE ] = {x-> x.session.joinChannel "#jerklib"}
stratMap[ Type.JOIN_COMPLETE ] = {x-> x.channel.say "Hello World"}
stratMap[Type.CHANNEL_MESSAGE] = {x-> println x.rawEventData}

conMan = new ConnectionManager(new ProfileImpl("foo","gscripbot","gscripbot1","teh"));
session = conMan.requestConnection('irc.freenode.net');
session.addIRCEventListener({x-> close = stratMap[x.type]; if(close != null)close(x)} as IRCEventListener);


Jason used the Strategy Pattern for the event handling. It cleans up your code greatly and eliminates the need for a bunch of if statements. It also can cut down on the verbosity of your code, like it has in this case.

2 comments:

Jason said...

Nice work Robby

Benjamin said...

Good looking out Robby! Great example of how to use groovy's features and jerklib. Perfect for groovy newbs like myself. Jason's code is so succinct it blows my mind!
Cheers!