I'm new to node
I have a PHP/Laravel cms and I have a simple Nodejs game server which is basically a loop generating some numbers
I connect my PHP backend to Nodejs via Socketio and use Socketio-JWT to identify the user
my client side (php/laravel)
PHP
$userToken = JWTAuth::customClaims(['userid' => Auth::user()->id, 'name' => Auth::user()->name, 'avatar' => asset_url('image/avatar-default.png')])->fromUser(Auth::user() );
html/js
var socket = io.connect("http://localhost:666");
socket.on('connect', function () {
socket.emit('authenticate', {token: '{{$userToken}}'});
});
socket.on('authenticated', function () {
console.log('Authenticated');
});
socket.on('unauthorized', function (data) {
console.log('Unauthorized, error msg: ' + data.message);
});
my serverside
const _online_users = { };
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
io.sockets.emit('update_online_users' , _online_users );
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
}) ;
as you can see I have an object called _online_users and I store authenticated users in this object and then I send it to the clients so they know who is online
io.sockets.emit('update_online_users' , _online_users );
here is the problem, when the user gets disconnected
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
I have to update my _online_users object and remove disconnected user .... how should I go about this? I was thinking maybe I can store the token itself in the _online_users
_online_users[socket.decoded_token.userid] = {
token : socket.token ,
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
and when the user gets disconnected I get the disconnected token from the socket and remove the user from an object by that token
of course, this is all theory! I'm not sure if that's the way to go .... first of all, I can't access the token itself from the socket !
or let's say one of the users sends another request to node server, how can I identify the user sending the request?
.on('authenticated', function(socket) {
socket.on('somaction', function() {
console.log(` who is this guy ? `)
});
})
is there anything unique insocket.decoded_token that I can use as id? if so I can store it in the online users send it back and forth when the user requests something
basically I'm lost and appreciate any pointers
You can use a middleware now in the newer versions of socket-io. So you can check if the user is logged in by the jwt token that is sent with the request. If decoded successfully you can assign the user info to the current socket and call next() and go to the event you are listening for. Here is the example provided in the socket-io docs slightly modified for your case.
io.use(function(socket, next) {
const handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized'));
// else decode jwt token here and append the user to the socket.request
// and call next
// pseudo code here
const {
authorization
} = handshakeData.header
let token;
if (authorization && authorization.split(" ")[0] === "Bearer") {
token = authorization.split(" ")[1]
}
let user = jwt.decode(token, secret);
socket.request.user = user;
next();
});
You are doing well!
Since you are adding socket event handlers in the 'authenticated' handler you still have access to socket.decoded_token.userid.
This should be enough:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name: socket.decoded_token.name,
avatar: socket.decoded_token.avatar,
};
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`);
delete _online_users[socket.decoded_token.userid];
io.sockets.emit('update_online_users', _online_users);
});
});
or to be a little more concise:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
const { avatar, name, userid } = socket.decoded_token;
_online_users[userid] = { name, avatar };
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
delete _online_users[userid];
io.sockets.emit('update_online_users', _online_users);
});
socket.on('any other event...', function() {
// ... still have access to userid
});
});
Edit: About unauthenticated socket I don't know; doc says nothing. You could try something like:
io.socket.on('connection', socket => {
socket.emit('update_online_users', _online_users);
// I'm afraid this closes the socket if unauthorized, you could check by yourself
socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
})(socket);
}).on('authenticated', socket => {
//...
});
Hope this helps.
Related
So I'm trying to set up a socket.io chat with expressjs backend and sveltejs frontend.
I have a custom namespace called 'chat' and I create a new room on 'join' request.
I tried to stick very closely to official documentation.
Here is my code:
Server:
const app = express();
const server = app.listen(3002);
log.info("Express server has started on port 3002");
const io = require("socket.io")(server, { path: "/api/express/socket.io" });
const chat = io.of("/chat");
chat.on("connection", socket => {
log.info("New User connected");
socket.on("join", room => {
log.info("New User joined room: " + room);
socket.join(room);
});
socket.on("chat message", data => {
log.info("'chat-message'-Event: ", data);
chat.in(data.room).emit("chat message", {
room: data.room,
msg: data.msg,
user: data.user
});
});
});
Client:
let chatSocket
onMount(async () => {
chatSocket = io('https://my.domain.com/chat', {
path: '/api/express/socket.io',
transports: ['websocket'],
})
chatSocket.on('connection', function(socket) {
socket.on('chat message', function(data) {
alert(data.msg)
})
})
chatSocket.emit('join', 'Chat-Room#' + id)
})
const Submit = async e => {
chatSocket.emit('chat message', {
room: 'Chat-Room#' + id,
msg: statusText,
user,
})
}
According to the server console output everything seems to be setup fine.
All the events are fired properly and logged to console.
But the clients don't seem to receive any 'chat message' events (they send them out just fine).
Any ideas whats going wrong here?
chatSocket.on('connection', function(socket) {
socket.on('chat message', function(data) {
alert(data.msg)
})
})
You're registering the 'connection' event, an event that doesn't event exist in the latest socketIO version at least. This function never fires. Also, even if it did exist, you would still need to use the original "chatSocket" that you already established, and not "socket".
Change that piece of code to:
chatSocket.on('connect', function() {
chatSocket.on('chat message', function(data) {
alert(data.msg)
})
})
It's possible that maybe in older versions of socketIO the "connection" event existed, you need to take a look at the docs: https://socket.io/docs/client-api/#Event-%E2%80%98connect%E2%80%99
Notice that the event names in the frontend and server aren't the same. You have separate docs for each.
Ok. So apparently the problem was due to faulty nginx reverse proxy configuration.
I had added the 'upgrade' and 'connection' headers for the server but not for the client.
Fixing that resolved the issue.
Yes, I have gone through the documentation, which is very well written:
Socket IO Cheatsheet
Here is the problem: I want to notify the user of a logout when his session from the Express App is being destroyed. Now this is what is happening: When I log out from the session, all other clients (including those who have or have not even logged in) get a message saying they're logged out. Yes, my express app is working fine - they are not getting logged off, but I believe SOCKET IO is sending them the message regardless. I ran the debugger and it turns out that both the clients are distinguishable, too.
Here is my code:
server.js:
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.set('socketio', io);
io.on('connection', function (socket) {
app.set('current_socket', socket);
console.log('No of clients:', io.engine.clientsCount);
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const io = req.app.get('socketio');
const this_socket = req.app.get('current_socket');
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
I even tried this instead:
io.emit('userAction', { action: 'logout' });
but turns out it still emits to all the clients. I am pretty sure there is a mismatch somewhere, just can't figure out where.
You need create room for each session id if you want to send emits to spesific user
io.on('connection', function (socket) {
app.set('current_socket', socket);
var sessionId = socker.request.session.id
//join room
socket.join(sessionId);
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const io = req.app.get('socketio');
const this_socket = req.app.get('current_socket');
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.sockets.in(sessionID).emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
You have to define unique socket object for each user. We have many ways to do that.
In simple way, we use user id (unique) as a key to store socket object (Map way: key(userId) - vaule(socketObj)).
Follow the rabbit:
When a user loggedin, client side emits a event (login) to server side, the event include the user id.
Client Side:
// login success
socket.emit('userLoggedIn', {userId: THE_USER_ID})
Server Side:
io.on('connection', function (socket) {
// app.set('current_socket', socket);
console.log('No of clients:', io.engine.clientsCount);
socket.on('userLoggedIn', function(data) => {
app.set(data.userId, socket); // save socket object
})
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const userId = MAGIC_GET_USER_ID_FROM_SESSION_ID(sessionID) // who want to logout
const io = req.app.get('socketio');
const this_socket = req.app.get(userId); // get "user socket"
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
I made a simple bot with Node.js and the Telegram BOT API and the question is how can I send a message each certain time, for example I want to say "hello" every 5 minutes, What do I have to do ?
Here is my current code:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
const axios = require('axios')
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({
extended: true
})); // for parsing application/x-www-form-urlencoded
//This is the route the API will call
app.post('/new-message', function(req, res) {
const {message} = req.body;
//Each message contains "text" and a "chat" object, which has an "id" which is the chat id
axios.get('some_api'+message.text.toLowerCase()+ '/')
.then(resp => {
axios.post('https://api.telegram.org/bot<MYTOKEN>/sendMessage', {
chat_id: message.chat.id,
text: `*${resp.data[0].name} (#${resp.data[0].symbol})*
Price USD: ${resp.data[0].price_usd}
Percent Change 24h: ${resp.data[0].percent_change_24h}
Market Cap USD: ${resp.data[0].market_cap_usd}`,
parse_mode:'Markdown'
})
.then(response => {
// We get here if the message was successfully posted
console.log('Message posted')
res.end('ok')
})
.catch(err => {
// ...and here if it was not
console.log('Error :', err)
res.end('Error :' + err)
})
})
.catch(err => {
// ...and here if it was not
console.log('Error :', err)
res.end('Error :' + err)
})
});
// Finally, start our server
app.listen(3000, function() {
console.log('Telegram app listening on port 3000!');
});
you can do that,
you can send messages to a specific user or to a specific chat.
But first you need to get the msg.from.id or 'msg.chat.id', store it, and send notifications whenever you need.
when a user joins your bot, and he press the 'start' button you can trigger that action, on your server:
// on global scope or wherever
var fromId=null
var chatId=null
// trigers when user press 'start' button
bot.onText(/\/start/, function (msg, match) {
chatId=msg.chat.id;
fromId = msg.from.id; // store that value and use it as param on sendMessage()
var name = msg.from.first_name
bot.sendMessage(fromId, `Welcome dear ${name} have fun`);
});
Now you can create our time intervals and send to user or char
if(fromId) {
setInterval(() => {
bot.sendMessage(fromId, `message to the user`),
300,000}) // every 5 mins
}
I hope this helps.
I'm new to socket.io, and I'm doing a simple API with NodeJS (express 4). I'm developing an action that is similar to the old "poke" action at facebook. A user send a poke to other user, and this one gets a notification on real time (this is the reason why I am using socket.io).
This is the code:
app.js
var port = 3000;
var app = module.exports = express();
var server = require('http').Server(app);
...
server.listen(port);
require('./config/socket-io')(app, server, secret);
socket-io.js
module.exports = function(app, server, secret) {
var clients = {};
console.log("initiating sockets...");
var sio = require('socket.io').listen(server, {'log level': 2});
sio.on('connection', function (socket) {
console.log("...new connection: "+socket.client.id);
clients[socket.id] = socket;
socket.emit('identification', { data : socket.client.id });
socket.on('newShoutOut', function(data) {
var receptor = data.idTo;
var emiter = socket.client.id;
console.log("...new shout out from " +emiter+ " to "+receptor);
sio.sockets.sockets[receptor].emit({ data : data.data, from : emiter });
});
socket.on('disconnect', function() {
console.log("..."+socket.client.id + " disconnected");
});
});
};
Here you can differentiate three states:
Connection: The server detects all the clients connection to the host:port. After that, the server sends to each client his ID. This works fine.
Send message: One client sends a notification to other client. For now, the server receives the notification from one client, but the "receiver" doesn't receive anything.
Disconnection: Doesn't matter in this case.
My question is, what is the way to send a message to a client directly knowing the ID? What I am doing wrong? I tried so many options to send a message directly to a specific client ID but didn't work...
EDIT
Frontend
var socket = io('http://localhost:3000');
var id = "";
socket.on('connection', function (data) {
console.log("connected!");
console.log(data);
});
socket.on('identification', function(data) {
id = data.data;
$("#socket_info h1").html("ID: "+id);
});
socket.on('newShoutOut', function(data) {
console.log("newShoutOut received!");
});
Ok, so I assume the shoutout is coming from a user? You will need to create the event on the clientside, such as:
var button = $('#button');
button.on('click', function() {
var msg = 'message',
userID = '123'; //get the ID who they are messaging
socket.emit('sendShoutOut', {msg: msg, id: userID});
});
Then you will need to receive that response on the server, and reply to the user in that function:
socket.on('sendShoutOut', function( data ) {
socket.sockets.sockets[data.id].emit('sendPrivateMsg', { data : data.msg, from : emiter });
});
Lastly, the reciever must be notified, so you will need to handle the response on the client:
socket.on('sendPrivateMsg', function( data ) {
alert(data);
});
Hope this helps.
I am using Node JS with express and I want to create a new user in my db with a REST call if the user logs in successfully with facebook. This code is just a slight modification of the heroku node.js facebook app : https://github.com/heroku/facebook-template-nodejs/blob/master/web.js
Here's what i've tried
//create Parse Object
var parse_app_id = process.env.PARSE_APP_ID;
var parse_master_key = process.env.PARSE_MASTER_KEY;
var nodeParse = new Parse(parse_app_id, parse_master_key);
function handle_facebook_request(req, res) {
// if the user is logged in
if (req.facebook.token) {
async.parallel([
function(cb) {
// query 4 friends and send them to the socket for this socket id
req.facebook.get('/me/friends', { limit: 4 }, function(friends) {
req.friends = friends;
cb();
});
},
function(cb) {
// query 16 photos and send them to the socket for this socket id
req.facebook.get('/me/photos', { limit: 16 }, function(photos) {
req.photos = photos;
cb();
});
}
], function() {
render_page(req, res);
nodeParse.signUp('',{ username: 'tajmahal', password: 'stuff'}, function(err, response) {
console.log(response);
});
});
}
}
app.get('/', handle_facebook_request);
app.post('/', handle_facebook_request);
handle_facebook_request is executed when the app is requested...everything works fine besides the user creation.
signUp creates a user when its outside of the conditional (so the function works)
How can I fix this?