I'm getting a Pusher subscribe/un-subscribe problem with presence groups on a fairly simple chat application. There should be two subscribed channels at a given time. Users switch between channels when navigating between backbone routes, so there are no hard page reloads.
Pusher appears to function most of the time, but I get intermittent errors for channel subscriptions.
I wrote two join channel methods that unsubscribe if one has been joined by a previous route. I'm worried that there is something async happening within Pusher that is breaking things.
My pusher related code for a single channel:
window.pusher = new Pusher('<%= Pusher.key %>', {
authEndpoint: 'api/pusher/auth'
});
Route:
this.groupFeed = this._pusherSubscribeGroup(group_id);
this.groupFeed.bind('new_conversation', function(data) {
var newConv = new App.Models.Conversation(data);
this.group.conversations().add(newConv);
}.bind(this));
Unsubscribe helper:
_pusherSubscribeGroup: function (group_id) {
if (this._groupChannelName) {
window.pusher.unsubscribe(this._groupChannelName);
}
this._groupChannelName = 'presence-group-' + group_id;
return window.pusher.subscribe(this._groupChannelName);
}
Console error:
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Existing subscription to channel presence-group-1"}}}
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Existing subscription to channel presence-group-1"}}}
The message is telling you that - as far as the Pusher service is concerned - this particular client identified by a socket_id is already subscribed to the presence-group-1 channel. From the point of view of the client this error normally isn't something you actually need to worry about.
However, if the user is quickly subscribing/unsubscribing by navigating routes it would be worth getting some more information to be completely sure. From the supplied code and description, it's not possible to determine if the problem is within the Pusher service or the client application.
Providing the output of the Pusher JavaScript library logging will provide more detail that could help determine the cause.
Related
I'm making a clone of discord and right now I'm trying to implement the online/offline functionality of the server users. I'm trying to implement it like this:
When a user joins a server, I emit a userCameOnline event with the username of the user:
state.socket.emit('userCameOnline', state.username)
Then on the back-end, I listen for that event and once I receive it, I set the socket's username to the emitted username, then push that username to an array of online users and finally, I emit back an event called onlineUsers:
socket.on('userCameOnline', (username) => {
socket.username = username
onlineUsers.push(socket.username)
console.log(onlineUsers)
socket.emit('onlineUsers', onlineUsers)
})
And this is the onlineUsers listener on the front-end that sets onlineUsers property to the server:
state.socket.on('onlineUsers', (onlineUsers) => {
console.log(onlineUsers)
server.onlineUsers = onlineUsers
})
Now here's the problem. The onlineUsers listener works when I load the page the first time, however, when I open a second browser and join the chat with another account, the client emits these events again with the new user:
state.socket.emit('userCameOnline', state.username)
I know this is working fine as I console.log(onlineUsers) on the back-end and see that the array indeed has 2 users once the second client has joined. This means that this works:
socket.on('userCameOnline', (username) => {
socket.username = username
onlineUsers.push(socket.username)
console.log(onlineUsers)
socket.emit('onlineUsers', onlineUsers)
})
Unfortunately, the onlineUsers listener on the first client doesn't trigger which means that the onlineUsers property of the server doesn't get updated with the newly joined user. I've been banging my head for some time now and I can't figure out why is this happening. I've been following the trail and console.logging everything but I still can't figure it out.
This is why I am wondering why does this:
state.socket.on('onlineUsers', (onlineUsers) => {
console.log(onlineUsers)
server.onlineUsers = onlineUsers
})
Triggers once I load the page and then doesn't trigger anymore even though I'm emitting the event from the back-end every time a new user joins the server?
So, if i understand your problem then instead of emitting in the Back-end:
socket.emit('onlineUsers', onlineUsers)
You should emit :
io.emit('onlineUsers', onlineUsers)
Because by emitting with socket.emit() you will end up emitting only to the same client that emitted to the server in the first place.
As with io.emit() you will emit to all of your clients.
Assuming that you are using something like :
const io = require('socket.io')(server, {(your_args)})
If you want to check for further information on how to use the emit function
check out this page https://socket.io/docs/emit-cheatsheet/ from their website.
Finally i would highly recommend you to check out their API documentation and other blogs about it
I have developed a fully functional Telegram Bot with Google App Script that uses Webhooks to read and write into a Google Spreadsheet.
Although the bot is stable, sometimes it crashes due to specific user interaction like pressing buttons too fast or making the bot send too many messages in a short amount of time.
The Error Handling section of the Telegram API mentions that errors are returned in rpc_error constructors, but there is no reference to any method that can be used to listen for any error that may occur.
I want to handle these errors but the only way I am aware is to listen for errors on each API request.
Here is an example of how I am handling an error right now:
function editKeyboardMessage(buttonArray) {
var id = PropertiesService.getUserProperties().getProperty("inline_form_chat_id");
var message_id = PropertiesService.getUserProperties().getProperty("inline_form_message_id");
var keyboardMarkup = {"inline_keyboard": buttonArray};
var data = {
method: "post",
payload: {
method: "editMessageReplyMarkup",
chat_id: String(id),
message_id: Number(message_id),
parse_mode: "HTML",
reply_markup: JSON.stringify(keyboardMarkup)
}
};
var response = UrlFetchApp.fetch(url + "/", data)
var responseCode = response.getResponseCode()
handleErrors(responseCode)
}
However, it seems unpractical to do this on all requests types and it doesn't allow complete error handling (like the ones that happen independently of requests, such as message flooding).
The expected outcome would be to properly handle all incoming errors in just one place.
Is there a way to listen for all bot errors with something similar to the doPost(e) function that is used to listen for updates?
Since we are currently talking on Telegram - it might be helpful for others.
The conversation started at https://t.me/BotTalk/105298
tl;dr you need to do error handling on your end. If you don't use a library, try checking each response from Telegram after sending out a request for errors. If there are errors, handle them accordingly.
I am trying to create an app that uses websockets, but am running into the dreaded "multiple connection" issue where everytime the page is reloaded rather than closing and reopening a new websocket, another connection is simply added to the list. My question is if anyone knows the best/proper way to close a Socket.io websocket from Angular 2/4+.
Here's what I have as far as code:
service.ts
getUserSocket(userID: string): Observable<any> {
return new Observable(_Observer => {
// Setup the socket namespace subscription
this.UserSocket = io(NTC_API_URL + `/user/${userID}`, { secure: true });
this.UserSocket.on('message', _Message => {
console.log(_Message);
})
})
}
closeUserSocket() {
this.UserSocket.disconnect();
this.UserSocket.close();
}
component.ts
ngOninit() {
// Setup the User Socket
this.UserSocket = this._UsersService.getUserSocket(this.currentUser.userID)
.subscribe(_UserSocketMessage => {
console.log(_UserSocketMessage);
})
}
ngOnDestroy() {
this.UserSocket.unsubscribe();
this._UsersService.closeUserSocket();
}
This doesn't seem to be working as I can still watch the connections pile up just by logging in an out of the application which I have confirmed calls the component to be destroyed.
Another option I tried was using the once listener instead of the on listener, which worked, but I am unsure about the side effects and have read several places that it's not necessarily a fix for not closing the connection, which I can understand.
After quite a bit of tinkering and research it occurred to me that web sockets are made to be resilient. In other words, since they were engineered to reconnect if possible, focusing on disconnect and close was the wrong approach. It was far easier to enforce policies with regard to the Creation of web socket connections.
For starters, I had to refactor a few things on the server side to make sure particular namespaces were only initialized once. This wasn't achieved so much by altering initialization code as it was by making sure that web socket initialization only occurred in places that either only happened on startup, or during processes that are only highly likely to happen once such as a particular user being created.
On the angular side it was also pretty straightforward in that all I had to do was assign the socket to a property within the service, then check if it existed before making the socket connection. Hence only creating the connection if no connection existed previously.
service.ts
// Setup the socket namespace subscription
if (!this.UserSocket) {
// Initialize user permissions socket
this.UserSocket = io(NTC_API_URL + `/user/${this.LocalUser.userID}`, { secure: true });
}
How do I retrieve the rooms a socket is a member of?
I'm using socket.io version 1.4
I tried with this.socket.adapter.rooms but I received this error in the chrome console: Cannot read property 'rooms' of undefined
In my client code I have this method:
send(msg) {
if(msg != ''){
var clientInfo = [];
clientInfo.push(msg);
clientInfo.push(socket.id);
clientInfo.push(this.socket.adapter.rooms);
socket.emit('message', clientInfo);
}
}
On my server side:
socket.on('message', function(clientInfo){
var clientmessage = clientInfo[0];
var clientid = clientInfo[1];
var clientroom = clientInfo[2];
io.to(clientroom).emit('messageSent', clientmessage);
});
Server side, you can get a list of rooms a socket is in with:
socket.rooms
Client side, a socket does not know what rooms it is in. The whole concept of rooms is a server-side concept and all the data structures are maintained there. If a client wanted to know what rooms it was in, it would either have to keep track of what rooms it requested to be a member of or it would have to ask the server what rooms it is in.
There is one oddity about the server-side socket.rooms structure. It is apparently not updated real-time. If you do socket.join("someRoom") and then immediately look at socket.rooms, you will not see the someRoom name listed. But, if you look on process.nextTick() or on setTimeout(), you will see it in socket.rooms. I haven't delved into the socket.io source code to figure out why that is the way it is, but apparently something is only being updated asynchronously.
Object.keys(socket.rooms).forEach(function(room, idx) {
if(idx!=0){
console.log(idx,"-->",room)
}
});
By the above code you can identify all the rooms the socket have joined.
You can skip the first room which is in index 0 as it is the default room a socket will connect on connection.
Tested this with SocketIO 1.7 and works well.
I'm building a web application with Node (Express) and Socket.IO that has chat functionality. Because opening a new tab on a page establishes a new socket connection, I need to group all instances of a single user into their own room based on the Express session ID to enable all messages aimed at said user to appear in all duplicate tabs. This is in addition to any other room/channel they might already be logged into. At a minimum, users are subscribed to two chatrooms: the actual "real" room, and their own channel using the sessionID.
The problem is that all of the sockets for my sessionID are also in the more general room (and need to be to get messages from other users). When I send out the general chat message I'd like to omit any sockets corresponding to the sending user, as they have already received the message through their own channel. I've gone ahead and made a hash of arrays containing lists of socketIDs for that session, keyed on the sessionID. I've seen a few different syntaxes for specifying exception lists, but none seem to work for me.
The relevant code, with certain parts omitted for brevity:
var sessionSockets = {};
io.sockets.on('connection', function(socket){
if(!io.sockets.manager.rooms["/" + sessionID]) {
sessionSockets[sessionID] = [];
//send message indicating log on to all sockets except for my session
}
socket.join(sessionID); //create private channel for all sockets of the same sessionID
sessionSockets[sessionID].push(socket.id);
socket.on('chat', function(data){
var payload = {
message: data.msg,
from: data.user
};
//send back as personal message to all sockets for this session
io.sockets.in(sessionID).emit('me',payload);
//send to everyone else as regular message; WHAT SYNTAX?
io.sockets.in('').except(sessionSockets[sessionID]).emit('chat', payload);
}
}
tl;dr: How can I send a message to a subset of users in a channel/room without manually doing a comparison of arrays?
use socket.join('room') and then emit to the room by using socket.in(socket.room).broadcast.emit.
This is how you can group clients in a room and emit a perticular example to room