How do you link GCM chrome push notifications and payload data? - javascript

Push notifications in Chrome via GCM are driving me crazy.
I've got everything up and running. I serve the push using my python server to GCM. A service worker displays the push notification fine.
To my knowledge, there is NO data passed with push events. Sounds like it's coming soon but not available yet.
So just before the push notification shows, I call my server to get extra data for the push notification. But I have no information on the push notification to send to my server to match and return relevant data.
Everything I can think of to match a notification and user data is purely speculative. The closest thing I can find is a timestamp object on the PushEvent{} that roughly matches the successful return of the GCM call for each user.
So how are other people handling custom payload data to display Chrome push notifications?
The PushEvent{} does not seem to have any ID associated with it. I know the user that the push is for because I've previously stored that information at the time of registration.
But once I receive a push, I have no idea of knowing what the push was for.
I would like to avoid:
Trying to match based on timestamp (since notifications displays are not guaranteed to be instant).
Trying to pull the 'latest' data for a user because in my case, there could be several notifications that are sent for different bits of data around the same time.
How are other sites like Whatsapp and Facebook linking custom payload data with a seemingly sterile event data as a result of a push notification?
How are you doing it? Any suggestions?
Here's what my receiver code looks like:
self.addEventListener('push', function(event) {
event.waitUntil(
fetch("https://oapi.co/o?f=getExtraPushData&uid=" + self.userID + "&t=" + self.userToken).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var title = data.notification.title;
var message = data.notification.message;
var icon = data.notification.icon;
return self.registration.showNotification(title, {
body: message,
icon: icon,
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this push message';
var icon = "https://oapi.co/occurrences_secure/img/stepNotify_1.png";
var notificationTag = 'notification-error';
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
})
);
});

I understand your problem, and i've been fiddling with the same when i wanted to use chrome notification. You can use indexedDB to save ObjectStore and retrieve data in webServices.
IndexedDB is accessible to webservices. I am using it to store user information and when the user recieves a push notification i pass the stored access key to identify the user and pass him relevent information.
Here's matt gaunt's tutorial which says indexed db is accessible to web services:
http://www.html5rocks.com/en/tutorials/service-worker/introduction/
Here's a good indexedDB tutorial:
http://blog.vanamco.com/indexeddb-fundamentals-plus-a-indexeddb-example-tutorial/

Assuming you are still in the past. That is, sending only a push trigger to your browser with no payload it's now time to move on with time. You can now send payload in your push events. Since you seem familiar with GCM, it's ok to go with that though there is now the Web Push Protocol which is browser vendor independent.
Briefly, to make that work you need to encrypt your payload with specifications found here, in your server.
There is a node by google chrome and PHP implementations for that, that I know of.
You may check out the PHP Web Push here.
In the browser you would need to provide the subscription object now with the p256dh and auth on top of the endpoint as before.
You may check this out for more details.

Related

Auth0 unable to get ID token / user metadata

I am currently working on adding Auth0 to a Vue.js/Node.js application and so far I have figured out how to allow users to register and log in (to /callback) and that seems to be working fine. However, I have manually added (will be automatic later on) some data to the user metadata section. I have the below code as a rule that is turned on. I can’t seem to get access to the data on the Vue.js end of things. What I’d like is to be able to get the user data and user metadata so I can store it in my front end.
Rule code
function (user, context, callback) {
const namespace = 'account_signup_type/';
const namespace2 = 'account_type';
context.idToken[namespace + 'is_new'] = (context.stats.loginsCount === 1);
context.idToken[namespace2] = user.account_type;
context.idToken.user = user;
callback(null, user, context);
}
Code I am trying in my Vue.js front end
getIdTokenClaims(o) {
return this.auth0Client.getIdTokenClaims(o);
}
Currently, this returns undefined
I ended up figuring it out, there was no namespace being created in the id_token which resulted in it not properly passing the data through to the Vue .js app. I added a namespace using a web address format with the domain extension cut off and it now works.

What does Message.fetch() actually do in discord.js

According to the Message#fetch() docs, this function simply fetches the message it was called on. However, I'm not sure in which circumstance it would ever make sense to use this function.
According to the screenshot above, this method returns Promise<Message>. I'm confused, because why would you need to fetch a message you already have access to? For example:
// let's say somebody sent the message `hello`
client.on('message', (message) => {
console.log(message.content) // this would return `hello`
message.fetch((message) => console.log(message.content)) // this would also return `hello`; it's the same object
So, what's the point of it? If you didn't have access to the message, you wouldn't be able to use Message.fetch() anyways.
And I'd ask the same question for Channel#fetch, ClientUser#fetch, Guild#fetch, GuildMember#fetch, GuildPreview#fetch, etc.
If we dive into the source of the Message class and look for the fetch method, we see something like this:
/**
* Fetch this message.
* #param {boolean} [force=false] Whether to skip the cache check and request the API
* #returns {Promise<Message>}
*/
fetch(force = false) {
return this.channel.messages.fetch(this.id, true, force);
}
The fetch() method in this cases, retreives the last message posted in the channel. If you invoke this method and log the output, you see that it fetches the message you have posted. It simply returns asynchronous message object. Promise<Message>.
So let's say you post this message Hello, i'm a message. and invoke and log the .fetch() method, you see in the console the message object and content Hello, i'm a message.. All this does is fetching the message posted in the channel. Same goes for channel#fetch, ClientUser#fetch.
All it does is using the cache to fetch a channel, user, guild, message. The force argument is to receive the realtime data.
The force flag is used if you want to skip the cached message and want to do the API request.
If you wish to fetch all messages or a few messages, you can use the MessageManager class:
message.channel.messages.fetch('Message ID');
or
message.channel.messages.fetch({
limit: 5, // Amount of messages to be fetched in the channel
before: "Snowflake (ID from message to be fetched)",
after: "Snowflake (ID from message to be fetched)",
});
More on this: https://discord.js.org/#/docs/main/stable/typedef/ChannelLogsQueryOptions
The point of this fetch command is because there are many situations where your bot does not have messages in cache to access. The most basic example is 1. During development you are are editing code and restarting the bot. Now those messages are lost and the bot doesn't have full access to them.
Your running program loses internet connection - enjoy your limited access and errors.
Cached messages are limited to 100 / channel. If you want access to earlier messages you must fetch. A common example is when the bot is asked to delete messages from a channel.
In our some of our servers the messages can exceed the cache amount within a minute. Specific messages / commands need to be fetched to auto-delete after x minutes.

Retrieve groupchat history using strophe.js

I am using ejabberd 15.06 version with Strophe.js. Retrieving the one-to-one chat from my backend database works fine. But how can I retrieve the groupchat history from the database??
For example, if I have a "strophe" group. When new users joins in the strophe group, then the chat history done in the group by other users should be displayed.
I am using this code
var pres = $pres({ to: room + "/" + nickname, from: connection.jid });
connection.send( msg.c('x', {xmlns: NS_MUC}));
if(chat_history != null){
var msg_history = msg.c('x', { "xmlns": "http://jabber.org/protocol/muc"}).c("history", chat_history, {maxstanzas: 50});
debugger;
console.log(msg_history);
}
In my console it looks like
h.Builder {nodeTree: presence, node: x}
I am stuck how to fetch the history of groupchat. Please help
Usually, unless the room has been configured not to send any history, send the join presence should be enough to let you receive the latest chat room messages.
Please, note that old messages have a delay tag on them to provide the time at which the original message was send, so make sure your client is not discarding those messages.
If you want control about the history size, you can use the Strophe MUC plugin to join the room and send the max stanzas and time limit as the history_attrs variable. Your server and room must also be configured to provide the history.
conn.muc.join(room, nick, msg_handler_cb, pres_handler_cb, roster_cb, password,{ maxstanzas: 10, seconds: 3600 });

Sending push notification to specific device knowing phone number, parse.com

I would just to get things clear here or get other suggestions if possible if it is better.
This is how my application works now:
1) Anonymous user is created if its the first time the user open the application
2) Phone verification is needed to be done. If verified, i save the phone number in a custom field in user object ( do i need to make this user a real user after this or can i still go with anonymous user?)( verification is one time only of course)
3) The user will be able to pick a friend from his contact list(ABPeoplePicker) and then send a push notification to that friend's device.
Now i have set up a relationship with the User and the installation object with this code:
PFInstallation *installation = [PFInstallation currentInstallation];
installation[#"user"] = [PFUser currentUser];
[installation saveInBackground];
And this created a pointer to the users ObjectId
So my question is how would i create a query and send a push notification to a number retrieved from that Users friend list?. I am having a hard time to connect how i can get from a phone number to the installation device that i need to send the notification to. If you could provide help in javascript since i read it is safer to send it through cloud code!
Also a subquestion mentioned above if i need to make the anonymous user to a real user.
many thanks!!
I'd recommend subscribing each user to their own channel where the channel name is equal to their phone number (Subscription can't be done in Javascript):
NSString *userPhoneNumber = ...
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
// "x" added to the beginning of the userPhoneNumber since
// Parse doesn't allow channels to begin with a number
[currentInstallation addUniqueObject:[NSString stringWithFormat:#"x%#", userPhoneNumber] forKey:#"channels"];
[currentInstallation saveInBackground];
That way no query is required before pushing the notification:
var friendPhoneNumber = ...
var friendChannel = "x" + friendPhoneNumber;
Parse.Push.send({
channels: [ friendChannel ],
data: {
alert: message
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});

meteor mongodb _id changing after insert (and UUID property as well)

I have meteor method that does an insert.
Im using Regulate.js for form validation.
I set the game_id field to Meteor.uuid() to create a unique value that I also route to /game_show/:game_id using iron router.
As you can see I'm logging the details of the game, this works fine. (image link to log below)
file: /lib/methods.js
Meteor.methods({
create_game_form : function(data){
Regulate.create_game_form.validate(data, function (error, data) {
if (error) {
console.log('Server side validation failed.');
} else {
console.log('Server side validation passed!');
// Save data to database or whatever...
//console.log(data[0].value);
var new_game = {
game_id: Meteor.uuid(),
name : data[0].value,
game_type: data[1].value,
creator_user_id: Meteor.userId(),
user_name: Meteor.user().profile.name,
created: new Date()
};
console.log("NEW GAME BEFORE INSERT: ", new_game);
GamesData.insert(new_game, function(error, new_id){
console.log("GAMES NEW MONGO ID: ", new_id)
var game_data = GamesData.findOne({_id: new_id});
console.log('NEW GAME AFTER INSERT: ', game_data);
Session.set('CURRENT_GAME', game_data);
});
}
});
}
});
All of the data coming out of the console.log at this point works fine
After this method call the client routes to /game_show/:game_id
Meteor.call('create_game_form', data, function(error){
if(error){
return alert(error.reason);
}
//console.log("post insert data for routing variable " ,data);
var created_game = Session.get('CURRENT_GAME');
console.log("Session Game ", created_game);
Router.go('game_show', {game_id: created_game.game_id});
});
On this view, I try to load the document with the game_id I just inserted
Template.game_start.helpers({
game_info: function(){
console.log(this.game_id);
var game_data = GamesData.find({game_id: this.game_id});
console.log("trying to load via UUID ", game_data);
return game_data;
}
});
sorry cant upload images... :-(
https://www.evernote.com/shard/s21/sh/c07e8047-de93-4d08-9dc7-dae51668bdec/a8baf89a09e55f8902549e79f136fd45
As you can see from the image of the console log below, everything matches
the id logged before insert
the id logged in the insert callback using findOne()
the id passed in the url
However the mongo ID and the UUID I inserted ARE NOT THERE, the only document in there has all the other fields matching except those two!
Not sure what im doing wrong. Thanks!
The issue is your code is running on the client side (or at least it looks like from the screenshot).
In meteor, Meteor.methods that run on the client side are simulation stubs. What this means is you put stuff in there that creates 'fake' data so that you can avoid the user feeling latency. This is because it would take 1-4 seconds for the server to reply with what was actually inserted in the database. This isn't really an issue though.
The reason this causes you trouble is the method is run twice (one on the server and one on the client), so it generates two different Meteor.uuids since they are random. So this is why you have the inconsistency. What you see is the 'fake' one initially, then the server sends down the real one.
This is how Meteor makes it look like data has been inserted instantly, even though its not fully yet inserted.
To fix this get rid of the the .method you have on the client so that you only have one running on the server. You would need to get the game_id from the server though and not from the client.
If you want to keep the latency compensation, pass the Meteor.uuid in data like you do your other form data. This way the game_id will be consistent on both the server and client.

Categories

Resources