subscription error in subscribing to a pusher presence channel - pusher

I'm trying to go through a basic presence channel example with pusher.js and I'm getting a pusher subscription_error , invalid auth response for channel, expected channel_data field
var presenceChannel = pusher.subscribe('presence-' + room);
presenceChannel.bind_all(function(err) {
console.log("err: " + err);
});
presenceChannel.bind('pusher:subscription_succeeded', function(members) {
members.each(function(member) {
console.log(member);
});
});
Is there some sort of initialization that I need to do to create a presence channel beforehand? Or can I just connect to one and it will create a presence channel. Does subscribing to a presence channel add my presence info to it as a member?

So it looks like authorization on presence endpoints need to be implemented and have a userinfo parameter passed in which I wasn't doing server side.
var presenceData = {
user_id: request.auth.credentials.id,
user_info: {
uname: request.auth.credentials.uname
}
};
var auth = pusher.authenticate(socketId, channel, presenceData);

Related

Notify Users when Child is added on specific node using Cloud function

Is it possible for cloud function to listen for specific node for when a child is added and then send a notification to users located on a different node, and if that is possible how so? I am using node.js with Firebase realtime database and not Firestore.
This is my database:
I want the cloud function to listen every time a child is added on "Emergencies", and then notify all the users in the "Registered Admins"
This is the contents of the users in "Registered Admins" node, it has a child "Notification" containing the message, and I want to send that message to all the users, when a child is added on "Emergencies" node.
This is my cloud function using node.js. I've deployed it however it does not work, does not send any notification at all.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.listen = functions.database.ref("/Emergencies")
.onWrite(async (change, context) => {
change.after.val();
context.params.pushId;
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise]);
tokensSnapshot = results[0];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return functions.logger.log(
'There are no notification tokens to send to.'
);
}
functions.logger.log(
'There are',
tokensSnapshot.numChildren(),
'tokens to send notifications to.'
);
// Notification details.
const payload = {
notification: {
title: "New Emergency Request!",
body: "Someone needs help check Emergenie App now!",
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
Yes, that sounds possible and is in fact quite close to what the example on notifying users when something interesting happens does.
To send a message to a specific device, you 'll need to know the token for that device. If you want to broadcast a message to multiple users, you could subscribe those users to a topic. Just keep in mind that anyone can subscribe to a topic if they know its name, so you can't use that to send messages that only a certain group of users is allowed to see.

DirectLine API: Define a new ChannelId

I'm implementing a new channel for my bot's front end using the botframework-directlinejs NodeJS SDK. This channel will provide some custom back-channel functionality; however, my bot needs to know that the conversation it is communicating with is via this channel before it can construct an activity to use it.
From what I can gather from the 'Activity' object in the API, the channelId field should be set by the channel.
However,
myChannel.postActivity({
type: 'message',
text: 'hi',
from: {
id: "Node test user",
},
channelId: 'myChannel'
}).subscribe(
id => console.log("Posted activity, assigned ID ", id),
error => console.log("Error posting activity", error)
);
indeed sends the message 'hi' to my bot, but the channelId comes out as 'directline'.
Performing this same operation in Fiddler as a post to https://directline.botframework.com/v3/directline/conversations/<conversationID>/activities has the same response.
My suspicion is that the 'channelId' property of the Activity object is read-only, and that the API adds this value.
Is it possible to set a custom id of the channel?
No, it is not possible to set a custom channel id. There is a corresponding connector service for each channel type. If you're using Direct Line, the channelId should be directline.
You can send custom information through channel data though:
BotChat.App({
botConnection: Object.assign({}, dl, {
postActivity: activity => {
var newActivity = Object.assign({}, activity, { channelData: { "MyKey": "MyValue" } });
return dl.postActivity(newActivity);
}
}),
bot: bot,
user: user,
resize: 'detect',
}, document.getElementById('bot'));

node-apn : Provider should be created per notification request or one-time

I am new to node-apn. I have implemented it in nodejs application. Below is my code.
var APN = require('apn')
var apnProvider = new APN.Provider({
token: {
key: "PATH_TO_FILE",
keyId: "KEY",
teamId: "TEAM"
},
production: false
});
module.exports = {
send: function (tokens, message, callBackFn) {
var note = new APN.Notification({
alert: "Breaking News: I just sent my first Push Notification",
});
// The topic is usually the bundle identifier of your application.
note.topic = "BUNDLE";
console.log(`Sending: ${note.compile()} to ${tokens}`);
service.send(note, tokens).then(callBackFn);
}
};
So in some documentation it says we should shutdown apnProvider.
So my question is should i create apnProvider globally (like i have done)?
OR should i create per send request (inside send function) & call shutdown after send notification.
I tried reading online. But i couldn't find any example like my requirements.

Slackbots node.js get usernames

Using Slackbots(https://www.npmjs.com/package/slackbots) I was hoping to build a simple message forwarding bot (i.e. when the bot receives a direct message, it shoots it into a chat room and says who it is from) but I can't seem to get it to push out user names, only user ID's. Any idea here?
var SlackBot = require('slackbots');
var bot = new SlackBot({
token: 'xoxb-',
name: 'helper'
});
bot.on('start', function() {
var params = {
icon_emoji: ':cat:'
};
bot.postMessageToChannel('helpertest', 'meow!', params);
});
bot.on('message', function(data) {
bot.postMessageToChannel('helpertest', data.text + data.user.name);
})

How to generate a room id and force two users to join that room?

Im building a chat application in node.js , socket.io and mongoose. My goal is to build a one on one chat, it is similar like facebook web chat. So basically whenever I click on a user it will go to that user url and possibly chat with him/her
But the problem, that I'm facing is that I could only emit the message to the targeted user, but not to myself.
Here's the current code
Serverside
socket.on('chatTo', function(data){
User.findOne({ username: data.destinationUsername, socketId: { '$ne': null}}, function(err, foundUser) {
if (foundUser) {
io.to(foundUser.socketId).emit('incomingChat', { sender: user.username, message: data.message });
} else {
io.to(socket.id).emit('deliverError', { error: foundUser + ' is not online' });
}
});
});
Clientside
$(function() {
var socket = io();
function chatTo(message, destinationUsername) {
socket.emit('chatTo', { message: message, destinationUsername });
}
$('#sendMessage').submit(function(){
var input = $('#message').val();
var username = $('#username').val();
chatTo(input, username);
$('#message').val('');
return false;
});
socket.on('incomingChat', function(data) {
var html = data; // Messages to append to media-list
$('.media-list').append(html);
});
});
So what is happening here is that, on the clientside, User A clicks form submit , to submit the message, it will invoke chatTo function and emit the data. Just want to let you guys know, input and username are from html page. It would look something like this
input = "Hello";
username = "jackmoscovi" // user's username
then it will emit both of these data back to server, which is socket.on('chatTo') is listening to. After some MongoDB operation, if found the user then emit to that specific socket.
The result will be
The message that was emitted by batman only shows up on joker's page but not on Batman's page. I know that because I specifically emit to that socket. The real question is How do I force both of these user to be in a unique room? Should I create a new Room mongoose model? and then call socket.join('uniqueRoom')?
and then simply io.to('uniqueRoom').emit("Message?");
How would I implement this room mechanism?
I have been struggling for 2 days already, please someone end this misery :(
First create a mongoose schema like this
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var messageSchema=new Schema({
sender : {
type : String,
},
receiver : {
type : String.
},
message : {
type : String
}
})
mongoose.model('messages',messageSchema);
Then in server code
message = mongoose.model('messages')
socket.on('chatTo', function(data){ // data is json containing sender, receiver and message
User.findOne({ username: data.destinationUsername, socketId: { '$ne': null}}, function(err, foundUser) {
if (foundUser) {
io.to(foundUser.socketId).emit('incomingChat', { sender: user.username, message: data.message });
socket.emit('incomingChat', { sender: user.username, message: data.message });
var newMessage = new message({
sender : data.sender,
receiver : data.receiver,
message : data.message
})
newMessage.save(function (err, data){
if(err)
console.log(err)
})
} else {
io.to(socket.id).emit('deliverError', { error: foundUser + ' is not online' });
}
});
});
This is a little bit of a difficult one to answer because it feels like you are actually asking the wrong question. To understand how you are "supposed" to do this you really have to have solved quite a few other architectural issues first.
The first thing you need to solve is the Authentication/Authorisation problem i.e. when you connect a socket from the browser to the server how does your server know who you are and know that you're not an imposter? I don't have a best practice for this but you might want to check out the socketio-auth package which might give you a few ideas.
The second thing you need to solve is how to associate the authenticated user with the socket. The only source that I could find that has any reference to this is in the chat demo on the socket.io website.
At this point, you should have a collection of sockets in your server that have associated users. When Batman comes online you should check the database for any Chats that he is part of and have him connect to that chat room by id:
Chat.find({usernames: "Batman"}).exec().then(function(chats){
chats.forEach(function(chat){
socket.join(chat.id);
})
});
So this so far is assuming that Joker hasn't already created a chat with Batman, and hence there is not a chat in the database that connects them both.
When Joker wants to chat to batman you need to send a "connect" message down the socket that
creates a chat with both of them in it
finds both their current sockets if they are currently connected to the server and
sends a message that they have connected to each other
example:
socket.on('startChat', function(data){
//assuming that you have assocated the current user with the socket
var currentUser = socket.user;
//who the current user (Joker) wants to connect to
var connectToUser = data.connectToUser; // Batman
Chat.create({
usernames: [currentUser.username, connectToUser.username] // ["Joker", "Batman"]
}).then(function(newChat){
socket.join(newChat.id); // add current user (Joker) to that socket.io room
// this is overly simplified: assuming you have an array collectionOfConnectedSockets which
// contains all currently connected sockets
var otherUserSocket = collectionOfConnectedSockets[connectToUser.username];
//otherUserSocket now is Batman's socket - so make him join the room
otherUserSocket.join(newChat.id);
//send them both a message that a chat has been connected
io.to(newChat.id).emit('You have been connected to a new chat');
});
});
Now this brushes over a lot of the details, and each of those details depend on your application's architecture. Also my syntax may be a little off, the examples are only given as an indication of you should be trying to do and not as a direct solution to your problem.

Resources