Get "Undefined" for snapshot values - node.js

log result
inside of the log error
I am currently in the process of setting up my Firebase Cloud functions to have a user to user push notification everytime there is a new child added to "Messages". For each user, there is a notification stored in a node in this structure "/User/UID/Token". However, in my logs in the Firebase console it turns up that values being returned is "Undefined". This is my first time working with Node.js so everything is very new. Any help would be appreciated. Here is what is inside of function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Listens for new messages added to messages/:pushId
exports.messageWritten = functions.database.ref('/messages/{pushId}').onWrite( event => {
console.log('Push notification event triggered');
// Grab the current value of what was written to the Realtime Database.
var valueObject = event.data.val();
console.log(valueObject.text,valueObject.toId);
return admin.database().ref(`/Users/${valueObject.toId}`).once('value').then(snapshot =>{
console.log('the users name',snapshot.name)
console.log('the users token',snapshot.token)
})
// Create a notification
const payload = {
notification: {
title:snapshot.name +' sent you a message',
body: valueObject.text,
sound: "default"
},
};
//Create an options object that contains the time to live for the notification and the priority
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
return admin.messaging().sendToDevice(snapshot.token, payload, options);
});

You have two return statements in your function. The code after the first return statement (which sends the message) won't be run.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Listens for new messages added to messages/:pushId
exports.messageWritten = functions.database.ref('/messages/{pushId}').onWrite(event => {
console.log('Push notification event triggered');
// Grab the current value of what was written to the Realtime Database.
var valueObject = event.data.val();
console.log(valueObject.text, valueObject.toId);
return admin.database().ref(`/Users/${valueObject.toId}`).once('value').then(snapshot => {
console.log('the users name', snapshot.val().name)
console.log('the users token', snapshot.val().token)
// Create a notification
const payload = {
notification: {
title: snapshot.val().name + ' sent you a message',
body: valueObject.text,
sound: "default"
},
};
//Create an options object that contains the time to live for the notification and the priority
const options = {
priority: "high",
timeToLive: 60 * 60 * 24
};
return admin.messaging().sendToDevice(snapshot.val().token, payload, options);
})
});

Related

Firebase realtime database get wildcard data

I'm trying to send a notification to users whenever their message receives a new reply. However, in the firebase cloud functions logs it is returning errors and not sending a notification. Here is the error:
TypeError: Cannot read properties of undefined (reading 'uid')
Here is my function:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
exports
.sendNewTripNotification = functions
.database
.ref("messagepool/{uid}/responses/")
.onWrite((event)=>{
const messageid = event.params.uid;
// console.log('User to send notification', uuid);
const ref = admin.database().ref(`messagepool/${messageid}/author`);
return ref.once("value", function(snapshot) {
const ref2 = admin.database().ref(`users/${snapshot.val()}/token`);
return ref2.once("value", function(snapshot2) {
const payload = {
notification: {
title: "💌 New Reply",
body: "You have received a new reply to your message!",
},
};
admin.messaging().sendToDevice(snapshot2.val(), payload);
}, function(errorObject) {
console.log("The read failed: " + errorObject.code);
});
}, function(errorObject) {
console.log("The read failed: " + errorObject.code);
});
});
Am I reading the wildcard uid incorrectly? Why is this happening?
The function in onWrite() takes 2 parameters - change that is a DataSnapshot and context which contains the params you are looking for. Try refactoring the code as shown below:
exports
.sendNewTripNotification = functions
.database
.ref("messagepool/{uid}/responses/")
.onWrite((change, context) => {
const { uid } = context.params;
console.log('UID:', uid);
})

Send user notification every hour with Bot Framework NodeJS

I am developing a bot that sends users a message every hour on skype. It makes an API call and then sends a message containing the parsed message. I started with the welcomebot sample. I then used the switch statement to fire a setInterval()to send the message activity when the user sends a certain message but the message keeps sending even after I call clearinterval() I set to a different string. How can I make clearInterval() work? Or am I going about it the wrong way?
Index.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required packages
const path = require('path');
const restify = require('restify');
const axios = require('axios');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter, UserState, ConversationState, MemoryStorage } = require('botbuilder');
const { WelcomeBot } = require('./bots/welcomeBot');
// Read botFilePath and botFileSecret from .env file
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create bot adapter.
// See https://aka.ms/about-bot-adapter to learn more about bot adapter.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppID,
appPassword: process.env.MicrosoftAppPassword
});
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
};
// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage.
// A bot requires a state store to persist the dialog and user state between messages.
// For local development, in-memory storage is used.
// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot
// is restarted, anything stored in memory will be gone.
const memoryStorage = new MemoryStorage();
const userState = new UserState(memoryStorage);
const conversationState = new ConversationState(memoryStorage);
// Create the main dialog.
const conversationReferences = {};
const bot = new WelcomeBot(userState, conversationReferences, conversationState);
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
// Listen for incoming activities and route them to your bot main dialog.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// route to main dialog.
await bot.run(context);
});
});
const getBalance = async () => {
try {
return await axios.get('https://balance.com/getbalance', { //this returns a json object
headers: {
Authorization: xxxxx
}
});
} catch (error) {
console.error(error);
}
};
const retrieveBalance = async () => {
const balance = await getBalance();
if (balance.data.message) {
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async turnContext => {
// If you encounter permission-related errors when sending this message, see
// https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity(balance.data.message);
});
}
}
};
module.exports.retrieveBalance = retrieveBalance;
Bot.js:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Import required Bot Framework classes.
const { ActionTypes, ActivityHandler, CardFactory, TurnContext } = require('botbuilder');
const balance = require('../index.js');
// Welcomed User property name
const CONVERSATION_DATA_PROPERTY = 'conversationData';
class WelcomeBot extends ActivityHandler {
/**
*
* #param {UserState} //state to persist boolean flag to indicate
* if the bot had already welcomed the user
*/
constructor(userState, conversationReferences, conversationState) {
super();
// Creates a new user property accessor.
// See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors.
this.userState = userState;
this.conversationReferences = conversationReferences;
this.conversationDataAccessor = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// The state management objects for the conversation and user state.
this.conversationState = conversationState;
this.onConversationUpdate(async (context, next) => {
this.addConversationReference(context.activity);
await next();
});
this.onMessage(async (context, next) => {
this.addConversationReference(context.activity);
// Add message details to the conversation data.
// Display state data.
// Read UserState. If the 'DidBotWelcomedUser' does not exist (first time ever for a user)
// set the default to false.
// const didBotWelcomedUser = await this.welcomedUserProperty.get(context, false);
// Your bot should proactively send a welcome message to a personal chat the first time
// (and only the first time) a user initiates a personal chat with your bot.
// if (didBotWelcomedUser === false) {
// // The channel should send the user name in the 'From' object
// await context.sendActivity( 'This bot will send you your paystack balance every 5 minutes. To agree reply \'ok\'');
// // Set the flag indicating the bot handled the user's first message.
// await this.welcomedUserProperty.set(context, true);
// }
// This example uses an exact match on user's input utterance.
// Consider using LUIS or QnA for Natural Language Processing.
var text = context.activity.text.toLowerCase();
var myint;
switch (text) {
case 'ok':
myint = setInterval(async () => {
await balance.retrieveBalance();
}, 1000);
// eslint-disable-next-line template-curly-spacing
break;
case 'balance':
await balance.retrieveBalance();
break;
case 'no':
clearInterval(myint);
break;
case 'hello':
case 'hi':
await context.sendActivity(`You said "${ context.activity.text }"`);
break;
case 'info':
case 'help':
await this.sendIntroCard(context);
break;
default:
await context.sendActivity('This is a simple notification bot. ');
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
// Sends welcome messages to conversation members when they join the conversation.
// Messages are only sent to conversation members who aren't the bot.
this.onMembersAdded(async (context, next) => {
// Iterate over all new members added to the conversation
for (const idx in context.activity.membersAdded) {
// Greet anyone that was not the target (recipient) of this message.
// Since the bot is the recipient for events from the channel,
// context.activity.membersAdded === context.activity.recipient.Id indicates the
// bot was added to the conversation, and the opposite indicates this is a user.
if (context.activity.membersAdded[idx].id !== context.activity.recipient.id) {
await context.sendActivity('Welcome to the Nag Bot. If you want to get nagged with your balance type \'ok\', if you want your balance alone type \'balance. for info type \'info\'');
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);
// Save state changes
await this.userState.saveChanges(context);
await this.conversationState.saveChanges(context, false);
}
addConversationReference(activity) {
const conversationReference = TurnContext.getConversationReference(activity);
this.conversationReferences[conversationReference.conversation.id] = conversationReference;
}
async sendIntroCard(context) {
const card = CardFactory.heroCard(
'Welcome to Nag Bot balance checker!',
'Welcome to Paystack Nagbot.',
['https://aka.ms/bf-welcome-card-image'],
[
{
type: ActionTypes.OpenUrl,
title: 'Open your dashboard',
value: 'https://dashboard.paystack.com'
},
{
type: ActionTypes.OpenUrl,
title: 'Ask a question on twitter',
value: 'https://twitter.com/paystack'
},
{
type: ActionTypes.OpenUrl,
title: 'View docs',
value: 'https://developers.paystack.co/reference'
}
]
);
await context.sendActivity({ attachments: [card] });
}
}
module.exports.WelcomeBot = WelcomeBot;

Realtime database function triggers constantly

I have deployed a JS function for Firebase realtime database triggers. In its operations, it should send a push notification just on value update in the database, which is dead simple:
{
"rollo" : "yes"
}
If value changes to yes it should trigger notification. If it goes to "no" then it should do nothing. Here is the JS function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendNewPostNotif = functions.database.ref('/rollo').onUpdate((change, context) => {
console.log('Push notification event triggered');
const beforeData = change.before.val();
const payload = {
notification: {
title: 'Push triggered!',
body: "Push text",
sound: "default"
}
};
const options = {
priority: "high",
timeToLive: 60 * 10 * 1
};
return admin.messaging().sendToTopic("notifications", payload, options);
});
Also even though I have set TTL, each value change sends another push notification.
Any ideas?
I would try something like this:
exports.sendNewPostNotif = functions.database.ref('/rollo').onWrite((change, context) => {
const newData = change.after.val();
const oldData = change.before.val();
const payload = {
notification: {
title: 'Push triggered!',
body: "Push text",
sound: "default"
}
};
const options = {
priority: "high",
timeToLive: 60 * 10 * 1
};
if (newData != oldData && newData == 'yes') {
return admin.messaging().sendToTopic("notifications", payload, options);
}
});
onUpdate():
triggers when data is updated in the Realtime Database.
When you update it to "no" it will send a notification and when you update it to "yes" it will also send a notification.
https://firebase.google.com/docs/functions/database-events

Promise not returning value on request

I have been trying to get this to work, but am new to NodeJS. I suspect the issue is due to async, but am not familiar with how it works.
The idea behind this code is that it monitors a firebase database change and sends an email to the users. I am getting everything from the change snapshot, and using the values to check another table for user data. The request is not returning before the email gets sent and I am unsure why.
Edit I should specify that the email function sgMail is firing off before I get the results from the requests. I've tried putting a delay, but I am still not getting the result to return in time.
Here's my index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
var requestify = require('requestify');
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database.ref('Order/{orderID}')
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = 'https://shlep-me-f516e.firebaseio.com/User/'+shipperInfo+'.json';
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
});
function getTravelerData() {
return new Promise(resolve => {
requestify.get('https://shlep-me-f516e.firebaseio.com/User/' + travelerInfo + '.json')
.then(function (response) {
resolve(response.getBody())
});
});
}
var TravelD = getTravelerData();
//Send an email
const msg = {
to: 'andrew#shlepme.com',
from: 'support#shlepme.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'd1ccfeb9-2e2d-4979-a3ca-c53975fe486e',
substitutionWrappers: ['%', '%'],
substitutions: {
'%shipper_name%': "Test",
'traveler_name': TravelD.name
// and other custom properties here
}
};
console.log('Sending email');
console.log(TravelD);
return sgMail.send(msg)
});
Any ideas? I have been trying to figure this out.
It seems that you need to understand about Promises first.
When you start using promises you will need to ALWAYS use them and chain one with the other.
So I would rewrite your code like this: (not tested)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require("firebase-functions");
var requestify = require("requestify");
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database
.ref("Order/{orderID}")
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = "https://shlep-me-f516e.firebaseio.com/User/" + shipperInfo + ".json";
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
var shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
})
.then(function (shipperResult) {
//Send an email
const msg = {
to: "andrew#shlepme.com",
from: "support#shlepme.com",
subject: "New Follower",
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: "d1ccfeb9-2e2d-4979-a3ca-c53975fe486e",
substitutionWrappers: ["%", "%"],
substitutions: {
"%shipper_name%": "Test",
traveler_name: shipperResult.name
// and other custom properties here
}
};
console.log("Sending email");
console.log(shipperResult);
return sgMail.send(msg);
});
});

Cloud Functions Error Firebase

I am trying to do push notification through Functions in Firebase.
Here is my code in Node.JS
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('Received Downs/{owner}/{propID}')
.onCreate(event => {
// get the owner name and propID
var owner = event.params.owner;
var propID = event.params.propID;
// Log it
console.log('Owner: ' + owner + ' Property ID: ' + propID);
// Get the list of device notification tokens.
return admin.database().ref(`/users/${owner}`).once('value', snapshot => {
var ownerID = snapshot.val();
// This will find requester ID
return admin.database().ref(`/Received Downs/${owner}/${propID}`).once('value', snapshot => {
// First will find the property the requester downed
var property = snapshot.val();
// Find the requester's name
return admin.database().ref('/Users Info/' + property.downedBy).once('value', snapshot => {
// Requester's ID
var downedBy = snapshot.val();
// Notification details.
const payload = {
notification: {
title: 'You have a new request!',
body: `${downedBy.name} is now following you.`,
sound: 'default'
}
};
// Listing all tokens. (the function save the keys of given variable)
// const tokens = Object.keys(getDeviceTokens.val());
// var fcmToken = "dzJLM-JdIt8:APA91bHBJJP6t3Z0_T7kEFDrLLsu5T_NpYsR6QmJz2EJhpK88SV1ZfemoyCtC_6hl3_0sCPdzkvlQFoAFhlWn4xTQOY3k5P8JMvdYFyeNBN1lHceQtytE0y-9oTP6qgKspi9p9E8V9dB";
// Send to all tokens of a device
admin.messaging().sendToDevice(ownerID.token, payload)
.then(response => {
console.log("Successfully sent message:", response);
}).catch(function(error) {
console.log("Error sending message:", error);
});
})
})
})
})
And here is what I got in LOGS at Firebase Functions
When I used a variable that has fem token , typed, it works fine, but not when i fetched from Firebase Realtime Database. Anyone could tell me why?
The problem I had the wrong path return admin.database().ref(/users/${owner})

Resources