Add AI to Discord Bot with Watson

Blumareks
Zero Equals False
Published in
7 min readOct 26, 2020

--

This blog post expands on the challenge of adding some AI driven development for the Node.js based chatbot with IBM Watson. You might want to watch a walk through this blog post on Crowdcast here (free two parts): https://www.crowdcast.io/e/discord-chatbot-with-ibm (I was live on 10/19 and 10/26/2020).

Adding Watson Assistant — an AI based chatbot engine to Discord

Prerequisites

Read the introduction part of my blog to get some more details here (a friendly link): https://medium.com/voice-tech-podcast/yeah-a-discord-ai-powered-bot-baby-d9cb73b4775d?source=friends_link&sk=5fd7e544211b3f64b8e5ca476dd547c9

The container with a Discord bot

Basically, you would like to clone my repository to create a container with a bot.

git clone https://github.com/blumareks/discord-bot.git
cd discord-bot

You can build your bot from your image to get the Ping-pong responses.

docker build -t <YOUR_DOCKER_ID>/node-discordbot:1.0 .

And start your chatbot container — passing the token in the start instruction:

docker run -e token="<YOUR_TOKEN>" -d <YOUR_DOCKER_ID>/node-discordbot:1.0

Alternatively used my version 1.0 of the bot here:

docker run -e token="<YOUR_TOKEN>" -d blumareks/node-discordbot:1.0

The AI based chatbot backend with IBM Watson

Now you would need to create a service with IBM Watson for free (well, free for developers for non-production loads). Get the free Lite Account for IBM Cloud here (or simply sign up using this link):

Try Watson Assistant yourself for free — just use this link to invoke IBM Cloud registration/signup/login page (this URL gives an author = me some brownie points): https://ibm.biz/Bd2CUa

Now navigate to Catalog and pick the Watson Assistant. You can have only one ☝️ such a service for Pay-Go accounts (free — aka not paying). Now we will do the magic — read on.

Creating a simple dialog

Creating a simple dialog that would respond to the Pings from a user. Instead of processing everything — you will use the prefix, and only process prefix based calls (think about the “activation word” like “Hello Watson”.

I added the following lines:

const regexPrefix = new RegExp('blu*'); //matching prefix blu...
client.on('message', msg => {
if (regexPrefix.test(msg.content)) {
msg.reply('Pong!');
}
//console.log(msg.content + " :" + regexPrefix.test(msg.content));
});

Let’s assume that for now if users write:

blu ping

Your blu-bot would respond with some quotes from Apex Legends:

It is a beautiful day (Gibby)

I think you haven’t been even trying (Lifeline)

I’ll high five you all (Path)!

I made it through (Wraith)!

Adding Intents and Entities in the Dialog of Watson Assistant

Alternatively when someone would write

blu when starts the new season of Apex?

the new season starts on November 3rd

so how to implement it in Watson Assistant?

Step 1 Adding Watson Developer Cloud

You need to add Watson Developer to your package.json (or to your native system if you are doing it without Docker).

npm install ibm-watson

Step 2 Creating new Dialog nodes, adding Intents, and Entities

Simply add new Dialog nodes, and suggest Intents and Entities in IBM Watson Assistant — watch the second part of the webinar here for more detail: https://www.crowdcast.io/e/discord-chatbot-with-ibm

Step 3 Get apiKeys, and other env variables

You will need a set of variables — two from the Cloud Service (ASSISTANT_URL and ASSISTANT_APIKEY), and one from the Assistant UI (ASSISTANT_ID).

const ASSISTANT_ID= process.env.assistantId; //from assistant UI
const ASSISTANT_URL=process.env.assistantUrl;//service-credentials
const ASSISTANT_APIKEY=process.env.apiKey; //service-credentials

Step 4 Adding the Watson SDK based connectivity to your code

This part is relatively easy.

You need to add couple of instructions to authenticate (with the above mentioned keys) 🔑.

try {
auth = new IamAuthenticator({ apikey: ASSISTANT_APIKEY });
url = ASSISTANT_URL;
} catch (e) {
console.log(e.result.stringify);}
assistant = new AssistantV2({
version: ASST_API_VERSION,
authenticator: auth,
url: url,
disableSslVerification: disableSSL,
});}

Then to call the service and parse the JSON of the response. This is how I have done it:

var text = 'no text' // the response text from Watson Assistantasync function getMessage(request, sessionId) {
assistant.message(
{
input: { text: request },
assistantId: ASSISTANT_ID,
sessionId: sessionId
})
.then(response => {
console.log("successful call");
console.log("text0: " + JSON.stringify(response.result, null, 2)); //an entire response from the service
text = JSON.stringify(response.result.output.generic[0].text, null, 2); //pass the value to the global variable
return JSON.stringify(response.result.output.generic[0].text, null, 2);
})
.catch(err => {
console.log("unsuccessful call");
console.log(err);
return error.stringify;
});
}
async function callAssistant(request) {
try {
const sessionId = (await assistant.createSession({ assistantId: assistantId })).result.session_id;
const responseText = (await getMessage(request, sessionId));
return responseText;
} catch (error) {
console.error(error);
}
}

Step 5 Just add one line to call AI based Chatbot — Watson Assistant

And here goes the update to the interaction with Watson — just one line!!! inside Discord:

const Token = process.env.token;client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
const regexPrefix = new RegExp('blu*');client.on('message', msg => {
//regexp for a key word 'blu*'
if (regexPrefix.test(msg.content)) {
//connecting to Watson Assistant here
callAssistant(msg.content.substring(3));

//need to timeout a bit - 2sec? to get the good response
//I am looking for a better way to do it...
setTimeout(function() {
return msg.reply(text)
}, 2000);
//unfortunately I am not getting the right text despite *await*
//msg.reply(text);
}
});
client.login(Token);

And it is ready.

So now build your image:

docker build -t blumareks/node-discordbot:2.0 .

Copy the APIkeys and… and you are ready. And run it:

docker run -e token="<Discord-bot-token-here>" -e assistantId="<AssistantId-here>" -e assistantUrl="<Cloud ⛅️ Service-Assistant-URL-Here>" -e apiKey="<Cloud ⛅️ Service-Assistant-apiKey-Here>"  -d <your-docker-id>/node-discordbot:2.0

Instead of building your image with your own regex environment — you can use my image from Docker Hub (it is working)… Use the given blumareks id there.

docker run -e token="<Discord-bot-token-here>" -e assistantId="<AssistantId-here>" -e assistantUrl="<Cloud ⛅️ Service-Assistant-URL-Here>" -e apiKey="<Cloud ⛅️ Service-Assistant-apiKey-Here>"  -d blumareks/node-discordbot:2.0

then clean environment like we have done it in the previous blog post here:

And watch the webinar here: https://www.crowdcast.io/e/discord-chatbot-with-ibm/2

My webinar with the material from this blog post

Congratulations! 🎉 you have programmed your first AI based chatbot with IBM Watson. Now let’s use it in your chatbot 🤖 — read on.

So now your index.js file looks like this:

const Discord = require('discord.js');
const client = new Discord.Client();
const AssistantV2 = require('ibm-watson/assistant/v2'); //Watson Assistant
const { IamAuthenticator } = require('ibm-watson/auth'); //Watson Auth
const ASSISTANT_ID= process.env.assistantId; //from UI
const ASSISTANT_URL=process.env.assistantUrl; //service-credentials-blog
const ASSISTANT_APIKEY=process.env.apiKey; //service-credentials-blog
const ASST_API_VERSION = '2020-05-04';
const assistantId = ASSISTANT_ID;
let assistant = false;
if (assistantId) {
let url;
let disableSSL = false;
let auth;
try {
auth = new IamAuthenticator({ apikey: ASSISTANT_APIKEY });
url = ASSISTANT_URL;
} catch (e) {
console.log(e.result.stringify);
}
assistant = new AssistantV2({
version: ASST_API_VERSION,
authenticator: auth,
url: url,
disableSslVerification: disableSSL,
});
}
var text = 'no text' // the response text from Watson Assistant
async function getMessage(request, sessionId) {
assistant.message(
{
input: { text: request },
assistantId: ASSISTANT_ID,
sessionId: sessionId
})
.then(response => {
console.log("successful call");
console.log("text0: " + JSON.stringify(response.result, null, 2)); //an entire response from the service
text = JSON.stringify(response.result.output.generic[0].text, null, 2); //pass the value to the global variable
return JSON.stringify(response.result.output.generic[0].text, null, 2);
})
.catch(err => {
console.log("unsuccessful call");
console.log(err);
return error.stringify;
});
}
async function callAssistant(request) {
try {
const sessionId = (await assistant.createSession({ assistantId: assistantId })).result.session_id;
const responseText = (await getMessage(request, sessionId));
return responseText;
} catch (error) {
console.error(error);
}
}

//test
//console.log("text1: " + callAssistant("Ping"));
const Token = process.env.token;client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
const regexPrefix = new RegExp('blu*');client.on('message', msg => {
//regexp for a key word 'blu*'
if (regexPrefix.test(msg.content)) {
//connecting to Watson Assistant here
callAssistant(msg.content.substring(3));
//need to timeout a bit - 2sec? to get the good response
//I am looking for a better way to do it...
setTimeout(function() {
return msg.reply(text)
}, 2000);
//unfortunately I am not getting the right text despite *await*
//msg.reply(text);
}
});
client.login(Token);

Let’s give the Bot a test.

Now in our Discord let’s start to interact with a bot. Doing some ping pongs…

blu ping
....
blu when does new Apex season start?
.....

It worked!

The responses from the bot — we added the question on the new season of Fortnite as well :-)

Summary

What is next? I want to cover in the intermediate session some additional functionalities for using our Discord AI bot containers in the cloud, and AI dialog for VOICE Discord channels and creating some voice interactions with our chatbot using Watson TTS and STT services to get our bot to speak. Is that even possible — join me on Crowdcast for my free webinar (or watch the recording to learn more): https://www.crowdcast.io/e/discord-chatbot-with-ibm

Finally cleaning after webinar

Post work cleaning up of your system

docker container list
docker container stop <container id>
docker container rm <container id>
docker image list
docker image rm <image id>
....everything cleaned (size of the image with our bot in Node.js is 930MB!) - just saying!

When you wrap up, you can clean up your system. Please list the containers

Then stop the one you created for our bot for our bot.

Stopping containers and getting ready to delete containers and images we do not need anymore — they are heavy ~930MB each!

You can now delete the cluster, and then the image associated, if it is needed.

Finally join me for the next webinars. Look for more information on my Twitter here: https://twitter.com/blumareks

--

--

Blumareks
Zero Equals False

I am a technology advocate for autonomous robots, AI, mobile and Internet of Things - with a view from both the enterprise and a robotics startup founder.