How to retrieve all user tokens and send FCM to all - node.js

I am trying to send a FCM to multiple users at a time. I can't seem to find a clear answer other than using a topic but i have several conditions which make this quite difficult on my side. Is it at all possible to extract all tokens from a particular document (similar to setting it up as a topic but based on my logic) and pass that array to the Cloud function?
My current code is as follows and it works for a single user (i am only posting the part where the token is extracted):
return admin.firestore().doc('Seller_tokens/tokens/' + brand + '/wc').get().then(usertokensdoc =>{
const sellertokenID = usertokensdoc.get("dXufWMvOTLXUOyj8XNv9NFmsQ4x1");
const payload = {
data:{
title:'FCM - Test',
content: 'FCM - This is a test message,
},
token: sellertokenID
};
for the line:
const sellertokenID = usertokensdoc.get("dXufWMvOTLXUOyj8XNv9NFmsQ4x1");
I do not want to specify a single userid, where the 'userid' is the field in the document . And hence I would like to pass an array, if possible.

As you mentioned and as clarified in the official documentation Send messages to multiple devices, there are only two ways of sending messages to multiple tokens:
Firebase Cloud Messaging provides these two ways to target a message to multiple devices:
Topic messaging, which allows you to send a message to multiple devices that have opted in to a particular topic.
Device group messaging, which allows you to send a message to multiple devices that belong to a group you define.
In addition to this, as mentioned in this other question here, there is no API for you to get all tokens at once, so, you will need to get the individually for usage.
So, to summarize, there is not an automatic way of getting the tokens, but once you get them, I believe using the Device Messaging way would be better for you, since you can define specific groups to receive the FCM, per tokens that you retrieved.
Let me know if the information helped you!

you can retrieve the token documents based on your conditions in cloud function. loop through them, create an array of tokens and pass them to Firebase messaging as below
admin.messaging.sendToDevice(tokens, payload). below url has a very good example.
FCM - Cloud Functions Example

Related

Azure Communication Service - pre create a video meeting for specified date and time and record the link

Azure Communication Service -
Is there a way we can pre create a video meeting for specified date and time and record the link so that it can be sent to the participants. and invoked at specified time
I have a solution, although it's not the best solution. It IS a solution.
MS has offered a JS option that wraps the react components.
https://github.com/Azure/communication-ui-library/tree/main/samples/StaticHtmlComposites
Directly from the sample,
const callAdapter = await callComposite.loadCallComposite({
containerId: 'video-call',
groupId: '', // Provide any GUID to join a group
displayName: displayName,
userId: user,
token: token
});
in my testing I was able to plug in any randomly generated GUID, and share a link with that guid to my co-workers who were able to join the video call.
Hope that helps lead to a better solution. I'm still trying to get this thing working friendly with MVC. I may update when I get that working.

Is there an anonymous ID in Actions on Google with Dialogflow?

Is there an anonymous ID in Actions on Google with Dialogflow that I can access using DialogFlow in Node.js?
I don't need to know the Google account of who is using the Action, but I do want to have a unique identifier so that the Action can know when they come back.
Google no longer provides one for you. You will have to generate one when a new user interacts with your webhook and store the generated id in their user storage object.
To identify a new user your just check if they already have an id in their user storage object. For generating the id you can use an library like uuid. https://www.npmjs.com/package/uuid
Uuidv4 is probably the one that you need if you just need a unique id for simple identifications
The original idea from Google was to leverage a field called userStorage, but this feature seems to be borked ATM.
userStorage Documentation:
https://developers.google.com/actions/assistant/save-data
Reddit thread regarding issues:
https://www.reddit.com/r/GoogleAssistantDev/comments/d88z7e/userstorage_saga_continued/
Unless something has changed (I haven't checked on userStorage since I've been busy writing a fix around it) you may be out of luck without Account Linking. Feel free to try userStorage and keep me honest as they may have remedied the situation internally.
Alternatively, if all you need is an identifier or session for a single conversation you can leverage the conversationId which will be unique until the conversation ends.
I've found a possible option...
(When working in DialogFlow in Node.js, most code is in a handler and the parameter is usually called conv. The following assumes that it is inside such a handler.)
On every single call, check for an 'existing' id in the session data and the user storage:
var id = conv.data.MyId || conv.user.storage.MyId || '';
if(!id) {
id = /* make a new Id for this user... a GUID or some other unique id */
conv.user.storage.MyId = id;
}
Once I get the Id from storage or make a new one, it is critical to reassign it to conv.data, since conv.user.storage seems to be reliably provided only on the first call!
// IMPORTANT
conv.data.MyId = id;
/* use the Id as needed */
My code looks up the Id in a firebase database to get details from their last visit.
This seems to be working, but may not be reliable.

Stripe - create / retrieve customer in one call

Is there a stripe API call that we can use to create a user if they don't exist, and retrieve the new user?
say we do this:
export const createCustomer = function (email: string) {
return stripe.customers.create({email});
};
even if the user with that email already exists, it will always create a new customer id. Is there a method that will create a user only if the user email does not exist in stripe?
I just want to avoid a race condition where more than one stripe.customers.create({email}) calls might happen in the same timeframe. For example, we check to see if customer.id exists, and does not, two different server requests could attempt to create a new customer.
Here is the race condition:
const email = 'foo#example.com';
Promise.all([
stripe.customers.retrieve(email).then(function(user){
if(!user){
return stripe.customers.create(email);
}
},
stripe.customers.retrieve(email).then(function(user){
if(!user){
return stripe.customers.create(email);
}
}
])
obviously the race condition is more likely to happen in two different processes or two different server requests, than the same server request, but you get the idea.
No, there is no inbuilt way to do this in Stripe. Stripe does not require that a customer's email address be unique, so you would have to validate it on your side. You can either track your users in your own database and avoid duplicates that way, or you can check with the Stripe API if customers already exist for the given email:
let email = "test#example.com";
let existingCustomers = await stripe.customers.list({email : email});
if(existingCustomers.data.length){
// don't create customer
}else{
let customer = await stripe.customers.create({
email : email
});
}
Indeed it can be solved by validating stripe's customer data retrieval result against stored db.
And then call another API to create afterward.
However for simplicity sake, i agree with #user7898461 & would vouch for retrieveOrCreate customer api :)
As karllekko's comment mentions, Idempotent Keys won't work here because they only last 24 hours.
email isn't a unique field in Stripe; if you want to implement this in your application, you'll need to handle that within your application - i.e., you'll need to store [ email -> Customer ID ]s and do a lookup there to decide if you should create or not.
Assuming you have a user object in your application, then this logic would be better located there anyways, as you'd also want to do this as part of that - and in that case, every user would only have one Stripe Customer, so this would be solved elsewhere.
If your use case is like you don't want to create a customer with the same email twice.
You can use the concept of stripe idempotent request. I used it to avoid duplicate charges for the same order.
You can use customer email as an idempotent key. Stripe handles this at their end. the two request with same idempotent key won't get processed twice.
Also if you want to restrict it for a timeframe the create an idempotent key using customer email and that time frame. It will work.
The API supports idempotency for safely retrying requests without
accidentally performing the same operation twice. For example, if a
request to create a charge fails due to a network connection error,
you can retry the request with the same idempotency key to guarantee
that only a single charge is created.
You can read more about this here. I hope this helps

How do I save and retrieve information across invocations of my agent in Dialogflow?

I would like my Actions on Google agent to store and retrieve certain pieces of information across invocations - like a cookie. How do I do this?
You have a lot of options on how you want to do this, depending on exactly what you're trying to do. It isn't exactly like a web cookie, although there are similarities.
If you want the equivalent of a session cookie, information that is retained during a single conversation, then your options are
Using the Session ID provided as part of the information sent to you on each invocation and tracking this in your fulfillment.
Storing information you want retained using a Dialogflow context
If you are using the actions-on-google JavaScript library, storing this in the app.data object created for you.
If you want the equivalent of a long-lasting cookie to retain information between conversations then your options are
Using the anonymous User ID provided as part of the information sent to you on each invocation and tracking this in your fulfillment.
If you are using the actions-on-google javascript library, storing this in the app.userStorage object created for you.
Storing it as part of the string in the JSON response under data.google.userStorage.
Some more information about each of these
Session ID
A different Session ID is created for each conversation you have. You can get this Session ID by examining the JSON sent to your webhook in the sessionId parameter.
You can then look this up in a data store of some sort that you manage.
Dialogflow context
Contexts are powerful tools that are available with Dialogflow. You return a context as part of your fulfillment webhook and indicate the name of the context, its lifetime (how many more rounds of the conversation it will be passed back to your webhook), and any parameters associated with the context (string key/value pairs).
Contexts are especially useful in helping determine what intents may be called. You can indicate what contexts must be active for an Intent to be recognized by Dialogflow.
If you're using the actions-on-google node.js library, you can set a context using something like this:
var contextParameters = {
foo: "Something foothy",
bar: "Your local bar."
};
app.setContext( "remember_this", 5, contextParameters );
You need to do this before you call app.ask() or app.tell().
Or you can do the equivalent in the JSON as part of the contextOut block of the response
"contextOut": [
{
"name": "remember_this",
"lifespan": 5,
"parameters": {
"foo": "Something foothy",
"bar": "Your local bar."
}
}
]
The next time your webhook is called, you can fetch this context either by looking at the result.contexts array or by using the app.getContext() or app.getContextArgument() methods in the library.
Using app.data
If you're using the library, Google has done some of the work for you. The app.data object is created for you. Any values you set in the object are available for the lifetime of the session - you just read them in later calls to your webhook.
(Under the covers, Google uses a context for this, so there is no magic. The two work together and you're free to do both.)
Anonymous UserID
When a user first uses your action, a user ID is generated. This ID doesn't give you access to any specific information about them, and isn't used for any other action, but every time you see it, you can be assured that it was the same user that used it on a previous occurrence. Just like a cookie, however, the user can reset it and a new ID will be generated for them for your action.
You get this from the JSON at originalRequest.user.userId or by using app.getUser().userId. Once you have it, you'd use a data store of some sort to store and retrieve information about this user.
Using app.userStorage
Similar to app.data, there is also an app.userStorage object that is created for you for each user. Any changes you make to this object are saved in between conversations you have with this user.
Unlike app.data, however, this doesn't get stored in a context. It has its own storage method. Which leads to...
Storing it in JSON
If you're not using the actions-on-google library, you still have access to userStorage through the response and request JSON directly. You need to store this as a string, but if you need to store a more complex object, a common method is to stringify it as JSON.
You'll store this value under data.google.userStorage in the response and can retrieve it under originalRequest.data.user.userStorage in the request your webhook receives.
You can save the information in Context with a key value parameter.
SAVING VALUES IN CONTEXT :
agent.set.Context({
name:'context-name',
lifespan: 5,
parameters:{
'parameter-name':'parameter-value'
}
});
GETTING VALUES FROM CONTEXT
agent.getContext('context-name');
For more Details : https://dialogflow.com/docs/contexts/contexts-fulfillment
You could also use a Google Cloud database like BigQuery or Firestore
Sounds like you may want to checkout out Account Linking: https://developers.google.com/actions/identity/account-linking. With account linking you can collect end-user information which you exchange with Google by providing a unique key. This unique key becomes part of every request you receive from Google, so when you get that unique key you lookup the information you collected from the end-user. In your case, you would store credentials or whatever key is required to access the end-user information. After the initial linking, any new data you obtain could be stored along with the original information collected, based on the unique key obtained during account linking.
For this purpose, i just did a node module just for that, in external json file from api call, i need to store and add additional informations to retrieve later. I thing that you can do a lot with this module, Store object, array, json, value, Navigation history?, back to previous page.
It work like localStorage or Cookies.
There's no limit, you can create multiple storage by name (key) an value. It's new and i'm testing it for bugs right now on my own project.
Test on Runkit
On npm
vStorage = require('virtual-storage');
vStorage.set('name', '{title:'Title 1', description:'Descriptions 1'}')
let getStorage_name = vStorage.get('name');
console.log(getStorage_name.title);
vStorage.get('name')

Is there any way to use our own server for storage of data generated using PUBNUB api? [duplicate]

I'm looking to develop a chat application with Pubnub where I want to make sure all the chat messages that are send is been stored in the database and also want to send messages in chat.
I found out that I can use the Parse with pubnub to provide storage options, But I'm not sure how to setup those two in a way where the messages and images send in the chat are been stored in the database.
Anyone have done this before with pubnub and parse? Are there any other easy options available to use with pubnub instead of using parse?
Sutha,
What you are seeking is not a trivial solution unless you are talking about a limited number of end users. So I wouldn't say there are no "easy" solutions, but there are solutions.
The reason is your server would need to listen (subscribe) to every chat channel that is active and store the messages being sent into your database. Imagine your app scaling to 1 million users (doesn't even need to get that big, but that number should help you realize how this can get tricky to scale where several server instances are listening to channels in a non-overlapping manner or with overlap but using a server queue implementation and de-duping messages).
That said, yes, there are PubNub customers that have implemented such a solution - Parse not being the key to making this happen, by the way.
You have three basic options for implementing this:
Implement a solution that will allow many instances of your server to subscribe to all of the channels as they become active and store the messages as they come in. There are a lot of details to making this happen so if you are not up to this then this is not likely where you want to go.
There is a way to monitor all channels that become active or inactive with PubNub Presence webhooks (enable Presence on your keys). You would use this to keep a list of all channels that your server would use to pull history (enable Storage & Playback on your keys) from in an on-demand (not completely realtime) fashion.
For every channel that goes active or inactive, your server will receive these events via the REST call (and endpoint that you implement on your server - your Parse server in this case):
channel active: record "start chat" timetoken in your Parse db
channel inactive: record "end chat" timetoken in your Parse db
the inactive event is the kickoff for a process that uses start/end timetokens that you recorded for that channel to get history from for channel from PubNub: pubnub.history({channel: channelName, start:startTT, end:endTT})
you will need to iterate on this history call until you receive < 100 messages (100 is the max number of messages you can retrieve at a time)
as you retrieve these messages you will save them to your Parse db
New Presence Webhooks have been added:
We now have webhooks for all presence events: join, leave, timeout, state-change.
Finally, you could just save each message to Parse db on success of every pubnub.publish call. I am not a Parse expert and barely know all of its capabilities but I believe they have some sort or store local then sync to cloud db option (like StackMob when that was a product), but even if not, you will save msg to Parse cloud db directly.
The code would look something like this (not complete, likely errors, figure it out or ask PubNub support for details) in your JavaScript client (on the browser).
var pubnub = PUBNUB({
publish_key : your_pub_key,
subscribe_key : your_sub_key
});
var msg = ... // get the message form your UI text box or whatever
pubnub.publish({
// this is some variable you set up when you enter a chat room
channel: chat_channel,
message: msg
callback: function(event){
// DISCLAIMER: code pulled from [Parse example][4]
// but there are some object creation details
// left out here and msg object is not
// fully fleshed out in this sample code
var ChatMessage = Parse.Object.extend("ChatMessage");
var chatMsg = new ChatMessage();
chatMsg.set("message", msg);
chatMsg.set("user", uuid);
chatMsg.set("channel", chat_channel);
chatMsg.set("timetoken", event[2]);
// this ChatMessage object can be
// whatever you want it to be
chatMsg.save();
}
error: function (error) {
// Handle error here, like retry until success, for example
console.log(JSON.stringify(error));
}
});
You might even just store the entire set of publishes (on both ends of the conversation) based on time interval, number of publishes or size of total data but be careful because either user could exit the chat and the browser without notice and you will fail to save. So the per publish save is probably best practice if a bit noisy.
I hope you find one of these techniques as a means to get started in the right direction. There are details left out so I expect you will have follow up questions.
Just some other links that might be helpful:
http://blog.parse.com/learn/building-a-killer-webrtc-video-chat-app-using-pubnub-parse/
http://www.pubnub.com/blog/realtime-collaboration-sync-parse-api-pubnub/
https://www.pubnub.com/knowledge-base/discussion/293/how-do-i-publish-a-message-from-parse
And we have a PubNub Parse SDK, too. :)

Resources