How to interpolate variable into firebase snapshot? - node.js

I am using firebase functions to send users a notification. I can user string interpolation like so:
const original = change.after.val();
const lover = context.auth.uid;
const recipient_username = original.username;
const topic = `${recipient_username}_notifications`;
But now I need to make a database call to get the username from the user_id, and use the username to get the value of loves form the 'original' snapshot, but this does not work:
return db.ref('/users/' + lover).once('value', (snapshot) => {
var username = snapshot.val().username;
var love = original.loves.username // I need this to use the variable username, but it is just saying "username"
console.log("lover username")
console.log(username)
console.log("loves")
console.log(loves)
const payload = {
notification:{
title: "You've got Love!",
body: `${username} sent you Love! Refresh your inbox to recieve it, and why not send some back!`
}
};
How can I change var love = original.loves.username to be something like: var love = original.loves.${username}?
The database looks like this:
users/
username: usernamehere
love/
otherusername: 10 // the amount of love they sent.

You have called .val() on the original turning this into a Javascript object.
Traversing paths in Javascript objects can be done with the .loves helper functions or using string lookups. Try the following
var username = snapshot.val().username;
var love = original.loves[username];

Related

Discord.js get user by nickname

I'm having a hard time figuring out
Is is possible to find a user by his or her nickname via the discord.js npm package.
How can I query a user by his or her nickname?
i've tried several things but nothing returns the nickname and I can't quite figure out how their documentation works.
So far I haven't been able to tell how to do it.
I've done as so in my code.
const { Client } = require('discord.js')
const discordClient = new Client()
discordClient.on('message', message => {
if (message.author.bot) return
if (message.content.startsWith('!gpwarn')) {
// The command is something like '!gpwarn thomas'.
// If his nick name is thomas but his username is john it doesn't work.
})
Yes, and it's pretty simple. Do this:
const user = client.users.cache.find(user => user.username == "The user's name");
Reference:
UserManager
To meet your exact requirements you would search the guild members cache for the provided nickname. However, I personally would suggest using either a direct tag or a UserID for this, as multiple people can have the same nickname in the server.
const U = message.guild.members.cache.find(E => E.nickname === 'NICKNAME')
Getting by Tag:
const U = message.mentions.members.first()
Getting by ID:
const U = message.guild.members.cache.find(U => U.id === 'IDHERE')
First you need to identify the nickname argument, you can do this by splitting, slicing, and joining message.content.
Next I recommend you fetch all of the GuildMembers from the guild using GuildMemberManager#fetch(), This way you don't run into the error of a member being uncached.
Handle the promise using async/await and use Collection#find() to return the member.
const { Client } = require('discord.js')
const discordClient = new Client()
discordClient.on('message', async message => {
if (message.author.bot) return
if (message.content.startsWith('!gpwarn')) {
// Returns all of the text after !gpwarn
const query = message.content.split(' ').slice(1).join(' ').toLowerCase()
const members = await message.guild.members.fetch()
const memberToWarn = members.find(m => m.nickname.toLowerCase() === query)
if (!memberToWarn) // No member found error
console.log(memberToWarn) // [GuildMember]
}
})

Using wildcards in firestore get query

I want to create a cloud function in firebase that gets triggered whenever a user logs in for the first time. The function needs to add the UID from the authentication of the specific user to a specific, already existing document in firestore. The problem is that the UID needs to be added to a document of which I do not know the location. The code I have right now doesn't completely do that, but this is the part where it goes wrong. The database looks like this when simplified
organisations
[randomly generated id]
people
[randomly generated id] (in here, a specific document needs to be found based on known email
adress)
There are multiple different organisations and it is unknown to which organisation the user belongs. I thought of using a wildcard, something like the following:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
console.log('function ready');
//Detect first login from user
//if(firebase.auth.UserCredential.isNewUser()){
if(true){
//User is logged in for the first time
//const userID = firebase.auth().currentUser.UID;
//const userEmail = firebase.auth().currentUser.email;
const userID = '1234567890';
const userEmail = 'example#example.com';
//Get email, either personal or work
console.log('Taking a snapshot...');
const snapshot = db.collection('organisations/{orgID}/people').get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.data());
});
});
}
I commented out some authentication-based lines for testing purposes. I know the code still runs, because hardcoding the orgID does return the right values. Also, looping trough every organisation is not an option, because I need to have the possibility of having a lot of organisations.
A lot of solutions are based on firestore triggers, like onWrite, where you can use wildcards like this.
However, I don't think that's possible in this case
The solution to the problem above:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
//Add UID to document in DB[FMIS-94]
//Detect first login from user
//if(firebase.auth.UserCredential.isNewUser()){
if(true){
//User is logged in for the first time
//const userID = firebase.auth().currentUser.UID;
//const userEmail = firebase.auth().currentUser.email;
const userID = '1234567890';
const userEmail = 'example#example.com';
var docFound = false;
//Get email, either personal or work
console.log('Taking a snapshot...');
//Test for work email
const snapshot = db.collectionGroup('people').where('email.work', '==', userEmail).get()
.then(function(querySnapshot){
querySnapshot.forEach(function(doc){
//work email found
console.log('work email found');
console.log(doc.data());
docFound = true;
const organisationID = doc.ref.parent.parent.id;
writeUID(doc.id, userID, organisationID);
});
});
if(!docFound){
//Test for personal email
const snapshot = db.collectionGroup('people').where('email.personal', '==', userEmail).get()
.then(function(querySnapshot){
querySnapshot.forEach(function(doc){
//personal email found
console.log('personal email found');
console.log(doc.data());
const organisationID = doc.ref.parent.parent.id;
writeUID(doc.id, userID, organisationID);
});
});
}
}
async function writeUID(doc, uid, organisationID){
const res = db.collection(`organisations/${organisationID}/people`).doc(doc).set({
userId: uid
}, { merge: true });
}
This was exactly what I needed, thanks for all your help everyone!
It is not possible to trigger a Cloud Function when a user logs in to your frontend application. There is no such trigger among the Firebase Authentication triggers.
If you want to update a document based on some characteristics of the user (uid or email), you can do that from the app, after the user has logged in.
You mention, in your question, "in here, a specific document needs to be found based on known email address". You should first build a query to find this document and then update it, all of that from the app.
Another classical approach is to create, for each user, a specific document which uses the user uid as document ID, for example in a users collection. It is then very easy to identify/find this document, since, as soon the user is logged in you know his uid.
I'm not sure I understand you correctly, but if you want to search across all people collections not matter what organizations document they're under, the solution is to use a collection group query for that.
db.collectionGroup('people').get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log("user: "+doc.id+" in organization: "+doc.ref.parent.parent.id);
});
});
This will return a snapshot across all people collections in your entire Firestore database.
First setup Cloud Functions according to the official Documentation.
Then after setting up create functions like this:
exports.YOURFUNCTIONNAME= functions.firestore
.document('organisations/[randomly generated id]/people/[randomly generated id]')
.oncreate(res => {
const data = res.data();
const email = data.email;/----Your field name goes here-----/
/-----------------Then apply your logic here---------/
)}
This will triggers the function whenever you create the People -> Random ID

advice on architecture for new user/entitlement checks in Bixby

I am getting ready to add a couple of persistence features to Bixby and want some advice on how to architect it. Specifically, I am going to add
1) check if user is new user
2) if existing user, check entitlements to content packages, each package has a string identifier
These are silent checks, they should be carried out prior to any utterance interaction.
My first thought was to create an action called Initialize with model, action, and code but no view and to call it as a require at the top of each action script. does that make sense? are there some good examples of how to do this?
This wasn't a very stack overflow-friendly question so I plowed ahead on my own. What I wound up doing to begin with is simply adding some code to the default-action.
module.exports.function = function (searchTerm, $vivContext) {
// check if user is already registered
// create query terms
bixbyUserId = $vivContext.bixbyUserId
const checkuserurl = properties.get("config", "baseUrl") + 'example-user-data'
const checkuserquery = {
apikey: properties.get("secret", "apiKey"),
q: "{\"" + properties.get("config", "userIdField") + "\":\"" + bixbyUserId + "\"}"
}
const checkuseroptions = {
format: "json",
query: checkuserquery,
cacheTime: 0
}
// submit query checking if user is in db
const checkuserresponse = http.getUrl(checkuserurl, checkuseroptions)
// if user exists in restdb, accept the userData
if (checkuserresponse && checkuserresponse.length === 1) {
var userData = checkuserresponse[0][properties.get("config", "userDataField")]
userData.$id = checkuserresponse[0]["_id"]
userData.bixbyuserid = bixbyUserId
console.log ('user exists and user id is', userData.bixbyuserid)
}
else {
// if user doesn't exist, create new user id in db with UserData
var userData = {}
userData.newuser = true
const createuserbody = {}
createuserbody[properties.get("config", "userIdField")] = bixbyUserId
createuserbody[properties.get("config", "userDataField")] = JSON.stringify(userData)
const createuserresponse = http.postUrl(checkuserurl, createuserbody, checkuseroptions)
console.log("user didn't exist in restdb so created one")
}
// get AltBrains
Needs testing and could benefit from a logic check.

Issues with storing discord channel ID in sqlite DB

I have a sqlite DB with a table called "guildinfo".
This is used for storing the guild ID, bot prefix, welcome message, leave message, bot message, welcome channel ID, and the starboard ID.
I created a command - ?welcome - to change welcomeChannel to the ID of the channel the command was ran in.
However, when I try to use the data I have in my DB, I get two completely different IDs.
I wrote this to test -
const info = sql.prepare(`SELECT * FROM guildinfo WHERE guild = ${message.guild.id}`)
const info2 = info.get();
console.log(This looks like ${message.guild.name} with the ID: ${message.guild.id} in: channel ID ${message.channel.id}. In the DB, we have ${info2.welcomeChannel} for this guild.)
This returns - This looks like test2 with the ID: 516761210776059906 in: 517048171084382214. In the DB, we have 517048171084382200 for this guild.
When I check the DB manually, I have 517048171084382214
I should be getting 517048171084382214 from the DB, rather than 517048171084382200.
Any help would be appreciated.
EDIT: ?welcome command -
const Discord = require("discord.js");
const bot = new Discord.Client();
const path = require('path')
const SQLite = require("better-sqlite3");
const sql = new SQLite(path.join(__dirname, '../', 'db/db55.sqlite'))
const botConfig = require(path.join(__dirname, '../', "./botConfig.json"));
const prefix = botConfig.prefix;
exports.run = async (bot, message, args) => { // This function takes three arguments, the bot (client) message (full message with prefix etc.) and args (Arguments of command
if (message.author.id !== '264850435985113088') {
return message.channel.send("You shouldn't be using this command.")
}
// Get guild ID
bot.getDefaults = sql.prepare(`SELECT * FROM guildinfo WHERE guild = ${message.guild.id}`)
bot.setDefaults = sql.prepare('INSERT OR REPLACE INTO guildinfo (guild, prefix, welcomeMsg, leaveMsg, botMsg, welcomeChannel, starboard) VALUES (#guild, #prefix, #welcomeMsg, #leaveMsg, #botMsg, #welcomeChannel, #starboard);')
const info = sql.prepare(`SELECT * FROM guildinfo WHERE guild = ${message.guild.id}`)
const info2 = info.get();
let Defaults
Defaults = bot.getDefaults.get()
if (message.guild && !Defaults) {
Defaults = {
guild: `${message.guild.id}`,
prefix: prefix,
welcomeMsg: "`Welcome to ${guild.name}, ${bot.user.username}`.",
leaveMsg: "`Bye, `${bot.user.username}!`",
welcomeChannel: `${message.channel.id}`,
botMsg: null,
starboard: null
};
bot.setDefaults.run(Defaults);
message.channel.send(`Welcome messages will now be sent to ${message.channel.id} - First condition`)
} else if (sql.prepare(`SELECT * FROM guildinfo WHERE guild = ${message.guild.id}`)) {
sql.prepare(`UPDATE guildinfo SET welcomeChannel = ${message.channel.id};`).run()
message.channel.send(`Welcome messages will now be sent to ${message.channel.id} - Second condition`)
}
}
exports.help = {
name: 'welcome' // Insert your command's name here!
}
My database looks like this -
It seems like this is an issue with how node.js numbers work.
After 9,007,199,254,740,991 which is Number.MAX_SAFE_INTEGER ( see ) node will "round" the number.
If I use node.js eval and eval 517048171084382214
It returns 517048171084382200 Type: Number.
This means you should check that:
in your database that the channelId column is string and not a number
that your SQL query doesn't convert the string to a number.

Update and add new Firebase child nodes in Firebase web app at the same time

I have been able to push new information and call it on the database but I been looking for several days now and I can't seem to find anything.
I am looking to be able to update a node that already has a value and add a new node if it doesn't. Can anyone help me with this. New to Firebase.
Thanks
code:
function userProfileUpdate(users) {
newUsername = document.getElementById("newUsername");
// Profile Update.
var user = {
username : newUsername.value
};
return firebase.database().ref().update(user);
};
From your comments it seems that you want to store user information in a data structure like this:
users
uid1
userName: "Frank van Puffelen"
To add a specific user in this structure, you'd do:
function userProfileUpdate(users) {
var user = firebase.auth().currentUser;
var root = firebase.database().ref();
newUsername = document.getElementById("newUsername");
var user = {
username : newUsername.value
};
return root.child('users').child(user.uid).set(user);
};
Update
To update a specific property of a specific user:
Map<String, Object> updates = new HashMap<String,Object>();
updates.put("username", newUsername.value);
root.child('users').child(user.uid).updateChildren(user);
Or alternatively:
root.child('users').child(user.uid).child("username").setValue(newUsername.value);
Both will auto-create the location if it doesn't exist yet.
Frank your code took away the entire key but I did figure out with this
var user = firebase.auth().currentUser;
var root = firebase.database().ref();
newUsername = document.getElementById("newUsername");
firebase.database().ref('users/' + user.uid).update({
username: newUsername.value
});
Thanks Frank yours push me in the right direction.

Resources