i am very new to pusher.com:
I am trying to set up a presence-channel Chat.
Here is my code:
var PresenceChannel = pusher.subscribe('presence-test_channel');
PresenceChannel.bind('pusher:subscription_succeeded', function(members){
$("#chatMembers").empty();
members.each(function(member) {
$("#chatMembers").prepend("<li id='"+member.info.employee_id+"'>"+member.info.customer_id+"</li>");
});
});
PresenceChannel.bind('pusher:member_added',function(member){
$("#chatMembers").prepend("<li id='"+member.info.employee_id+"'>"+member.info.customer_id+"</li>");
});
PresenceChannel.bind('pusher:member_removed',function(member){
$("li#"+member.info.employee_id).remove();
});
Its working as expected.
But i have a problem:
When i refresh one of the opened browser windows, the following events get fired:
PresenceChannel.bind('pusher:member_added',function(member){...
And directly after that,
PresenceChannel.bind('pusher:member_removed',function(member){...
get fired.
So, after a refresh of one window, the user get removed from my list, and
1 second later, the user again is added to the list....
1) Reload 1 browser window
2) The other window triggers 'pusher:member_removed': User removed from List
3) The other window triggers 'pusher:member_added': User added to the list agein
What to do ?
The 2nd window receives a pusher:member_removed because the 1st window has unloaded and the user had therefore left the presence channel. When the 2nd window reloads and the user resubscribes to the presence channel the pusher:member_added is triggered.
This is expected behaviour.
However, Pusher do add a delay to these event in order to try and stop events being triggered in this scenario. In your case it would seem that the delay in not long enough to stop that happening. In your situation there is an FAQ which provides some information about what you can do to work around this:
How can I stop users going offline for an instant when they navigate between pages?
It is simply solved.
Try this.
Pusher dashboard -> Webhooks
and add Webhook url & event type to Presense.
$app_secret = 'YOUR PUSHER SECRET KEY';
$app_key = $request->headers->get('X-Pusher-Key');
$webhook_signature = $request->headers->get('X-Pusher-Signature');
$body = file_get_contents('php://input');
$expected_signature = hash_hmac( 'sha256', $body, $app_secret, false );
if($webhook_signature == $expected_signature) {
// decode as associative array
$payload = json_decode( $body, true );
foreach($payload['events'] as &$event) {
// do something with the event
if ($event['name'] == 'member_added') {
// do process user joind & trigger message
$this->setAddMember($event);
} elseif ($event['name'] == 'member_removed') {
// do process user out & trigger message
$this->setRemoveMember($event);
}
}
header("Status: 200 OK");
}
else {
header("Status: 401 Not authenticated");
}
More detailed information see document this.
https://pusher.com/docs/webhooks
Related
I have this simple command where I and one other user can only use it to set the "Playing" status of the bot, whilst it's pretty useful for changing it on the fly, I also want to set it when I start up the bot so that I don't have to set it myself every time I restart the bot. Is it possible I can implement this code in the Bot.cs file so that when it starts up, it has the status set and ready
[Command("status")]
public async Task SetBotStatus(CommandContext ctx, string message)
{
if (ctx.User.Id == 572877986223751188 || ctx.User.Id == 327845261692895232)
{
DiscordActivity activity = new DiscordActivity();
DiscordClient discord = ctx.Client;
activity.Name = message;
await discord.UpdateStatusAsync(activity);
return;
}
Basically, the way this current command works is that it's based on an if statement stating only I and another user can use this command. The command has a requirement that you pass over a string which is the text that will be applied to the status and that is simply updated using discord.UpdateStatusAsync
With Dsharp+ this is a pretty easy fix to implement!
In your Main Async method or in a global/public context, you will want to define your activity status which will be the default activity and then set a client-ready handler.
In this example I am using a sharded client, this is not necessary to do so yourself. Your bot must lose its presence in a server before the ready event will fire on starting so it will need to be offline for a couple of minutes for Discord to catch that the client is offline and revoke the presence of the bot from servers. This means in testing you must let the bot lose its presence from being offline, and in production your bot will not lose its set activity from a momentary disconnection or a couple of missed heartbeats.
If 5 consecutive heartbeats are missed the next successful heartbeat will fire the ready event resetting the bot's activity status to the default status. Dsharp+ will tell you this happened by warning you the client has become a zombie.
static async Task MainAsync()
{
DiscordActivity activity = new();
activity.Name = "Testing...";
activity.ActivityType = ActivityType.Streaming;
//The client I have here is sharded but that is not necessary.
var discord = new DiscordShardedClient(new DiscordConfiguration()
{
Token = botToken,
TokenType = botTokenType,
Intents = botIntents,
MinimumLogLevel = botLogLevel
});
// the handler for ready will set with UpdateStatusAsync()
discord.Ready += async (client, readyEventArgs) =>
await discord.UpdateStatusAsync(activity);
// StartAsync() MUST come after this and any other handler.
await discord.StartAsync();
await Task.Delay(-1);
}
I am using bot builder sdk for node.js to create a chatbot. Also connected it to facebook channel. I am using the following code to greet the user:
var bot = new builder.UniversalBot(connector, [
(session, result, next) => {
let text = '';
switch(session.message.address.channelId) {
case 'facebook':
text = 'Hi ' + session.message.user.name + ' !';
break;
default:
text = 'Hi !';
}
session.sendTyping();
session.say(text);
next();
},
(session, say) => {
}
]);
The above code works fine, but I want to add "Get Started" button in the typing bar to invoke the above code. Note that this button appears only once. Please find image of the typing bar below:
Is there a way to achieve this using bot builder sdk for node.js ?
Thanks
Although one can certainly add a button to start any activity with the bot, but that will limit the bots potential to only one customizable channel, i.e. WebChat.
I think there are better 2 alternative ways to get the desired functionality which will work across many channels.
First
I would suggest to add a conversation update event. Code goes in the botbuilder's middleware. Here is a sample code from the docs.
bot.on('conversationUpdate', function (message) {
if (message.membersAdded && message.membersAdded.length > 0) {
// Say hello
var txt = "Send me a Hi";
var reply = new builder.Message()
.address(message.address)
.text(txt);
bot.send(reply);
});
What this will do is make the bot send a message Send me a Hi to the user, if it determines this is a first time visitor. This will give the visitor enough cue to send the bot Hi by typing it. Although he can enter whatever he wants, but this will result in the invocation of the 1st dialog configured which in this case is the will be the dialog which you have posted in question.
Second
You can mark some dialog to be invoked automatically if your bot has never encountered this visitor. Here is the sample code...
var bot = new builder.UniversalBot(connector);
bot.dialog('firstRun', function (session) {
session.userData.firstRun = true;
session.send("Hello...").endDialog();
}).triggerAction({
onFindAction: function (context, callback) {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1);
} else {
callback(null, 0.0);
}
}
});
Here we have split the bot creation and dialog registration in 2 steps. And while registering the firstRun dialog, we have provided it the triggerAction that if the visitor is new, then trigger this dialog.
Both of these approaches do not use adding some extra buttons and it is up to the bot either to educate him on sending some message which in turn will start the 1st dialog or directly start some dialog.
For more info on conversationEvent you can refer to this page
I tried the above options, but they didn't seem to be working for facebook messenger. But I found a solution to add the Get Started button into the typing bar of the messenger. For that we need to use the Facebook Graph API and not the bot builder sdk.
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
{
"get_started":{
"payload":"Get Started"
}
}
The above API call will add the button for you to get the conversation started.
Thanks all for the help!!
I'm providing my users with live notifications.
I'm debating two options and can't decide which is the best way to go when polling the DB.
(The notifications are transmitted using WebSockets.)
Option 1 (current):
I hold a list of all the logged in users.
Every 1000 ms, I check the db for new notifications and if there is any, I send a message via WS to the appropriate user.
Pros:
This task is rather not expensive on resources
Cons:
In off-times, where's there's only a new notification every 1 minute, I poll the DB 60 times for no reason.
In a sense, it's not real-time because it takes 1 full second for new notifications to update. Had it been a chat service, 1 second is a very long time to wait.
Option 2:
Create a hook that whenever a new notification is saved (or deleted), the db get polled.
Pros:
The db does not get polled when there are no new notifications
Actual real-time response.
Cons:
In rush-hour, when there might be as many as 10 new notifications generated every second, the db will be polled very often, potentially blocking its response time for other elements of the site.
In case a user was not logged in when their notification was generated, the notification update will be lost (since I only poll the db for logged in users), unless I also perform a count whenever a user logs in to retrieve their notifications for when they were offline. So now, not only do I poll the DB when ever my notification hook is triggered, but also I poll the db every time a user logs-in. If I have notifications generated every second, and 10 log-ins every second, I will end up polling my DB 20 times a second, which is very expensive for this task.
Which option would you choose? 1, 2? or neither? Why?
Here is the code I am currently using (option 1):
var activeSockets = [] //whenever a user logs in or out, the array gets updated to only contain the logged-in users in any given moment
var count = function () {
process.nextTick(function () {
var ids = Object.keys(activeSockets) //the ids of all the logged in users
//every user document has a field called newNotification that updates whenever a new notification is available. 0=no new notifications, >0=there are new notifications
User.find({_id:{$in:ids}}).select({newNotifications:1}).lean().exec(function (err,users) {
for(var i=0;i<users.length;i++) {
var ws = activeSockets[String(users[i]._id)]
if(ws!=undefined) {
if (ws.readyState === ws.OPEN) {
//send the ws message only if it wasn't sent before.
if(ws.notifCount!=users[i].newNotifications) {
ws.send(JSON.stringify({notifications:users[i].newNotifications}));
activeSockets[String(users[i]._id)].notifCount = users[i].newNotifications
}
}
else {
//if the user logged out while I was polling, remove them from the active users array
delete activeSockets[String(users[i]._id)]
}
}
}
setTimeout(function () {
count()
},1000)
})
})
}
The implementation of Option 2 would be just as simple. Instead of calling
count()
using
setTimeout()
I only call it in my "new notification", "delete notification", and "log-in" hooks.
Code:
var activeSockets = [] //whenever a user logs in or out, the array gets updated to only contain the logged-in users in any given moment
var count = function () {
process.nextTick(function () {
var ids = Object.keys(activeSockets) //the ids of all the logged in users
//every user document has a field called newNotification that updates whenever a new notification is available. 0=no new notifications, >0=there are new notifications
User.find({_id:{$in:ids}}).select({newNotifications:1}).lean().exec(function (err,users) {
for(var i=0;i<users.length;i++) {
var ws = activeSockets[String(users[i]._id)]
if(ws!=undefined) {
if (ws.readyState === ws.OPEN) {
//send the ws message only if it wasn't sent before.
if(ws.notifCount!=users[i].newNotifications) {
ws.send(JSON.stringify({notifications:users[i].newNotifications}));
activeSockets[String(users[i]._id)].notifCount = users[i].newNotifications
}
}
else {
//if the user logged out while I was polling, remove them from the active users array
delete activeSockets[String(users[i]._id)]
}
}
}
//setTimeout was removed
})
})
}
Hooks:
hooks = {
notifications : {
add: function () {
count()
//and other actions
},
remove: function () {
count()
//and other actions
}
},
users: {
logIn: function () {
count()
//and other actions
}
}
}
So, Which option would you choose? 1, 2? or neither? Why?
So i working on my Facebook Messenger Bot.
I want to know ho can i catch a answer for a question like
Bot: Enter your E-mail
User: enters e-mail
Bot: adress was added
My code looks like the sample app from Facebook
app.post('/webhook', function (req, res) {
var data = req.body;
// Make sure this is a page subscription
if (data.object == 'page') {
// Iterate over each entry
// There may be multiple if batched
data.entry.forEach(function(pageEntry) {
var pageID = pageEntry.id;
var timeOfEvent = pageEntry.time;
// Iterate over each messaging event
pageEntry.messaging.forEach(function(messagingEvent) {
if (messagingEvent.optin) {
receivedAuthentication(messagingEvent);
} else if (messagingEvent.message) {
receivedMessage(messagingEvent);
} else if (messagingEvent.delivery) {
receivedDeliveryConfirmation(messagingEvent);
} else if (messagingEvent.postback) {
receivedPostback(messagingEvent);
} else {
console.log("Webhook received unknown messagingEvent: ", messagingEvent);
}
});
});
// Assume all went well.
//
// You must send back a 200, within 20 seconds, to let us know you've
// successfully received the callback. Otherwise, the request will time out.
res.sendStatus(200);
}
});
You can set a flag for their ID that the E-Mail prompt was sent, and then after they respond check to see if it's an E-mail, and if so, then save it and echo it back to them.
If the bot is based on question/answer, what I normally do to handle response tracking is treat the bot like a finite state automata. Assign every "state" your bot can be in to some unique state identifier, and use said state identifier to determine what the user is replying to. You could also store callbacks instead of state ids, but high level this will behave the same way.
For Example:
First define a finite automata. In this case, lets assume it's:
0 --> 1 --> 2
Where 0 means new user, 1 means waiting for email response, 2 means user successfully completed registration.
User messages bot
We check our database and see it's a new user. We assume
state==0.
Because state is 0, we ignore what was sent and prompt for email
Change state to 1 to denote the email was prompted.
User replies with email.
We check database and see state==1. We use the "1" routine to do fancy stuff to verify the email and store it.
Change state to 2 to denote the email was received and the program has ended.
Note:
If the conversation id for the platform you're targeting is reset
after a certain amount of inactivity (or if you just want the bot to
mimic real conversations), store the time of each user's last
interaction and purge all inactive conversations well after the
conversation has been terminated.
Typical chat app. Using the presence channel to tell who is online, but looking for an elegant way to mark a User in the presence channel with an idle flag.
The full solution to this is probably reasonably complicated and it also depends on the runtime - I'm going to assume web web client.
Ultimately you need a way of doing two things:
to detect a user as being "idle"
to signal all other users about that user being idel
To detect a user is idle
window.onblur so you know your app window is no longer focused
mousemove tracking to see if the user is actually doing anything within your application.
In order to achieve this you probably just want a timeout and only if that timeout triggers do you send an event to indicate the user is idle:
var timeout = null;
function sendUserIdle() {
// see part 2
}
function resetIdleTracking() {
if( timeout !== null ) {
// reset
clearTimeout( timeout );
}
timeout = setTimeout( sendUserIdle, 3*60*1000 ); // 3 minutes
}
window.addEventListener( 'mousemove', resetIdleTracking );
Signal other users about idle users
A missing feature of Pusher presence channels IMO is the ability to update presence information. So, you need another way of achieving this. I think you have two solutions:
Enable client events on the presence channel and trigger an event from the idle user when your code detects the user becoming idle.
Send a message to the server from the idle client. The server then triggers a message telling the users that the user is idle.
See: accessing channel members.
1. Using client events
function sendUserIdle() {
var channel = pusher.channel( 'presence-<your-channel>' );
// client events have to have a 'client-' prefix
channel.trigger( 'client-user-idle', channel.members.me );
}
2. Sending to the server
function sendUserIdle() {
makeAjaxRequest( '/idle-notification-endpoint', channel.members.me );
}
Note: you can serialise channel.members.me using JSON.stringify( channel.members.me )
On the server (in pseudo code):
userInfo = getUserInfoFromRequest();
pusher.trigger( 'presence-<your-channel>', 'user-idle', userInfo );
Showing a client is idle
Upon receipt of the event you would update the list of users UI accordingly (mark that user as idle).
channel.bind( 'user-idle', function( user ) {
var uniqueUserId = user.id;
// update UI
}