RabbitMQ Node JS Validate User ID - javascript

I use RabbitMQ, SocketIO and MongoDB to make private messages for my app.
The plan is when new user register, app make new unique RabbitMQ Queue for that user, with that user is able to get messages when it is offline. So user send message through SocketIO and it is passed to RabbitMQ Publisher and then when consumer is online he get that message.
My questions now is how to set Validate User ID from sendToQueue (Publisher) function to be able later to read sender ID from consume function?
amqp.connect(CONN_URL, function(err, conn) {
conn.createChannel(function(err, channel) {
ch = channel;
console.log("RabbitMQ channel created...");
});
});
const publishToQueue = async (queueName, data) => {
ch.sendToQueue(queueName, Buffer.from(data.message));
};
const consumeToQueue = async queueName => {
ch.consume(
queueName,
function(msg) {
return msg.content.toString();
},
{ noAck: true }
);
};

I worked out...
const publishToQueue = async (queueName, data) => {
let properties = { headers: {userId: data.to }, timestamp: Date.now() };
ch.sendToQueue(queueName, Buffer.from(data.message), properties);
};
This will send headers with userId information and also timestamp of message

Related

problem with emit from socket.io (server to client)

I'm having a problem with socket.io at the moment I try to send a second time from the server to the client
here is the server code with express and socket.io
io.on('connection', async function (socket) {
let socketId = socket.id;
const mta = new Client("20.64.24.144", 22005, "*", "*");
mta.resources.evokestats.getPlayerCount()
.then((result) => {
console.log("result", result);
socket.emit("players-start", { players: result })
})
.catch((err) => {
console.error(`Ooops! Something went wrong ${err}`);
});
app.post('/player_connect', async function (req, res) {
let ip = req.body[0];
let player = await players.findOne({ ip: ip })
if (player) {
await socket.emit("players", { players: req.body[1] })
} else {
try {
player = await players.create({ ip: ip, name: req.body[2] })
await socket.emit("players", { players: req.body[1] })
await socket.emit("last_24_players", { players: 1 });
} catch (error) {
console.log("error", error)
}
}
res.send("connected")
});
});
and here is my client with reactjs and socket.io
useEffect(() => {
getStats();
}, [])
async function getStats(params) {
socket.on("players-start", function (data) {
setNowPlayers(data.players)
});
socket.on("players", function (data) {
console.log("players", data)
setNowPlayers(data.players)
});});
And in my client using react, in useEffect I listen to the "players-start" and the "players" that was emit.
players-start: It is for every first time that I enter my client he only calls once, to bring all players connected
players: Every time someone connects to the game server, a post call is made to my server where I use the express with socket, in the url '/player_connect' and then immediately emit
The problem: whenever I issue an issue on 'players-start' and then immediately enter the game server that calls the url '/player_connect' it is not triggering the issue of 'players' or at least the client is not receiving.
Test I've done:
My first attempt was to stick everything to the listener "players" but it still doesn’t work
I really appreciate everyone's help.

Send notifications to android app using Firebase Functions

I am developing a chat app and so, I need to send notifications that new messages have been received.
For that, I am using Firebase Functions.
I'm using the sendToDevice function, that needs a token to send a notification. The problem is, I can't seem to retrieve token of the user that sent the message.
This is my .js code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref("/chats/{id}/messages/{messageId}/content")
.onWrite((change,context) => {
var content = change.after.val();
var payload = {
data:{
title: "Stranger has sent you a message",
text: content
}
};
// Here I need to the ID of the person who sent the message
// And then compare this Id with the two Ids of the to users that are in the conversation
// If the two Ids are different, then save the other Id as the token
// So that I can send a notification to the other user.
const senderId = database.ref("/chats/{id}/messages/{id}/sender/{senderId}");
admin.messaging().sendToDevice(senderId, payload)
.then(function(response){
console.log("Successfully sent message: ", response);
return null;
})
.catch(function(error){
console.log("Error sending message: ", error);
})
});
As you can see, I am checking for any changes in the messages/content child.
That as the content of my notification.
Then, I am trying to retrieve the message sender ID so I can know who sent the message and retrieve the other user Id to notify him.
This might be a little confusing so here is my Firebase Realtime Database:
What am I doing wrong so this piece of code works as it should? This is the activity I have in android to receive the message:
class MyFirebaseInstanceId : FirebaseMessagingService() {
override fun onMessageReceived(p0: RemoteMessage) {
if(p0.data.size > 0){
val payload :Map<String, String> = p0.data
sendNotification(payload)
}
}
private fun sendNotification(payload: Map<String, String>) {
val builder = NotificationCompat.Builder(this)
builder.setSmallIcon(R.drawable.common_google_signin_btn_icon_disabled)
builder.setContentTitle(payload.get("username"))
builder.setContentText(payload.get("email"))
val intent = Intent(this, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntent(intent)
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(resultPendingIntent)
val notificationManager = (getSystemService(Context.NOTIFICATION_SERVICE)) as NotificationManager
notificationManager.notify(0, builder.build())
}
}
Following our comments above, here is how to use the once() and val() methods in your Cloud Function:
//.....
const refSenderId = database.ref("/chats/{id}/messages/{id}/sender/{senderId}");
return refSenderId.once('value')
.then(dataSnapshot => {
const senderId = dataSnapshot.val();
return admin.messaging().sendToDevice(senderId, payload)
})
.then(function(response){
console.log("Successfully sent message: ", response);
return null;
})
.catch(function(error){
console.log("Error sending message: ", error);
return null; // <- Note the return here.
})
//.....

firebase functions push Notifications to specific user

i want to send Push notification via firebase functions to the user who posted the post when some other user likes his/her post.
i want to get the highlighted user-id in the image to get fcm token of this user id stored in other tree.
here is my firebase function code below.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/user-posts/{userID}/{pushId}/stars/')
.onWrite(event=> {
var request = event.data.val();
console.log("request",request);
console.log("key",Object.keys(request)[0]);
var key = Object.keys(request)[0];
var token;
const payload = {
notification: {
title: 'You have a new follower!',
body: 'is now following you.'
}
};
const getDeviceTokensPromise = admin.database()
.ref(`/users-notifications/${key}`)
.once('value').then(function(snapshot) {
console.log("val",snapshot.val());
token= snapshot.val();
admin.messaging().sendToDevice(token,payload)
.then(response=>{
console.log("Successfully sent message:", response);
})
.catch(function(error){
console.log("error sending message",error);
})
})
}, function(error) {
// The Promise was rejected.
console.error(error);
});
You can backwards traverse the DB tree by using the event ref's parent property.
userID = event.data.ref.parent.parent.parent.key
parent of event.data.ref is "stars"
parent of "stars" is your pushID
parent of pushID is userID
Try extracting it from the uri with event.params.userID

How to add results from a promise based API call with message.addReply using Recast.ai?

I'm making a bot that searches restaurants based on location. Can anyone help me why this doesnt show up in FB messenger?:
restaurants(result.getMemory('location').raw)
.then(res=>{
message.addReply(res);
message.reply();
});
}
The call to the restaurants function returns the results from a YELP API call (an array of restaurants) but when I add it as a reply to message, nothing happens in FB messenger.
Here is the full code for message.js:
const recastai = require('recastai');
const restaurants = require('./restaurants');
// This function is the core of the bot behaviour
const replyMessage = (message) => {
// Instantiate Recast.AI SDK, just for request service
const request = new recastai.request(process.env.REQUEST_TOKEN,
process.env.LANGUAGE);
// Get text from message received
const text = message.content;
console.log('I receive: ', text);
// Get senderId to catch unique conversation_token
const senderId = message.senderId;
// Call Recast.AI SDK, through /converse route
request.converseText(text, { conversationToken: senderId })
.then(result => {
//Recast takes text analyses that, returns a result object, generates replies adds messages to reply stack and then sends the replies
//Call Yelp API with when the intent is Location. When Yelp returns result we add it to the result.replies array.
//Then we add everything in result.replies to the messaging queue that sends the responses to FB
if (result.action) {
console.log('The conversation action is: ', result.action.slug);
}
// If there is not any message return by Recast.AI for this current conversation
if (!result.replies.length) {
message.addReply({ type: 'text', content: 'I don\'t have the reply to this yet :)' });
} else {
// Add each reply received from API to replies stack
result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }));
}
// Send all replies
message.reply()
//send initial reply generated by Recast first
.then(() => {
//call restaurant function that returns a list of results from API
//if the action is location and done
if(result.action && result.action.slug === 'location' && result.action.done){
restaurants(result.getMemory('location').raw)
.then(res=>{
console.log(res);
message.addReply(res);
message.reply();
});
}
})
.catch(err => {
console.error('Error while sending message to channel', err);
});
})
.catch(err => {
console.error('Error while sending message to Recast.AI', err);
});
};
module.exports = replyMessage;
And here is my restaurants.js code that is imported into the message.js file for the bot behavior:
const rp = require('request-promise');
// Load configuration
require('./config');
const restaurants = (location) => {
return Promise.all([
yelpCall(location)
]).then(result => {
//result contains the return value from Yelp call
return result;
});
};
const yelpCall = (location) => {
const auth = {
method: 'POST',
url: 'https://api.yelp.com/oauth2/token?grant_type=client_credentials&client_id='+ process.env.YELP_APP_ID +'&client_secret='+process.env.APP_SECRET
};
return rp(auth)
.then(result => {
const tokens = JSON.parse(result);
return tokens;
})
.then(result=>{
const options = {
url: 'https://api.yelp.com/v3/businesses/search?location=' + location + "&term=thai",
headers: {Authorization: "Bearer " + result.access_token}
};
return rp(options).then(findings =>{
return findings;
});
});
};
module.exports = restaurants;
A few thoughts :
message.reply is thenable, therefore return message.reply() in two places.
request.converseText() is thenable, therefore return request.converseText(...).
restaurants is thenable, therefore return restaurants(...).
in message.js, message.addReply() is passed object of the form {type:..., content:...} in two places but finally just res. Is that correct?
in restaurants.js, Promise.all() appears to be unnecessary. It will cause its result to be wrapped in an array. module.exports = location => yelpCall(location); seems more appropriate.

Socket.io - Know in which room socket is

I am writing a simple chat on socket.io with node.js server. So I need to know in which room socket is. For example, socket has connected and joined a room. So, when this socket sends a message I want to broadcast it only to the sockets that are in that room (sck.broadcast.to(it_room).emit).
io.on("connection", function(sck) {
...
sck.join(availableRoom);
sck.on("message", function(msg) {
sck.broadcast.to(**its room**).emit(msg);
sck.emit(msg);
});
...
});
I do not need an array of sockets and information about them (in which room etc).
You first enter your username and chat room that you join. Once you submit the form, username and room info will be appended URL as query string. in browser: location.search :this is how we reach to query strings. We have to parse it.
add this CDN to your main.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
this cdn for npm querystringify package. since it will be headache to setup npm package in clients side, cdn is easier to handle it. make sure cdn script files should be loaded first before other js files in hour html. once this cdn is loaded we can use:
const { username, room } = Qs.parse(location.search, { ignoreQueryPrefix: true })
{ ignoreQueryPrefix: true } this option will omit the "?" from query string.
once you reached username and room name, we can emit our event from client side.
client.js
socket.emit('join', { username, room }, (error) => {
if (error) {
alert(error)
location.href = '/' //will keep the client in the same url
}
})
now we gotta listen to this event from server side. but we have to keep track of users.
index.js
io.on("connection", socket => {
socket.on("join", ({ username, room }, callback) => {
//we have to define addUser function
let users = [];
const addUser = ({ id, username, room }) => {
// Clean the data
username = username.trim().toLowerCase();
room = room.trim().toLowerCase();
// Validate the data
if (!username || !room) {
return {
error: "Username and room are required!"
};
}
// Check for existing user
const existingUser = users.find(user => {
return user.room === room && user.username === username;
});
// Validate username
if (existingUser) {
return {
error: "Username is in use!"
};
}
// Store user
const user = { id, username, room };
users.push(user);
// users=users.push(user) this will cause error
return { user };
};
//as you see this function will return either error or user object
//Socket generates an id upon connection.
//result is either error or user
const { error, user } = addUser({ id: socket.id, username, room });
if (error) {
return callback(error);
}
socket.join(user.room);//socket joined to this room
//now time to emit a new event
socket.emit("message", ( "Welcome!"));
socket.broadcast
.to(user.room)
.emit(
"message",
(`${user.username} has joined!`)
);
//broadcast will send messages to everyone in the chat room except the connected socket
callback(); //success scenario. let the client knows that it is allowed to connect
})
})
Try with the following, but I am not sure if it works in every situation.
Object.keys(sck.manager.roomClients[sck.id])[1]
I found this by logging socket, then I looked up for rooms information and extract it the way you can see in my code.
I am using 0.9.6 socket.io version, maybe there is a different solution for latest version.

Categories

Resources