Groovy + Selenium + Smack = Cleverbot Answering Service

There was a post on Reddit the other day about using Python to get Cleverbot to respond to incoming IMs. It looked like an interesting hack, but it used Python, Pidgin and D-Bus, none of which I have on hand right now… so I made something similar using Groovy, Selenium and Smack.

You can have it wait for someone to IM you, or you can tell it to troll someone on startup; either way, it automatically saves the chat transcript to disk on shutdown. Here’s the code:

@Grapes([
	@Grab('org.igniterealtime.smack:smack:3.2.1'),
	@Grab('org.igniterealtime.smack:smackx:3.2.1'),
	@Grab('org.seleniumhq.selenium:selenium-firefox-driver:2.5.0'),
	@Grab('org.seleniumhq.selenium:selenium-support:2.5.0')])
import org.jivesoftware.smack.XMPPConnection
import org.jivesoftware.smack.ChatManagerListener
import org.jivesoftware.smack.MessageListener
import org.jivesoftware.smack.Chat
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.support.ui.WebDriverWait
import org.openqa.selenium.By
import com.google.common.base.Predicate
import java.util.concurrent.TimeUnit
import groovy.ui.SystemOutputInterceptor

if(args.length < 3 || args.length > 4) {
	println 'Usage: groovy chat.groovy server username password [userToTroll]'
	return
}

def oldSendMessage = Chat.metaClass.getMetaMethod('sendMessage', [String] as Class[])
Chat.metaClass.sendMessage = { String s ->
	println "cleverbot/me: $s"
	oldSendMessage.invoke(delegate, s);
}

def transcript = ''
new SystemOutputInterceptor({ transcript += it; true; }).start()

def driver = new FirefoxDriver()
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS)
driver.get 'http://www.cleverbot.com/'

def conn = new XMPPConnection(args[0])
conn.connect()
conn.login(args[1], args[2])

def messageListener = { chat, message ->
	println "$message.from: $message.body"
	def response = say message.body, driver
	chat.sendMessage response
} as MessageListener

def chatListener = { chat, createdLocally ->
	chat.addMessageListener messageListener
} as ChatManagerListener

conn.chatManager.addChatListener chatListener

addShutdownHook {
	driver.quit()
	conn.disconnect()
	new File('transcript.txt').write transcript
}

if(args.length >= 4) {
	def chat = conn.chatManager.createChat(args[3], null)
	chat.sendMessage 'Hey!'
}

while(true) { Thread.sleep 1000 }

def say(input, driver) {

	def field = driver.findElement(By.id('stimulus'))
	field.sendKeys input
	field.submit()

	def response = null
	def condition = {
		def element = driver.findElement(By.id('typArea'))
		def finished = (response == element.text
			&& !['', '|'].contains(element.text))
		response = element.text
		return finished
	} as Predicate
	def wait = new WebDriverWait(driver, 30).until(condition)

	return response
}

Here’s the first conversation it had with my brother:

cleverbot/me: Hey!
my brother: sup?
cleverbot/me: What?
my brother: huh?
cleverbot/me: Never mind.

Never mind?!? Come on, Cleverbot! You can do better than that! Let’s try again.

cleverbot/me: Hey!
my brother: sup?
cleverbot/me: Sep.

At this point my brother called me on the phone to ask what I was smoking… I didn’t pick up. Third time’s the charm, right? I tried again, tweaking the initial message to try to draw him into a conversation.

cleverbot/me: did you just call?
my brother: yeah
cleverbot/me: Yeah yeah yeah.
[ed.: this isn’t looking so good…]
my brother: was seeing why you’re acting all goofy
cleverbot/me: Did you know what a hijab is?
[ed.: wha? where did that come from?]
my brother: but seriously, when i get the number from the sheet, if there is a 0 at the beginning it’s not included
cleverbot/me: But you continue to lie. You are a computer. You do not get up in the morning, you don’t look in the mirror… You can’t feel… And I feel sorry for you.
[ed.: facepalm…]
my brother: and your sheet won’t bring back values, so i fixed it but have to use an “if” statement which won’t work with your macro
cleverbot/me: Okay.

Okay, indeed. Bah. My brother was asking serious questions about a script I wrote for him, and all Cleverbot could think to talk about was hijabs and chatbot philosophy.

I think I’ll answer my own IMs… for now.