Add AI to Discord Bot with Watson
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).
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)!
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
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 Authconst 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 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);
}
}
//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!
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.
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