How to display message when user disconnects from chat room in socket.io - node.js

I am working on a project in which there is a chat room. The messaging and the joining message is working fine but I don't know how to display a message when someone disconnects. I don't have much knowledge about socket.io as you will see in the code below.
(Edit)
Server Code(Express/Node)
io.on('connection', socket => {
socket.on('message', ({ name, message }) => {
io.emit('message', { name, message })
})
socket.on('join' , (name)=>{
console.log(name);
socket.broadcast.emit("join" , name.usnm.toUpperCase() + " just joined")
})
socket.on('pre_disconnect', (name) => {
console.log(name);
socket.broadcast.emit("pre_disconnect" , name.usnm.toUpperCase() + " just left")
})
})
Client Code(React.js)
const socketRef = useRef()
useEffect(
() => {
socketRef.current = io.connect("http://localhost:4000")
socketRef.current.on("message", ({ name, message }) => {
setChat([ ...chat, { name, message } ])
})
socketRef.current.on("join", (usnm) => {
setChat([ ...chat, {name:usnm} ])
})
socketRef.current.on("pre_disconnect", (usnm) => {
setChat([ ...chat, {name:usnm} ])
})
},
[ chat ]
)
useEffect(
() => {
let usnm = sessionStorage.getItem("User");
socketRef.current.emit("join" ,{usnm});
return () => {
socketRef.current.emit("pre_disconnect" ,{usnm});
socketRef.current.disconnect()
}
},
[ ]
)
In the code above the pre_disconnect doesn't do anything. Code works the same with or without it.

In the server app, define a pre_disconnect event which will be similar to the join event.
io.on('connection', socket => {
socket.on('message', ({ name, message }) => {
io.emit('message', { name, message })
})
socket.on('join', (name) => {
console.log(name);
socket.broadcast.emit("join" , name.usnm.toUpperCase() + " just joined")
})
// Fire this before disconnecting the socket
socket.on('pre_disconnect', (name) => {
console.log(name);
socket.broadcast.emit("pre_disconnect" , name.usnm.toUpperCase() + " just left")
})
})
In the first useEffect hook call, a new socket connection is made and closed everytime there is a chat state update. This means that number of messages in the current session will be generated by amount of socket connects/disconnects. From React Docs
The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes. However, this may be overkill in some cases, like the subscription example from the previous section. We don’t need to create a new subscription on every update, only if the source prop has changed. To implement this, pass a second argument to useEffect that is the array of values that the effect depends on.
Similarly, unless you explicitly need to create and close a new connection on every chat update, avoid it for performance reasons.
In the react app, fire pre_disconnect event with usnm, before calling socketRef.current.disconnect() and add the appropriate handler similar to join.
const socketRef = useRef()
useEffect(() => {
// socketRef.current = io.connect("http://localhost:4000")
if (socketRef.current) {
// Add socketRef as dependency and check if socketRef.current exists then continue with listeners
socketRef.current.on("message", ({ name, message }) => {
setChat([...chat, { name, message }]);
});
socketRef.current.on("join", (usnm) => {
setChat([...chat, { name: usnm }]);
});
// handler for disconnect message
socketRef.current.on("pre_disconnect", (usnm) => {
setChat([...chat, { name: usnm }]);
});
}
// return () => socketRef.current.disconnect()
}, [chat, socketRef]);
useEffect(
() => {
// Here, creates a socket connection only when component renders the first time. Similar to componentDidMount() in class components
socketRef.current = io.connect("http://localhost:4000")
let usnm = sessionStorage.getItem("User");
socketRef.current.emit("join" ,{usnm});
// Disconnect the socket when component is to be unmounted from DOM. Similar to componentWillUnmount() in class components
return () => {
socketRef.current.emit("pre_disconnect" ,{usnm});
socketRef.current.disconnect()
}
},
[ ]
)
Edit:
You were getting that error because socketRef was not initialized. I have added socketRef as a dependency and wrapped the code inside an if condition. Please check the changes.

Related

Message.awaitReactions in a DM to user not collecting Discord.js v13

I am currently writing a bot that acts like a timeclock for a friend of mine. Currently I am trying to get the bot to message someone if they have been clocked in for more than 2 hours and have not "refreshed" their clock in time. More of just a reminder of, "hey did you forget to clock out".
Currently the bot sends a correct DM with the correct reaction to the user identified. However, it never detects when a user reacts to that message. My onMessageReactionAdd event fires, but i have blocked that if it is in a DM channel because I would like to perform a task if the user doesnt respond in time(clock them out). I would only like to collect the refresh attached to this method so I can keep methods separate.
I am suspecting i am awaiting on the wrong message, or not understanding exactly what is going on after reading through other answers.
const refresh = require('../database/checkRefresh');
const refrechClockin = require('../commands/refreshClockin');
module.exports = async (client) =>{
let users = [];
const res = refresh.execute()
.then(res => {
console.log(`refresh query executed`);
if(res.rows.length > 0){
res.rows.forEach(row => {
users.push(row.user_id);
})
}
})
.then( () => {
console.log(`users from refresh query: ${JSON.stringify(users)}`);
users.forEach(user => {
console.log(`user_id: ${user}`);
client.users.fetch(user)
.then((user) => {
var message = user.send('Did you forget to clock out? If you are still working click the little bell, after 5 minutes you will be clocked out.')
.then((message, user) => {
message.react('πŸ””');
const filter = (reaction, user) => reaction.emoji.name === 'πŸ””';
message.awaitReactions(filter, {max:2, time: 5000})
.then(collected => {
console.log(`inside of new then method`);
console.log(`collected: ${JSON.stringify(collected)}`);
})
.catch(console.error)
});
});
});
});
}
After looking through other answers, I double checked my Intents and they seem to be correct.
const client = new Client({
intents: [Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGES,
Intents.FLAGS.DIRECT_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGE_TYPING,
],
partials: ['MESSAGE', 'CHANNEL', 'REACTION']
});
UPDATE
I opted to change to a reactionCollector on the message. At first I was still having the same issue. After many console.logs, both in the filter and in the collector events, I discovered that my filter would not properly match the emoji that was being used. I even printed the reaction that was used(the bell) to console and copied from there to insert in the comparison and still will not match.
I would like to ultimately only allow them to respond using the bell, but for now I am ok with it. I have attached a default reaction from the bot so hopefully they see that and just click it. putting my updated code below. If anyone has any idea why I could not get the compare to work it would help. All of my other emoji comparisons are working correctly in my commands.
const refresh = require('../database/checkRefresh');
const refrechClockin = require('../commands/refreshClockin');
module.exports = async (client) =>{
let users = [];
const res = refresh.execute()
.then(async res => {
console.log(`refresh query executed`);
if(res.rows.length > 0){
res.rows.forEach(row => {
users.push(row.user_id);
})
}
})
.then( async () => {
console.log(`users from refresh query: ${JSON.stringify(users)}`);
users.forEach(user => {
console.log(`user_id: ${user}`);
client.users.fetch(user)
.then(async (user) => {
var message = await user.send('Did you forget to clock out? If you are still working click the little bell, after 5 minutes you will be clocked out.');
message.react('πŸ””');
const filter = (reaction, user) => {
console.log(`reaction: ${JSON.stringify(reaction)}`);
console.log(`Reaction Emoji: ${reaction.emoji.name}`);
return (user.bot === false);
};
const collector = message.createReactionCollector({ filter, max: 1, time: 45_000 });
collector.on('collect', r => console.log(`Collected ${r.emoji.name}`));
collector.on('end', collected => console.log(`Collection ended with ${collected.size} Collected items`));
});
});
});
}

chat_member not getting invocked

I am using TelegrafJS and nodejs for developing a telegram bot and I want to use "chat_member" because it returns the invite link used by the user to join the telegram group. but the problem is it is not getting invoked. when new members are joined. my primary goal is to get the invite link used by the member to join the telegram group.
const { Router, Markup, Telegraf } = require('telegraf');
//require .ENV
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname + './../.env') });
//creating Bot
const bot = new Telegraf(process.env.TELEGRAM_TOKEN);
bot.on("chat_member", async (ctx) => {
console.log(ctx);
})
bot.on("group_chat_created", async (ctx) => {
console.log("group_chat_created")
})
//launching bot
bot.launch()
.then(() => {
console.log("Bot Running");
})
.catch((err) => {
console.log(`Error Running Bot: ${err}`);
})
All other are working like "group_chat_created","new_chat_members","left_chat_member".
The documentation says I need to add it to allowed_updates but how to add it.
I got the solution the thing I needed to add was the launch options in the bot.launch method
var options2 =
{
allowedUpdates: ['chat_member']
}
//launching bot
bot.launch(options2)
.then(() => {
console.log("Bot Running");
})
.catch((err) => {
console.log(`Error Running Bot: ${err}`);
})
you will not get update about this method directly you need to add it to allowed_updates

peer.on('calll') is never being called

Peer js is not working
I am just creating peerjs video streaming application in NodeJs and ReactJs
The below code is working fine, i am able to create new peer and open event is also working.
const peer = new Peer(undefined,{
host:"/",
port:"5001"
});
peer.on('open',id=>{
socket.emit('join-room', roomId,id);
})
On server side whenever 'join-room' event is emitted, server will emit another event 'user-disconnected' and passes userId (peerjs) to client.
// server.js
socket.on('join-room',(roomId,userId)=>{
console.log(`user ${userId} joined ${roomId}`);
socket.join(roomId);
socket.to(roomId).emit('user-connected',userId);
socket.on('disconnect', () => {
socket.to(roomId).emit('user-disconnected', userId)
})
})
Whenever 'user-connected' is triggered on client side I'm calling connectToNewUser and up to this is working fine.
socket.on('user-connected',userId=>{
console.log("New user connected...")
connectToNewUser(userId, stream)
});
Error
This is being logged on console console.log('connectToNewUser',1222.....) there is no error.
But, this call.on('stream') is never being called
connectToNewUser(userId, stream) {
console.log('connectToNewUser',userId)
const call = peer.call(userId, stream);
const video = getVideo();
call.on('stream', userVideoStream => {
// never called
console.log('connectToNewUser','on','stream')
addVideoStream(video, userVideoStream)
});
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
The reason of call.on('stream') is never being called is peer.on('call') is never being called.
peer.on('call', call => {
// never called
console.log('This peer is being called...');
call.answer(stream)
const video = getVideo();
call.on('stream', userVideoStream => {
console.log('This peer is being called...on-stream...');
addVideoStream(video, userVideoStream)
})
});
Github repo
I also had the same problem, after going through more than 10+ solutions on StackOverflow, here is the thing I suggest.
connectToNewUser() gets triggered before the user has finished the navigator promise.
Try to modify to this:
socket.on('user-connected',userId=>{
console.log("New user connected...")
//connectToNewUser(userId, stream)
setTimeout(connectToNewUser,1000,userId,stream)
});
It worked for me.
The only problem with your code is that you are emitting user-connected event from server before initializing peer.on("call") event on client which eventually leads this event to be missed by new client.
The best solution for this is to emit a ready event after initializing peer.on("call",...) event.
peer.on('call', call => {
console.log('This peer is being called...');
call.answer(stream)
const video = getVideo();
call.on('stream', userVideoStream => {
console.log('This peer is being called...on-stream...');
addVideoStream(video, userVideoStream)
})})
socket.emit("ready")`
And then on server side call the "user-connected" broadcast event inside the listener for "ready" event:
socket.on('join-room',(roomId,userId)=>{
console.log(`user ${userId} joined ${roomId}`);
socket.join(roomId);
socket.on('ready',()=>{
socket.broadcast.to(roomId).emit('user-connected',userId);
})
socket.on('disconnect', () => {
socket.to(roomId).emit('user-disconnected', userId)
})})
This will broadcast user-connected event after the client is ready to recieve peer.on("call". ..
.) event
I think your code is something like this
const peer = new Peer(undefined,{host:"/",port:"5001"})
peer.on('open',id=>{
socket.emit('join-room', roomId,id)
})
navigator.mediaDevices.getUserMedia({
video : true,
audio : true
}).then( stream => {
socket.on('user-connected',userId=>{
// call the new user and send our stream
});
peer.on('call',call=>{
// pick up the call and send our stream
})
})
whenever a new client joins a new peer Object is created after which the socket emits join-room and the server broadcasts the user-connected to all the other clients
so now when the new user joins the user-connected function of all the other client is called in which all the other clients call the new client .
one key thing to note is that the navigator.mediaDevices.getUserMedia(video : true,audio : true}) takes some time
So when a new client joins all the other clients instantly call the new client but the function to pick up the call i.e. peer.on('call',call=>{}) may not be defined yet on the new client so that's why the new clients does not response because it is still setting up its video stream
to fix this you must join the room after setting up the video stream
so something like this
navigator.mediaDevices.getUserMedia({
video : true,
audio : true
}).then( stream => {
const peer = new Peer(undefined,{host:"/",port:"5001"})
peer.on('open',id=>{
socket.emit('join-room', roomId,id)
})
socket.on('user-connected',userId=>{
// call the new user and send our stream
});
peer.on('call',call=>{
// pick up the call and send our stream
})
})
Here the Peer object is getting created before Camera and/or Mic are ready to use.
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
//peer.on('call')
})
peer.on('call') will be implemented by browser only once getUserMedia() will finish execution.
Thing is that you are receiving a call event but there is no handler is ready to handle that event yet. Best solution would be to join the room once the media is ready to use.
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
const myPeer = new Peer(undefined, {
host: '/',
port: '3001'
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
myPeer.on('call', call => { })
})
Your code is similar to a project that I'm working on. I had trouble with where I was initiating the peer instance. I had to move it up one level in my component tree, because something I was doing was causing a re-render and the peer instance was being re-created, with a new id.
Also check where you're creating your socket. Make sure that your user-connected event is being registered in a useEffect().
useEffect(() => {
videoSocket = initiateVideoSocket(roomId, username, peer.id);
// When a viewer connects, this event is emitted and the streamer will connect to the viewer.
videoSocket.on("user-connected", (roomId, viewerPeerId) => {
connectToNewViewer(viewerPeerId, videoStream);
});
}, [videoStream]);
const viewerVideoSocket = initiateVideoSocket(
room,
props.username,
viewerPeerId
);
const viewerChatSocket = initiateChatSocket(room, props.username);
let streamerVideoDiv;
useEffect(() => {
viewerVideoSocket.emit(
"user-connected",
room,
props.username,
viewerPeerId
);
viewerPeer.on("call", (call) => {
call.answer();
call.on("stream", (stream) => {
streamerVideoDiv = document.getElementById("streamer-video");
const content = document.createElement("video");
content.srcObject = stream;
content.addEventListener("loadedmetadata", () => {
content.play();
});
streamerVideoDiv.append(content);
});
});
return () => {
disconnectSocket();
};
}, []);
You must declare peer in the stream call back function as below:
myVideo.muted = true;
navigator.mediaDevices.getUserMedia ({
video: true,
audio: true
}).then(
stream => {
const myPeer = new Peer (undefined, {
host: '/',
port: '4001'
});
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
console.log("!#1")
})
addVideoStream(myVideo,stream)
socket.on('user-connected', userId => {
console.log("user is connected with id: ", userId);
// connectToNewUser(userId, stream)
const call = myPeer.call(userId, stream);
console.log('call');
const video = document.createElement('video');
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream);
})
call.on('close', () => {
video.remove()
})
})
myPeer.on('call', call => {
call.answer(stream)
console.log("CALL");
})
})
The solution is simple...
Set debug level to 3 when initializing the peer and then you will see the error
peer.call is not running since you may not be passing the parameters , id or stream...

observer create multiple message with socket.io in angular 2

i am using of this code for receive MSG to socket in angular 2 but i have used in global application but it's created multiple time msgs.. on routeing another pages i created one chat-box component which opened global after open like Facebook chat-box.
`get-messages() {
let observable = new Observable(observer => {
this.socket = io(this.url);
this.socket.on('message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
})
return observable;
} `
I am not sure if this will help or not. In angular 1.x I use
$scope.$on('$destroy', function(event) {
// Code to un observe the socket here...
});
I am sure there is an equivalent in angular 2

Listen to reconnect events in MongoDB driver

I would like to add event listeners to a MongoDB connection to run something when the connection drops, each reconnection attempt and at a successful reconnection attempt.
I read all the official docs and the API, but I can't find a solution.
Currently, I have this, but only the timeout event works.
// If we didn't already initialize a 'MongoClient', initialize one and save it.
if(!this.client) this.client = new MongoClient();
this.connection = await this.client.connect(connectionString, this.settings);
this.client.server.on('connect', event => {
console.log(event);
});
this.client.server.on('error', event => {
console.log(event);
});
this.client.server.on('reconnect', event => {
console.log(event);
});
this.client.server.on('connections', event => {
console.log(event);
});
this.client.server.on('timeout', event => {
console.log(event);
});
this.client.server.on('all', event => {
console.log(event);
});
I tried the events listed here, and they work, but there is no "reconnect" event:
http://mongodb.github.io/node-mongodb-native/2.2/reference/management/sdam-monitoring/
Sure you can. Basically though you need to tap into the EventEmitter at a lower level than basically off the MongoClient itself.
You can clearly see that such things exist since they are visible in "logging", which can be turned on in the driver via the setting:
{ "loggerLevel": "info" }
From then it's really just a matter of tapping into the actual source emitter. I've done these in the following listing, as well as including a little trick for getting the enumerated events from a given emitted, which was admittedly used by myself in tracking this down:
const MongoClient = require('mongodb').MongoClient;
function patchEmitter(emitter) {
var oldEmit = emitter.emit;
emitter.emit = function() {
var emitArgs = arguments;
console.log(emitArgs);
oldEmit.apply(emitter, arguments);
}
}
(async function() {
let db;
try {
const client = new MongoClient();
client.on('serverOpening', () => console.log('connected') );
db = await client.connect('mongodb://localhost/test', {
//loggerLevel: 'info'
});
//patchEmitter(db.s.topology);
db.s.topology.on('close', () => console.log('Connection closed') );
db.s.topology.on('reconnect', () => console.log('Reconnected') );
} catch(e) {
console.error(e)
}
})()
So those two listeners defined:
db.s.topology.on('close', () => console.log('Connection closed') );
db.s.topology.on('reconnect', () => console.log('Reconnected') );
Are going to fire when the connection drops, and when an reconnect is achieved. There are also other things like reconnect attempts which are also in the event emitter just like you would see with the loggerLevel setting turned on.

Resources