15 minutes to create a personal assistant that can search on wikipedia (and tell some horrible jokes)
Leonardo Bonetti
Posted on March 2, 2021
Introduction
On a boring day at work, a colleague and I were talking to Alexa, when a bet arose due to the joke, creating a virtual assistant in 15 minutes.
Alexa has a multitude of functions, so to make the challenge possible ours should just take questions about common topics.
Then we choose the following rules:
- The assistant must interact with you by speaking your name
- Responses must be in audio
- Searches must be carried out using wikipedia
- Tell some jokes
Let's code
Create project
I can't code without typescript anymore, so...
npm init -y && npx ts-init
Check that your tsconfig.json
file is as follows
{
"compilerOptions": {
"lib": [
"es6",
"DOM"
],
"alwaysStrict": true,
"strictNullChecks": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"files": [
"src/index.ts"
]
}
Lets get our packages
npm i --save say
Say is an amazing TTS (Text-to-Speech) library, it will help us with a voice to our robot.npm i --save wikipedia
Wikipedia is a simple lib that abstract the wiki endpoints and make our lives easier
Add some Jokes
Create a file named src/jokes.json
, copie the content of this gist to your file
Finally we will code
Inside your src/index.ts
import all libraries
import say from 'say';
import wikipedia from "wikipedia";
import jokes from './jokes.json';
import readline from "readline";
...
Our personal assistant will receive the commands by text, like the old ways
So, w'll need a interface to interact with our user by terminal
...
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
The main goal here is create an assistant that can make searchs on wikipedia, so, lets create this func
...
const wikisearch = async (topic: string) => {
const search = await wikipedia.search(topic);
const page = await wikipedia.page(search.results[0].title);
const summary = await page.summary();
return summary.extract;
}
Now let's give our assistant a voice
...
const speak = (text: string) => {
return new Promise((resolve, reject) => {
say.speak(text, 'Samantha', 0.9, (err) => {
if(err) reject(err);
resolve(true);
});
})
}
As my assistant is a woman, I decided to give her the voice of Samantha, and to make it more audible, I leave the playback speed at 0.9, but feel free to change any of these properties, you can check the full list of voices available here
Ok, lets interact.
By the first rule of the challenge, the bot need to know our name, so
...
let name = "";
rl.question("What is your name ? ", async function(received: string) {
name = received;
await speak(`Hello ${received}, my name is Clotilde`);
ask();
});
Now, lets code the final function
...
const ask = () => {
rl.question("ask me something: ", async function(rQuery: string) {
if(rQuery == 'stop') {
say.stop();
return rl.close();
}
if(rQuery.toLocaleLowerCase().split(' ').findIndex(item => item == 'joke') > -1) {
const jokeIndex = Math.floor(Math.random() * jokes.length);
const joke = jokes[jokeIndex];
await speak(joke.text);
} else {
const searchTopic = await wikisearch(rQuery);
await speak(`According to wikipedia, ${searchTopic}`);
}
ask();
});
}
The idea here is very simple, if the user asks something that contains the word joke, we tell a joke, if not, we search on wikipedia.
And, for finally, when we shutdown our assistant:
...
rl.on("close", async function() {
console.log("\nBYE BYE !!!");
await speak("Bye Bye");
process.exit(0);
});
She will say "Bye Bye"
Your code should now look like this:
import say from 'say';
import wikipedia from "wikipedia";
import jokes from './jokes.json';
import readline from "readline";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const wikisearch = async (topic: string) => {
const search = await wikipedia.search(topic);
const page = await wikipedia.page(search.results[0].title);
const summary = await page.summary();
return summary.extract;
}
const speak = (text: string) => {
return new Promise((resolve, reject) => {
say.speak(text, 'Samantha', 0.9, (err) => {
if(err) reject(err);
resolve(true);
});
})
}
let name = "";
rl.question("What is your name ? ", async function(received: string) {
name = received;
await speak(`Hello ${received}, my name is Clotilde`);
ask();
});
const ask = () => {
rl.question("ask me something: ", async function(rQuery: string) {
if(rQuery == 'stop') {
say.stop();
return rl.close();
}
if(rQuery.toLocaleLowerCase().split(' ').findIndex(item => item == 'joke') > -1) {
const jokeIndex = Math.floor(Math.random() * jokes.length);
const joke = jokes[jokeIndex];
await speak(joke.text);
} else {
const searchTopic = await wikisearch(rQuery);
await speak(`According to wikipedia, ${searchTopic}`);
}
ask();
});
}
rl.on("close", async function() {
console.log("\nBYE BYE !!!");
await speak("Bye Bye");
process.exit(0);
});
The complete repo of this project can be found on here
Thank you very much, let us know in the comments section how long it took you to create this bot
Posted on March 2, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.