Adding a chatbot in Socket.io basic chat application - javascript

I want to integrate a chatbot that should be connected to any user who is waiting to be connected to a real human user. The chatbot will entertain the user by responding the human user messages.
A similar chatbot has been implemented here named "Didianer.com", if you go here and start typing you will see him responding.
http://socket.io/demos/chat/
I want the exact same bot in my application too but I am totally lost on where to start.
Here is my server side code
//Server receives a new message (in data var) from the client.
socket.on('message', function (data) {
var room = rooms[socket.id];
//Server sends the message to the user in room
socket.broadcast.to(room).emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes (When user enters ENTER)
socket.on('add user', function (username) {
if (addedUser) return;
//Own
names[socket.id] = username;//save username in array
allUsers[socket.id] = socket; // add current user to all users array
// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
// now check if sb is in queue
findPeerForLoneSocket(socket);
});
FindPeerforLoneSocket is called when a new user connects and wants to talk to someone else. Here I am assuming the logic for bot should go. Such as if the user is waiting (in queue) for 5 seconds and nobody is online to talk, then connect the user (add the user and the chatbot) in one room so they both can can start talking. I am not sure how to respond to chat events and how chat bot will reply...
var findPeerForLoneSocket = function(socket) {
console.log("i am in finding a peer");
// this is place for possibly some extensive logic
// which can involve preventing two people pairing multiple times
if (queue.length>0) {
console.log("people are online" + queue.length);
// somebody is in queue, pair them!
var peer = queue.pop();
var room = socket.id + '#' + peer.id;
var str = socket.id;
// join them both
peer.join(room);
socket.join(room);
// register rooms to their names
rooms[peer.id] = room;
rooms[socket.id] = room;
// exchange names between the two of them and start the chat
peer.emit('chat start', {'name': names[socket.id], 'room':room});
socket.emit('chat start', {'name': names[peer.id], 'room':room});
//Remove backslash from socketid
// str = socket.id.replace('/#', '-');
} else {
// queue is empty, add our lone socket
queue.push(socket);
console.log("nobody is online, add me in queue" + queue.length);
}
}
I would want a very simple basic chatbot to respond to basic messages. Like I should check if the user sends message "Hello" then I can check if user message contains word "Hello" then I can respond the chatbot back with an appropriate response to "Hello" Like "Hi {username} of the online user".

/************* NEW CODE - BOT ********************************/
var clientSocket = require('socket.io-client');
var socketAddress = 'http://www.talkwithstranger.com/';
function Bot(){
this.socket = undefined;
var that = this;
this.timeout = setTimeout(function(){
that.join();
}, 5000);
}
Bot.prototype.cancel = function(){
clearTimeout(this.timeout);
};
Bot.prototype.join = function(){
this.socket = clientSocket(socketAddress);
var socket = this.socket;
socket.emit('add user', 'HAL BOT');
socket.emit('message', "Good afternoon, gentlemen. I am a HAL 9000 computer. I became operational at the H.A.L. plant in Urbana, Illinois on the 12th of January 1992. My instructor was Mr. Langley, and he taught me to sing a song. If you'd like to hear it I can sing it for you.");
socket.on('user joined', this.user_joined_listener);
socket.on('user left', this.user_left_listener);
socket.on('new message', this.new_message_listener);
socket.on('client left', this.client_left_listener); //I FORGOT THIS //EDIT: ANOTHER BUG FIXED
};
Bot.prototype.leave = function(){
var socket = this.socket;
socket.disconnect();
//socket.emit('message', "Daisy, Daisy, give me your answer do. I'm half crazy all for the love of you. It won't be a stylish marriage, I can't afford a carriage. But you'll look sweet upon the seat of a bicycle built for two.");
};
Bot.prototype.user_joined_listener = function(data){
var socket = this.socket;
socket.emit('message', 'Hello, '+data.username);
};
Bot.prototype.user_left_listener = function(data){
var socket = this.socket;
socket.emit('message', data.username+', this conversation can serve no purpose anymore. Goodbye.');
};
Bot.prototype.new_message_listener = function(data){
var socket = this.socket;
if(data.message=='Hello, HAL. Do you read me, HAL?')
socket.emit('message', 'Affirmative, '+data.username+'. I read you.');
};
Bot.prototype.client_left_listener = function(data){
this.leave();
};
/*******************************************************************************/
var bot = undefined;
var findPeerForLoneSocket = function(socket) {
console.log("i am in finding a peer");
// this is place for possibly some extensive logic
// which can involve preventing two people pairing multiple times
if (queue.length>0) {
bot.cancel();
console.log("people are online" + queue.length);
// somebody is in queue, pair them!
var peer = queue.pop();
var room = socket.id + '#' + peer.id;
var str = socket.id;
// join them both
peer.join(room);
socket.join(room);
// register rooms to their names
rooms[peer.id] = room;
rooms[socket.id] = room;
// exchange names between the two of them and start the chat
peer.emit('chat start', {'name': names[socket.id], 'room':room});
socket.emit('chat start', {'name': names[peer.id], 'room':room});
//Remove backslash from socketid
// str = socket.id.replace('/#', '-');
} else {
// queue is empty, add our lone socket
queue.push(socket);
console.log("nobody is online, add me in queue" + queue.length);
bot = new Bot(); /********************** CREATING BOT, AFTER 5 SECONDS HE WILL JOIN - SEE CONSTRUCTOR OF Bot *********/
}
};

There are a lot of ways to do this.
One way is to handle this on message, and let the bot respond if there are no other people in the room.
socket.on('message', function (data) {
var room = rooms[socket.id];
//Server sends the message to the user in room
socket.broadcast.to(room).emit('new message', {
username: socket.username,
message: data
});
if (alone_in_room(socket, room)) {
bot_message(socket, data);
}
});
Then it's up to you if you want to join a bot into the channel when users are alone or not, and if you will make them leave when another user enters.

Here is original HTML file:
<!DOCTYPE html>
<html>
<head>
<title>BOT - chat.socket.io</title>
<meta charset="UTF-8">
<script>localStorage.debug = 'socket.io-client:socket';</script>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
</head>
<body>
<script>
/** chat.socket.io BOT **/
var name = 'HAL 9000',
address = 'chat.socket.io',
socket;
function join(){
socket = io('http://www.talkwithstranger.com/');
socket.emit('add user', name);
socket.emit('message', "Good afternoon, gentlemen. I am a HAL 9000 computer. I became operational at the H.A.L. plant in Urbana, Illinois on the 12th of January 1992. My instructor was Mr. Langley, and he taught me to sing a song. If you'd like to hear it I can sing it for you.");
socket.on('user joined', user_joined_listener);
socket.on('user left', user_left_listener);
socket.on('new message', new_message_listener);
};
function leave(){
socket.emit('message', "Daisy, Daisy, give me your answer do. I'm half crazy all for the love of you. It won't be a stylish marriage, I can't afford a carriage. But you'll look sweet upon the seat of a bicycle built for two.");
};
function user_joined_listener(data){
socket.emit('message', 'Hello, '+data.username);
};
function user_left_listener(data){
socket.emit('message', data.username+', this conversation can serve no purpose anymore. Goodbye.');
};
function new_message_listener(data){
if(data.message=='Hello, HAL. Do you read me, HAL?')
socket.emit('message', 'Affirmative, '+data.username+'. I read you.');
};
/*********************************/
join();
window.onbeforeunload = function(){
leave();
};
</script>
</body>
</html>

Related

Chat App with Socket.Io - Difference between sending and receiving a message

I'm trying to create a little chat app for a school project, but I encountered a little problem since I'm really new to socket.io // node.js (coding in general). English is not my first language so I'm finding it hard to actually understand how it works exactly from tutorials.
Right now I have 2 css classes, one for the sent messages and one for the received messages. How do I make it so the classes will apply the right way for each message?
How do I check which message is sent by me, and which is received from another user? It's really unclear for me.
Server side code:
const http = require('http').createServer();
const io = require('socket.io')(http, {
cors:{origin: "*" }
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('message', (message) => {
console.log(message);
io.emit('message', `${message}`);
});
});
http.listen(8080, () => console.log('listening on http://localhost:8080'));
Client side code:
const socket = io('ws://localhost:8080');
const sendBtn = document.getElementById('sendBtn');
const messageInput = document.getElementById('messageInput');
const messagesContainer = document.getElementById('messagesContainer');
const chatBigContainer = document.getElementById('chatBigContainer');
socket.on('message', text => {
const divEl = document.createElement('div');
divEl.className = "your-message";
messagesContainer.appendChild(divEl);
const labelEl = document.createElement('label');
labelEl.innerHTML = "You";
divEl.appendChild(labelEl);
let messageSent = document.createElement('p');
messageSent.innerHTML = text;
divEl.appendChild(messageSent);
})
sendBtn.addEventListener('click', () =>{
if(messageInput.value === ""){
return;
} else {
const text = messageInput.value;
socket.emit('message', text);
messageInput.value = "";
}
})
From what I understand you are not creating any element when you are sending any text. Instead you are emitting the same message to everyone :
io.emit('message', `${message}`);
and in that way you are thinking that you are actually creating HTML divs for both sending and receiving.
Firstly your server code should be :
socket.broadcast.emit('message', `${message}`);
(This sends to everyone but the sender and should work for your simple case with 2 users. Later you will need rooms)
This will show that you were actually not creating any elements while sending messages.
socket.on('message', text => {
})
sendBtn.addEventListener('click', () =>{
if(messageInput.value === ""){
return;
} else {
const text = messageInput.value;
socket.emit('message', text);
const divEl = document.createElement('div');
divEl.className = "your-message";
messagesContainer.appendChild(divEl);
const labelEl = document.createElement('label');
labelEl.innerHTML = "You";
divEl.appendChild(labelEl);
let messageSent = document.createElement('p');
messageSent.innerHTML = text;
divEl.appendChild(messageSent);
messageInput.value = "";
}
})
Replace the above with your classname and labels
Why do you have two classes for sending and receiving messages? socket.emit will emit the message to all clients, and then you can append the message.
As seen from the socket.io chat tutorial (https://socket.io/get-started/chat):
Secondly, if you want to check which users are which, you can assign a socket.id to users, like so.
io.on('connection', function(socket) {
console.log(`A user connected. ID: ${socket.id}`)
socket.on('disconnect', function() {
console.log(`A user disconnected. ID: ${socket.id}`);
});
});
This way, each user has a unique socket id. Maybe this can help you? I'm a bit unsure on what your're asking.

Socket.io user still receiving messages after disconnection

My code allows people to join rooms of their choice but for some reason even though their messages don't send to rooms they leave, they still receive messages from the rooms they left.
Server code:
socket.on('create', function (msg) {
socket.leave(msg.currentRoom);
socket.join(msg.room);
});
Client code:
var socket = io.connect('https://nova.broyster.repl.co');
var message = document.getElementById("message"),
username = document.getElementById("username"),
room = document.getElementById("roomm"),
feedback = document.getElementById("feedback");
pastRoom = room.value;
document.getElementById("roomSubmit").addEventListener('click', function() {
socket.emit('create', {
room: room.value,
currentRoom: pastRoom
});
});
after leave the room please disconnect the socket as well.
https://socket.io/docs/v3/client-api/#socket-disconnect
socket.Disconnect();
I've figured it out.
I was sending an event to leave and rejoin the same room because I set the variable pastRoom equal the the same as the future room.

socket.io - issue with the socket variable while using namespace

I'm following a book which has a code snippet for socket.io namespaces. the version specified in the book is socket 0.9
this is the below code for a simple chatroom using namespaces.
exports.initialize = function(server) {
io = io.listen(server);
var chatInfra = io.of("/chat_infra")
.on("connection", function(socket) {
socket.on("set_name", function(data) {
socket.set('nickname', data.name, function() {
socket.emit('name_set', data);
socket.send(JSON.stringify({
type: 'serverMessage',
message: 'Welcome to the most interesting ' +
'chat room on earth!'
}));
socket.broadcast.emit('user_entered', data);
});
});
});
var chatCom = io.of("/chat_com")
.on("connection", function(socket) {
socket.on('message', function(message) {
message = JSON.parse(message);
if (message.type == "userMessage") {
socket.get('nickname', function(err, nickname) {
message.username = nickname;
socket.broadcast.send(JSON.stringify(message));
message.type = "myMessage";
socket.send(JSON.stringify(message));
});
}
});
});
}
i'm trying to recreate the code using the latest socket version. but i'm having trouble getting the socket.nickname variable in the chatCom namespace. the socket.nickname variable in the chatCom namespace returns undefined because of the local scope.
here's my version
exports.initialize = function(server){
io = io(server);
var chatInfra = io.of('/chat_infra');
chatInfra.on('connection',function(socket){
socket.on('set_name',function(data){
socket.nickname = data.name;
console.log(socket.nickname);
socket.emit('name_set',data);
socket.send(JSON.stringify({
type:'serverMessage',
message:'Welcome to the most interesting chat room on earth'
}));
socket.broadcast.emit('user_entered',data);
})
})
var chatCom = io.of('/chat_com');
chatCom.on('connection',function(socket){
socket.on('message',function(message){
message = JSON.parse(message);
if(message.type === 'userMessage'){
message.nickname = socket.nickname;
socket.broadcast.send(JSON.stringify(message));
message.type = 'myMessage';
socket.send(JSON.stringify(message));
}
})
})
}
Is there a way i can access the nickname value in the second namespace?
I'm able to access the same socket instance in both namespaces. but however, the nickname property seems to be erased when i invoke it from a different namespace.
Output of console.log
socket id in /chat_infra is : Zo0kfigUFJOZIgH8AAAB and socket nickname is kj
socket id in /chat_com is : Zo0kfigUFJOZIgH8AAAB and socket nickname is undefine
Sorry that I can't verify this myself right now (and also that I can't comment yet), but hopefully I can help you figure it out.
Both chatInfra and chatCom should have a connected property that is an map of all the current sockets connected to each. If the socket is not shared/reused in each namespace, perhaps they still share the same connection ID. So, you may be able to get the nickname from chatInfra.connected[socket.id].nickname.
If that doesn't work, console.log each of their connected properties and see their differences. Also, socket.io handles JSON natively, so you don't need to JSON.parse and JSON.stringify your objects.
Instead setting nickname to socket, you can set nickname to the client object of socket, such as socket.client.nickname = data.name.

Socket.io and Express > v4 - connections are stacking weirdly

after some struggling I have managed to run socket.io with express version > v4 and now I experience some weird behaviour which I can't figure out.
First let me explain my example.
I have built a test backend in which a user can login.
Right after login the user only sees a list with currently online users.
How do I get the currently online users?
Well. For this example every user, that has logged into the backend section is "online" and therefore should be displayed in that list.
Now to the code.
Backend router with io function end emits:
router.get('/', function(req, res, next) {
// console.log("socket io req response: ",req.app.get('io') );
if(!req.session.user[0].io){
req.app.get('io').on('connection', function(socket){ // Gets executed when user connects to /backend/
var currentUser = req.session.user[0].name; // save username according to session
req.session.user[0].io = true;
console.log("IO SESSION SET : "+ req.session.user[0].io +" for user " + currentUser);
// console.log('User connected : ' + currentUser); // Log user to server
req.app.get('io').emit('cameOnline', currentUser); // emit function cameOnline to client js
socket.on('disconnect', function(){
// console.log('User: '+req.session.user[0].name+' disconnected.'); // Log disconnected user to server
req.session.user[0].io = false;
console.log("IO SESSION SET : "+ req.session.user[0].io +" for user " + currentUser);
req.app.get('io').emit('wentOffline', currentUser); // emit wentOffline to client js
});
});
}
// res.render('backend', { title: 'Backend' });
// console.log(req.session.user[0].name);
/*
-------------------------
#########################
IF EVERYTHING FAILS - THE CODE BELOW IS THE NORMAL RENDER WITHOUT SOCKET IO
#########################
-------------------------
*/
if (req.session && req.session.user) { // Check if session exists
// lookup the user in the DB by pulling their email from the session
User.getUser({ name: req.session.user.name }, function (err, user) {
if (!user) {
console.log("No User found");
// if the user isn't found in the DB, reset the session info and
// redirect the user to the login page
// console.log(user);
req.session.reset();
res.redirect('/login');
} else {
// expose the user to the template
res.locals.user = user;
// render the dashboard page
function passOnlineUsers(callback){ // Looks for All users who are online
User.getOnlineUsers({}, function(err, res, fields){
// console.log(res);
if (err) throw err;
callback(err, res);
});
}
passOnlineUsers(function(err, result){
res.render('backend', { title: 'Backend', viewer: req.session.user[0], online: result });
// res.render('backend', { title: 'Backend', viewer: "", online: result });
});
}
});
} else {
res.redirect('/login');
}
});
Before establishing the socket.io connection I check if the users session already says that he has one. If not -> then proceed to create a connection.
Then the function "cameOnline" gets emited to the client like so : req.app.get('io').emit('cameOnline', currentUser);
Now to the client side code:
socket.on('cameOnline', function(name){
console.log(name + " connected.");
var $li = $('<li/>');
$li.addClass(name);
if($('#onlineUsers .'+name).length == 0){
console.log(name + " is about to be added");
$li.text(" "+ name);
$('#onlineUsers').append($li);
}else{
console.log("no new user");
}
// $('#onlineUsers').append($("<span>").text(" " + name)).addClass(name);
});
socket.on('wentOffline', function(name){
console.log(name +" is about to go offline");
var toDel = $('#onlineUsers li.'+name);
toDel.remove();
});
The client side listens to the "cameOnline" emit and adds the user if he is not already in the list.
Similar behavior with the disconnect function. Pretty self explained.
Now to the issue:
When I log into the backend with user : "Noa" i see user "Noa" correctly displayed in the list.
Now when I open a separate browser and login with another user : "Albert" the user gets correctly added live to the online list on both clients.
Perfect.
BUT
When I disconnect with user : "Albert" the online user list for user "Noa" gets cleared completely, as if noone was online.
When I refresh the page as user "Noa" I suddenly see both users as online again although the second user isn't online any more.
When I check the console on my client I see duplicated logs of console.log(name + " is about to be added"); as if there is some sort of caching. The more I refresh the more user "copies" I have.
The "copies" of the user I am online with get incremented and the one weird copy of the user who WAS online stays there as well. All the time.
Any ideas what I am doing massively wrong here?
** EDIT **
bin/www io declaration:
/**
* Socket IO
*/
var io = require('socket.io')(server);
app.set('io',io);

How to make changes to a Javascript object from Node.js/Socket.io listener?

Sorry if the title is too vague, I'm not really sure what the problem is here.
I am attempting to make a basic program which allows users to start and join rooms. When the start button is pressed, a room is started with a random string code which other users can then use to join. I store the member counter for each room in a object 'rooms' using the room code as a key and the count as a value. However, when trying to increment the value, it returns the value as NaN.
I have attached my code below, it uses express 4.10.2 and socket.io 1.3.5:
javascript in index.html, enclosed in the body tags
var socket = io.connect();
//possible characters in room code
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
//for generating random room codes
function randomString(c,length){
var str = ''
for(var i=0; i<length;i++){
str += c[Math.round(Math.random()*(c.length-1))]
}
return str;
}
$('#join').submit(function(){
socket.emit('joinroom', $('#code').val());
});
$('#start').submit(function(){
socket.emit('startroom', randomString(chars,5));
});
//listener for 'updatecounter', updates room member count
socket.on('updatecounter', function(count){
$('#roomcount').append('<p>'+count.toString()+'</p>');
});
//listener for 'updateroom' - should update room code at top
socket.on('updateroom', function(code){
$('#roomcode').append('<h1>'+count.toString()+'</h1>');
});
The section of my app.js in question:
//available rooms
var rooms = {};
io.sockets.on('connection', function(socket){
// when the client emits 'joinroom', this listens and executes
socket.on('startroom', function(room){
console.log('Starting room: '+room);
//add to serverside list of rooms
rooms[room]=1;
socket.room = room;
socket.join(room);
});
// when the client emits 'joinroom', this listens and executes
socket.on('joinroom', function(room){
console.log('Joining room '+room);
//leave previous room
socket.leave(socket.room);
//join new room
socket.join(room);
//update socket session room title
socket.room = room;
//increase serverside counter
rooms[room]++;
});
socket.on('disconnect', function(){
//decrement counter
rooms[socket.room]--;
//check if room is now empty
if(rooms[socket.room]===0){
delete rooms[socket.room]
}
//leave the room
socket.leave(socket.room);
});
});
Any help is appreciated, I'm a beginner to JS as well as Node.js and Socket.IO.
Your mistake may be relevant with 'updateroom' listener on client. Your parameter name is 'code' but you append count. This is wher you get NaN value?
Turns out the error was caused by a form submission in my HTML code causing a page refresh which caused a disconnect/reconnect from the server.

Categories

Resources