I need help, I've been searching for solutions all day but I can't fix my issue, the code below won't read the device tokens.
Below contains my db structure. I manage to receive the log: 'We have a new News for you.' When I added a new post but I received the log "There are no notification tokens to send to." Which means it cannot detect the device tokens even though there is already ones. What am I doing wrong?
{
"Newsv2" : {
"All" : {
"-Ktr7ZkuChCjsUIMb_4f" : {
"title" : "",
"type" : "",
}
},
"Usersv2" : {
"h0RzzpdO7nZVLpAR4fi7xRWUqsT2" : {
"device_token" : "",
"name" : "",
"user_no" : ""
}
},
}
/--News
--All
--name
--desc
/--Usersv2
--{userID}
--device_token
exports.sendNotif = functions.database.ref('/Newsv2/All/{newsID}').onWrite(event => {
const newsID = event.params.newsID;
const userID = event.params.userID;
if (!event.data.val()) {
return console.log('News! ', newsID);
}
console.log('We have a new News for you!',newsID);
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref(`/Usersv2/${userid}/device_token`).once('value');
return Promise.all([getDeviceTokensPromise]).then(results => {
const tokensSnapshot = results[0];
//const follower = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
//console.log('Fetched follower profile', follower);
// Notification details.
const payload = {
notification: {
title: 'Test Message',
body: '',
icon: ''
}
};
// Listing all tokens.
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.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);
});
});
});
To get the device token I store it in my firebase DB when a user registers or logs in.
private DatabaseReference mUserDatabase;
mUserDatabase = FirebaseDatabase.getInstance().getReference().child("Users/");
//and if the login/register is successful
mUserDatabase.child("device_token").setValue(deviceToken).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Intent intent = new Intent(application.getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |Intent.FLAG_ACTIVITY_NEW_TASK);
application.startActivity(intent);
}
});
as for my firebase funciton:
const deviceToken = admin.database().ref(`/Users/${unique_id}/device_token`).once('value');
Related
I am trying to send a notification to an android device using firebase cloud messaging.
Below is sendNotification which is a cloud function I have deployed on firebase:
const sendNotification = (owner_uid: any, type: any) => {
return new Promise((resolve, reject) => {
admin.firestore().collection('users').doc(owner_uid).get().then((doc) => {
if (doc.exists && doc.data()?.token) {
if (type === 'new_comment') {
console.log('NEW COMMENT');
console.log('TOKEN: ' + doc.data()?.token);
admin.messaging().sendToDevice(doc.data()?.token, {
data: {
title: 'A new comment has been made on your post',
}
}).then((sent) => {
console.log("SENT COUNT " + sent.successCount);
console.log('SENT APPARENTLY')
resolve(sent);
});
}
}
});
});
}
And here is where I'm calling this function:
export const updateCommentsCount = functions.firestore.document('comments/{commentId}').onCreate(async (event) => {
const data = event.data();
const postId = data?.post;
const doc = await admin.firestore().collection('posts').doc(postId).get();
if (doc.exists) {
let commentsCount = doc.data()?.commentsCount || 0;
commentsCount++;
await admin.firestore().collection('posts').doc(postId).update({
'commentsCount': commentsCount
})
return sendNotification(doc.data()?.owner, 'new_comment');
} else {
return false;
}
})
However, I'm not receiving a notification on the android device.
And here are the cloud function logs when I leave a comment:
Can someone please tell me why is happening, & how it can be resolved? I can show further code if required.
I managed to find the solution.
In the notification sending method, sendToDevice, I updated the key "data", to "notification" and the notification is now being automatically sent & displayed on the original user's device.
Here is the updated
admin.messaging().sendToDevice(doc.data()?.token, {
notification: {
title: 'A new comment has been made on your post',
}
I am trying to send notifications to an Android app's users with firebase cloud messaging. I am using cloud firestore triggers but when trying to access a user node's properties, they are undefined.
Here is my index.js :
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.notifyNewMessage = functions.firestore
.document('conversations/{conversation}/messages/{message}')
.onCreate((docSnapshot, context) => {
const message = docSnapshot.data();
const recipientId = message['recipientId'];
const senderName = message['senderName'];
return admin.firestore().collection("users").doc(recipientId).get().then(userDoc => {
const registrationTokens = userDoc.registrationTokens;
console.log("registrationTokens = "+ registrationTokens);
const notificationBody = message['message'];
const payload = {
notification : {
title : senderName + " sent you a message,",
body: notificationBody,
clickAction: "ConversationActivity"
},
data : {
contactName : senderName,
userId : message['senderId']
}
}
return admin.messaging().sendToDevice(registrationTokens, payload).then(response => {
const stillRegisteredTokens = registrationTokens;
response.results.forEach((result, index) => {
const error = result.error;
if (error){
const failedRegistrationToken = stillRegisteredTokens['index'];
if (error.code === 'messaging/invalid-registration-token'
|| error.code === 'messaging/registration-token-not-registered') {
const failedIndex = stillRegisteredTokens.indexOf(failedRegistrationToken)
if (failedIndex > -1) {
stillRegisteredTokens.splice(failedIndex, 1);
}
}
}
})
return admin.firestore().doc("users/" + recipientId).update({
registrationTokens: stillRegisteredTokens
})
})
})
})
Because of that I get an error "sendDevice() argument must be non-empty array or non null string"
UPDATE
registrationTokens were undefined because I called userDoc instead of userDoc.data()
Now registrationTokens is not null nor empty but I still get the error :
Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
Just trying to impletment Stripe Payment into my Android App.
The trouble i have is that my cloud function is triggered twice when i enter a credit card in my app. the first trigger returns an "error" status and the second trigger returns an "ok" status
Here is the code i use to save the Stripe token to my firebase realtime database:
if (cardToSave != null) {
stripe.createToken(
cardToSave,
object:TokenCallback {
override fun onSuccess(token: Token?) {
val currentUser = FirebaseAuth.getInstance().currentUser?.uid
val database = FirebaseDatabase.getInstance()
val pushId = database.getReference("stripe_customers/$currentUser/sources/").push().key
val ref = database.getReference("stripe_customers/$currentUser/sources/$pushId/token/")
//save the token id from the "token" object we received from Stripe
ref.setValue(token?.id)
.addOnSuccessListener {
Log.d(TAG, "Added Stripe Token to database successfully")
}
.addOnFailureListener {
Log.d(TAG, "Failed to add Token to database")
}
}
...
Here is the cloud function i copied straight from Stripe's example in their github repo:
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database
exports.addPaymentSource = functions.database
.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((change, context) => {
const source = change.after.val();
if (source === null){
return null;
}
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source});
}).then((response) => {
return change.after.ref.parent.set(response);
}, (error) => {
return change.after.ref.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
Any help would be appreciated!
EDIT:
index.js
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const logging = require('#google-cloud/logging');
//functions.config() is firebase's environment variables
const stripe = require('stripe')(functions.config().stripe.token);
const currency = functions.config().stripe.currency || 'USD';
// [START chargecustomer]
// Charge the Stripe customer whenever an amount is written to the Realtime database
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}')
.onCreate((snap, context) => {
const val = snap.val();
// Look up the Stripe customer id written in createStripeCustomer
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
// Create a charge using the pushId as the idempotency key
// protecting against double charges
const amount = val.amount;
const idempotencyKey = context.params.id;
const charge = {amount, currency, customer};
if (val.source !== null) {
charge.source = val.source;
}
return stripe.charges.create(charge, {idempotency_key: idempotencyKey});
}).then((response) => {
// If the result is successful, write it back to the database
return snap.ref.set(response);
}).catch((error) => {
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with StackDriver
return snap.ref.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// [END chargecustomer]]
// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
return stripe.customers.create({
email: user.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${user.uid}/customer_id`).set(customer.id);
});
});
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database
exports.addPaymentSource = functions.database
.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((change, context) => {
const source = change.after.val();
if (source === null){
return null;
}
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source:source});
}).then((response) => {
return change.after.ref.parent.set(response);
}, (error) => {
return change.after.ref.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// When a user deletes their account, clean up after them
exports.cleanupUser = functions.auth.user().onDelete((user) => {
return admin.database().ref(`/stripe_customers/${user.uid}`).once('value').then(
(snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.del(customer.customer_id);
}).then(() => {
return admin.database().ref(`/stripe_customers/${user.uid}`).remove();
});
});
// To keep on top of errors, we should raise a verbose error report with Stackdriver rather
// than simply relying on console.error. This will calculate users affected + send you email
// alerts, if you've opted into receiving them.
// [START reporterror]
function reportError(err, context = {}) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
const logName = 'errors';
const log = logging.log(logName);
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
const metadata = {
resource: {
type: 'cloud_function',
labels: {function_name: process.env.FUNCTION_NAME},
},
};
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
// Write the error log entry
return new Promise((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
}
// [END reporterror]
// Sanitize the error message for the user
function userFacingMessage(error) {
return error.type ? error.message : 'An error occurred, developers have been alerted';
}
My application is simply order app and I want admin get notification when new order is placed.
So to do that I send notification to device with token Id by using firebase functions.
'use-strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('admin/{user_id}/notification/{notification_id}').onCreate((snapshot, context) => {
console.log("hehehehehehehehe");
const user_id=context.params.user_id;
const getDeviceTokensPromise = admin.database().ref(`admin/${user_id}`).once('value');
//let tokensSnapshot;
//let tokes;
return Promise.all([getDeviceTokensPromise]).then(result => {
//const registrationToken= result[0].tokenid.val();
//console.log(registrationToken);
const payload = {
notification: {
title: 'You have a new follower!',
body: 'Yeni sifaris var',
}
};
return admin.messaging().sendToDevice('fOCU86Rhhb4:APA91bHwJZInZYGp9cIDc7PyCw48QcEvvMOMuFepYcCTvkxlCJp8_Ieq1Ikwd9xNoU2rfTA9paRqCTLAuhUlZgF952AvpBstGdGRWMK8lCR2MHgHn6xzbvyxFEu-auRYexnPYmOnlTB1',payload).then((response) => {
return console.log('Successfully sent message:', response);
}).catch((error) => {
return console.log('Error sending message:', error);
});
});
});
Although I got the success response message in console log, my android phone couldn't get any notification.
Successfully sent message: { results: [ { messageId: '0:1542910779596746%095eb9af095eb9af' } ],
canonicalRegistrationTokenCount: 0,
failureCount: 0,
successCount: 1,
multicastId: 6551370257673400000 }
What is wrong with my code. I am new to firebase. I need your help.
Below code is my firebase function index.js file
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref("Notifications/{userId}")
.onWrite(event => {
var request = event.data.val();
var payload = {
data: {
title: "Welcome to ChitChat Group",
message: "You may have new messages"
}
};
admin.messaging().sendToDevice(request.token, payload)
.then(function (response) {
console.log("Successfully sent message: ", response);
})
.catch(function (error) {
console.log("Error sending message: ", error);
})
});
below code contains where i crate token when user get registered
String uid = (String) firebaseAuth.getCurrentUser().getUid();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Notifications/"+uid);
reference.child("token").setValue(FirebaseInstanceId.getInstance().getToken());
You can make all your users subscribe to one big topic like registration on newMembers then send notification to the topic :
var message = {
to: '/topics/newMembers',
notification: {
title: 'title',
body: 'body'
},
};