ELIZA: Back in Slack


how to spice up your Slack account by integrating ELIZA, the 1960s natural language processing program

By Florian Brick

Remember ELIZA, Weizenbaum’s computer program from the 1960s for processing natural language? Check out how to spice up your Slack account by integrating ELIZA. Fun times guaranteed!

Slack Preparation

First of all, you need to set up a Slack account. There is a free basic version, which is sufficient to have a little fun with ELIZA. After you’ve created an account, go to https://api.slack.com/web and create a token for your user — you will need it later on. Create a new bot under https://slack.com/services/new/bot and name it “eliza-bot” (or another name of your choice). The last thing you need to do in Slack to be ready to go is to create a channel, name it “Eliza”, and then add the bot you just created to it.

ELIZA Implementation

You can download a Java implementation of ELIZA here. Now, you need to edit your pom.xml. Add the processing framework as well as the Slack integration as shown below:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.projecta.example</groupId>
	<artifactId>eliza</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>eliza</name>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<testSource>1.8</testSource>
					<testTarget>1.8</testTarget>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>com.ullink.slack</groupId>
			<artifactId>simpleslackapi</artifactId>
			<version>0.4.6</version>
		</dependency>

		<dependency>
			<groupId>org.processing</groupId>
			<artifactId>net</artifactId>
			<version>3.0</version>
		</dependency>
	</dependencies>
</project>Code language: HTML, XML (xml)

Add all ELIZA Java files you downloaded to a package of your choice. You’ll see a compiler error in Eliza.java. Open the file and replace the constructor with the code below:

public Eliza() {
		defScript = new DefaultScript();
		readDefaultScript();
	}Code language: PHP (php)

Slack Integration

It’s time to integrate ELIZA into your Slack account. We already added the required API to our pom.xml. Now, all we have to do is set up three simple classes and then ELIZA will be able to answer all of your questions, though you may not necessarily become any wiser. 😉

Our first step here is to implement a service that is able to connect to Slack and enables ELIZA to send messages.

ElizaSlackService.java:

import java.io.IOException;

import com.ullink.slack.simpleslackapi.SlackChannel;
import com.ullink.slack.simpleslackapi.SlackSession;
import com.ullink.slack.simpleslackapi.impl.SlackSessionFactory;

public class SlackElizaService {
	private final static String ELIZA_API_TOKEN = "REPLACE_WITH_YOUR_API_TOKEN";
	
	private SlackChannel elizaChannel = null;
	private SlackSession elizaSession = null;
	private boolean shutdown = false;
	
	public SlackElizaService() throws IOException {
		// create a session by connecting to slack
		elizaSession = SlackSessionFactory.createWebSocketSlackSession(ELIZA_API_TOKEN);
		elizaSession.connect();

		// find the eliza channel
		elizaChannel = elizaSession.findChannelByName("eliza");
		
		// send a message to channel that Eliza opened her office
		elizaSession.sendMessage(elizaChannel, "Eliza opened her office!", null);
		
		// register new listener so that Eliza can answer to users
		elizaSession.addMessagePostedListener(new ElizaMessagePostedListener(this));
	}
	
	public void sendMessage(String message) {
		elizaSession.sendMessage(elizaChannel, message, null);
	}

	public void shutdown() {
		try {
			elizaSession.sendMessage(elizaChannel, "Eliza closed her office!", null);
			elizaSession.disconnect();
			shutdown = true;
		} catch (IOException e) {
			System.err.println("failed to close session: " + e.getMessage());
			e.printStackTrace();
		}
	}

	public boolean isShutdown() {
		return shutdown;
	}
}Code language: PHP (php)

Don’t forget to replace the API_TOKEN with the one you created earlier. Since communication in Slack requires a listener, we need to provide one by creating ElizaMessagePostedListener.java:

package com.projecta.example.eliza;
import java.util.HashMap;
import java.util.Map;
import com.ullink.slack.simpleslackapi.SlackSession;
import com.ullink.slack.simpleslackapi.events.SlackMessagePosted;
import com.ullink.slack.simpleslackapi.listeners.SlackMessagePostedListener;
public class ElizaMessagePostedListener implements SlackMessagePostedListener {
    private SlackElizaService slackElizaServiceImpl = null;
    
    private Map<String, Eliza> username2Eliza = new HashMap<>();
    
    public ElizaMessagePostedListener(SlackElizaService slackElizaServiceImpl) {
        this.slackElizaServiceImpl = slackElizaServiceImpl;
    }

    private String askEliza(String username, String message) {
        // Eliza has a ‘memory’ and we want her to have a separate memory for each user she talks to
        Eliza eliza = username2Eliza.get(username);
        
        if (eliza == null) {
            eliza = new Eliza();
            username2Eliza.put(username, eliza);
        }
        
        return eliza.processInput(message);
    }
    
    @Override
    public void onEvent(SlackMessagePosted event, SlackSession session) {
        // we only want to listen to this channel
        if (!event.getChannel().getName().equals("eliza")) {
            return;
        }
        
        // don't react on own messages ;)
        if (event.getSender().getId().equals(session.sessionPersona().getId())) {
            return;
        }
        
        if (event.getMessageContent().toLowerCase().startsWith("@eliza")) {
            // answer to people
            slackElizaServiceImpl.sendMessage("@" + event.getSender().getUserName() + ": " + askEliza(event.getSender().getUserName(), event.getMessageContent().substring(6)));
        } else if (event.getMessageContent().toLowerCase().startsWith("<@" + session.sessionPersona().getId().toLowerCase() + ">")) {
            // answer to people
            slackElizaServiceImpl.sendMessage("@" + event.getSender().getUserName() + ": " + askEliza(event.getSender().getUserName(), event.getMessageContent().substring(session.sessionPersona().getId().length() + 5)));
        } else if (event.getMessageContent().toLowerCase().endsWith(" has joined the group")) {
            // send a welcome message when someone enters eliza's office
            slackElizaServiceImpl.sendMessage("@" + event.getSender().getUserName() + ": Welcome! I'm Eliza! How can I help you?");
        } else if (event.getMessageContent().equalsIgnoreCase("close office")) {
            // close the office
            slackElizaServiceImpl.shutdown();
        }
    }
}Code language: JavaScript (javascript)

We want ELIZA to only react to those messages in “her” channel which she has not sent by herself, so we need to restrict which messages ELIZA will receive or answer. Since the API key is assigned to your personal user, ELIZA receives all of your messages regardless of the channel they are sent on. Moreover, ELIZA would also receive her own messages since the websocket sends all messages and actions to the bot. So we set up the bot to just listen to the respective channel and to ignore its own messages. In addition, ELIZA should only react to messages when she is actually addressed (e.g. by “@Eliza” or “@eliza-bot”) and we want to be able to shut down the bot within Slack by typing “close office”.

It would also be nice to let ELIZA say hello to new users. The Slack implementation offers a class called SlackGroupJoinedListener which could be utilized for this purpose, but unfortunately there seems to be a bug. I’ll dig into it soon, check how to fix it, and let the framework developers know. But for now, there is a workaround: As you can see in the code, I utilized the class named SlackMessagePostedListener instead.

Finally, we need a ElizaMain.java class to start ELIZA:

import java.io.IOException;

public class ElizaMain {
	
    public static void main( String[] args ) throws IOException, InterruptedException {
    	SlackElizaService service = new SlackElizaService();
    	
    	while (!service.isShutdown()) {
    		Thread.sleep(100);
    	}
    }
}Code language: JavaScript (javascript)

Consulting ELIZA

ELIZA probably won’t boost your productivity or provide you with enlightening insights, but who knows! At least, you’ll have a little fun and learn how easy Slack integrations can be implemented if you weren’t already aware.

To give you an impression of ELIZA in action I’d like to share the following “conversation” with you:

Welcome! I’m Eliza! How can I help you?
 — Hey Eliza! How are you doing today?
 Why do you ask?
 — Why not?
 Does that question interest you?
 — Not really.
 I’m not sure I understand you fully.
 — Me neither.
 Please go on.
 — How do you like Slack?
 What is it you really want to know?
 — You tell me.
 Why do you think I tell you?
 — So you won’t tell me?
 You like to think I won’t tell you — don’t you?
 — So you will tell me?
 What makes you think I will tell you?
 — Nothing, to be honest.
 What does that suggest to you?
 — That I ask the wrong questions.
 You say that you ask the wrong questions?
 — Yes.
 You seem to be quite positive.
 — What does that suggest to you?
 Are such questions much on your mind?
 — Since I know you…
 Perhaps in your fantasies we know each other.