I'm currently making a Discord bot. The code I've provided below is supposed to get data from Hypixel to display guild info. It's getting the correct info, but I want to send all the names as one message instead of one person per message.
This is my code:
const fetch = require('node-fetch');
module.exports = {
name: 'hguild',
aliases: ['hg'],
description: 'Shows info about a hypixel guild!',
guildOnly: true,
args: true,
usage: '<player>',
execute(message, args) {
var ruuid = [];
const guildName = args[0];
message.channel.send('Please wait, checking API').then((msg) => {
fetch(`https://api.hypixel.net/guild?key=[REMOVED]&name=${guildName}`)
.catch((err) => message.channel.send(err))
.then((res) => res.json())
.catch((err) => message.channel.send(err))
.then((json) => {
console.log(json);
msg.edit('Here is about your guild!');
for (const guild of json.guild.members) {
const rawUsername = guild.uuid;
fetch(`https://api.mojang.com/user/profiles/${rawUsername}/names`)
.catch((err) => message.channel.send(err))
.then((res) => res.json())
.catch((err) => message.channel.send(err))
.then((json) => {
console.log(json[0].name);
if (json.name == null || json.status == 'ERR') {
}
var testList = [json[0].name];
message.channel.send(testList);
});
}
});
});
},
};
Currently its showing all the names, but sends one name per message. I want to group all these names together.
Instead of executing these two lines:
var testList = [json[0].name];
message.channel.send(testList);
And then closing the for loop, try pushing the results to an array initialized before the loop, then send that. Example:
var testList = [];
const getUsernames = async () => {
for await (const guild of json.guild.members) {
const rawUsername = guild.uuid;
fetch(`https://api.mojang.com/user/profiles/${rawUsername}/names`)
.catch((err) => message.channel.send(err))
.then((res) => res.json())
.catch((err) => message.channel.send(err))
.then((json) => {
console.log(json[0].name);
if (json.name == null || json.status == 'ERR') {
}
testList.push(json[0].name);
});
}
};
await getUsernames();
message.channel.send(testList.join('\n'));
Make sure you change the execute(message, args) line at the top of your code to async execute(message, args) so that await is possible.
As a disclaimer, I am not knowledgable in the Minecraft API area, and do not know how the object returned from your request is structured. This is just my best guess.
#Lioness100 Also unfamiliar with the Minecraft API.
I'm curious about the loop, seems like your initial fetch to the api with guild info returns what you're looking for.
Are you deconstructing names to test the functionality to eventually push entire profiles to discord? or are you content with just names?
(For the latter, instead of the loop have you tried)
message.channel.send(json.guild.members)
Otherwise another thing to consider would be utilizing the loop to generate an array of URLs that you could resolve together with a Promise.all. Wondering if the undesired output is a result of asynchronous behavior. Here's the api docs for reference:
https://javascript.info/promise-api
Hope that works, friend!
Related
I've been struggling with a weird error. Can't create a doc in firebase. There are no security rules to speak of, just:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Firestore is initialised the normal way and is active:
import { Firebase } from "../db";
let firebase = Firebase();
let firestore = firebase.firestore();
But nothing happens after this is run other than printing "here1", the other consoles aren't doing anything and the userid doc is not being created and no collection and doc under it.
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
The above doesn't print anything other than "here1" and gets stuck on the setting of the doc. The doc isn't created in Firestore either.
Any idea what might be wrong and how to debug it? Wasted a good 4 hours on trying to figure it out and worried if Firestore is so buggy that it's unsafe to use it in production.
First of all, I assure you Firebase is not buggy at all, we have it running on several production applications and they're running fantastic.
Second, I think your issue here is that you're passing a function as the second argument in the set() method, which is nowhere that I can find in the API reference. Instead, it returns a promise. Your code should look like this:
firebase.firestore()
.collection("users")
.doc(uid)
.set({ uid: uid })
.then((doc) => { console.log(doc.id) })
.catch((err) => { console.log(err) })
Cheers.
Here is an example which will work for you:
file test.mjs
import { Firestore } from '#google-cloud/firestore';
const firestore = new Firestore()
export default (uid) => firestore.collection("users")
.doc(uid)
.set({ uid })
.then(() => console.log('success')) // documentReference.set() returns: Promise < void >
.catch(err => console.error(err))
It's super weird, but what solved the issue for me is adding an unnecessary doc.get() like so:
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
const x = await firestore.collection("users").doc(data.uid).get();
// ^^^ added the above line I don't actually need, but somehow it
// establishes a connection to firebase or something which allows the
// promise below to resolve, rather than just hang!
// Doesn't resolve without it for whatever reason!
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
When removing the line, the function hangs again. So have to keep it in for now!
A bit worrying that we have to use such workaround hacks to make a simple write to the firestore, to work, Firebase!
Nonetheless, hope it helps someone facing this undebuggable problem.
I joined the site a few days ago.
I'm new here.
I have a problem that I can not solve. I built a function in Nodejs, every time I run the function I want to get information from the server.
On the client side I have this function, which I run, and then save the data, here no problem.
export const getRealtimeConversations = (user) => (dispatch) => {
axios.post('/realtimeConversations', user)
.then((res) => {
dispatch({
type: userConstants.GET_REALTIME_MESSAGES,
payload: res.data
});
})
.catch((err) => console.log(err))
}
I have a series of axios, when I use this line, I run a server-side function.
app.post('/realtimeConversations', FBAuth, getRealtimeConversations);
This is the function on the server side, I have a collection, which I want to access according to details I send from the client. After I have done the calculations I want, I want to return the end result.
exports.getRealtimeConversations = (req, res) => {
db.collection('conversations')
.where('user_uid_1', 'in', [req.body.uid_1, req.body.uid_2])
.orderBy('createdAt', 'asc')
.onSnapshot((querySnapshot) => {
const conversations = [];
querySnapshot.forEach(doc => {
if (
(doc.data().user_uid_1 == req.body.uid_1 && doc.data().user_uid_2 == req.body.uid_2)
||
(doc.data().user_uid_1 == req.body.uid_2 && doc.data().user_uid_2 == req.body.uid_1)
) {
conversations.push(doc.data())
}
});
console.log(conversations);
return res.json(conversations);
})
}
I do not know why but I get that I have a 404 error, I am completely new to it, I think I have a logical error in the server side function, but I'm not sure what I'm doing wrong.
I'm rewriting a project of mine and was wondering how would I post an array of data where I reuse the return value of a previous post request as their ID. Here's a rough detail of the data structure
Checklist A
[ChecklistItem 1, ChecklistItem 2, ChecklistItem 3] has their ID set as Checklist A
So my current setup is I send Checklist A, get the return value from FaunaDB(which is its unique ID)
then plug it in the array using array.map then resend the array to FaunaDB.
But i don't know how to save the array since the request paramater is already used up.
so i was wondering what's the normal way to do this.
here's a code snippet of the function
app.post('/checklists', (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
const ckdoc = client.query(
Crt('checklists',checklist))
.then((ret) => {
//would like to catch this ret and plug it into _checklistitems as its ID
//then send the _checklistitems to faunaDB
});
res.send(ckdoc);
});
function Crt(collection,data){
return Create(
Collection(collection),
{data}
)
}
UPDATE
after #eskwayrd pointed out that you can chain client queries within a single express js request. i chained another client query where i save the checklist items collection along with the return reference from a previous query. though i had problems sending the it as an Array, saving it through array.map still worked.
app.post('/checklists', async (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
var _ref;
console.log(checklist)
await client.query(
Crt('checklists',checklist)
)
.then((ret) => {
_ref = ret.ref
})
_checklistItems.map(item => {
item.checklist = _ref
console.log(item)
client.query(
Crt('checklist_items', item)
)
})
});
Using the Fauna JavaScript driver, the client object that you create is quite reusable; it is not "used up".
Generally, you can chain dependent queries like this:
client.query( ... your FQL query ... )
.then((result) => {
const ref = result.ref
client.query( ... another FQL query involving the ref ...)
.then((result2) => {
console.log(result2)
})
})
Using async and await, you can avoid nesting with something like:
;(async () => {
const result = await client.query( ... FQL query 1 ...)
.then((res) => res)
.catch((err) => console.log(`query 1 failed: ${err}`))
const ref = result.ref
const result2 = await client.query( ... FQL query 2 ...)
.then((res) => res)
.catch((err) => console.log(`query 2 failed: ${err}`))
console.log(result2)
})()
Note that both examples are, effectively, equivalent, and also demonstrates how to extract a value from the reponse.
I try to put a listener on Firebase that will replicate a value in the matching element in Firestore.
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userKey = event.data.ref.parent.key
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userKey}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const userRef = admin.firestore().collection('users')
const firestoreRef = userRef.where('email', "==", email)
firestoreRef.onSnapshot().update({ activities: event.data.toJSON() })
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
)
This function is able to retrieve and locate the elemnt needed to target the right document in firestore, but the .update()function still error firestoreRef.update is not a function
I try several ways to query but I still have this error.
How to properly query then update a document in this scenario?
The onSnapshot() method of Query introduces a persistent listener that gets triggered every time there's a new QuerySnapshot available. It keeps doing this until the listener is unsubscribed. This behavior is definitely not what you want. Also, there's no update() method on QuerySnapshot that your code is trying to call.
Instead, it looks like you want to use get() to fetch a list of documents that match your query, then update them all:
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userId = event.params.userId
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userId}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const usersRef = admin.firestore().collection('users')
const query = usersRef.where('email', "==", email)
const promises = []
query.get().then(snapshots => {
snapshots.forEach(snapshot => {
promises.push(snapshot.ref.update(event.data.val()))
})
return Promise.all(promises)
})
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
Note that I rewrote some other things in your function that were not optimal.
In general, it's a good idea to stay familiar with the Cloud Firestore API docs to know what you can do.
I am following the answer of #dstoiko from here
I am calling the API in ADD_MOVIE block and want to pass some value to my postback with payload ADD_TO_FIREBASE
here is my blocks
'use strict';
const Script = require('smooch-bot').Script;
var YtsHelper = require('./libs/YtsHelper.js');
const FirebaseHelper = require('./libs/FirebaseHelper.js');
var firebaseHelperObj = new FirebaseHelper();
module.exports = new Script({
processing: {
prompt: (bot) => bot.say('Beep boop...'),
receive: () => 'processing'
},
start: {
receive: (bot) => {
return bot.say('Hi! I\'m Smooch Bot!')
.then(() => 'showUserMenu');
}
},
showUserMenu: {
prompt: (bot) => bot.say("Here are the areas I can help you out. %[Add Movie](postback:ADD_MOVIE) %[Serve Food](postback:SERVE_FOOD)"),
receive: () => 'finish'
},
ADD_MOVIE : {
prompt: (bot) => bot.say('Enter movie name or keywords you want to search please.'),
receive: (bot, message) => {
const movie_name_searched = message.text;
return bot.setProp('movie_name_searched', movie_name_searched)
.then(() => bot.say('Search in progress...'))
.then(() => {
YtsHelper.getMoviesList(movie_name_searched,function(movies_array){
var movies_postbacks = "";
console.log("Movies SIZE " + movies_array.length);
for (var i = 0; i < movies_array.length ; i++){
movies_postbacks = movies_postbacks + " %["+movies_array[i]+"](postback:ADD_TO_FIREBASE)";
}
bot.say(movies_postbacks)
.then(() => bot.say("Click any movie to add into firebase."));
});
});
}
},
ADD_TO_FIREBASE: {
prompt: (bot) => bot.say("confirm, y/n"),
receive: () => 'showUserMenu'
},
finish: {
receive: (bot, message) => {
return bot.getProp('name')
.then((name) => bot.say(`Sorry ${name}, my creator didn't ` +
'teach me how to do anything else!'))
.then(() => 'showUserMenu');
}
}
});
Questions
Q0. I am new to nodeJS also, What should I call ADD_MOVIE, start, showUserMenu (in my code) blocks? function, method, code, module etc.
Q1. I Have called an yts api in my ADD_MOVIE block. Is this fine to call API in script.js file?
Q2. Important!: How can I pass the param to my postback with payload ADD_MOVIE so that I can perform some conditional code in ADD_TO_FIREBASE block
Q0: is a question of style, there's no definitive answer to give here. In other words, this is the wrong forum for this kind of discussion :) https://stackoverflow.com/help/how-to-ask
Q1: Yes making a DB query in receive is fine, however your receive function isn't waiting for the query to finish before it resolves your bot state. If for example you don't want your bot to accept user input until after the movie list is returned, you could do this:
receive: (bot, message) => {
const movie_name_searched = message.text;
return bot.setProp('movie_name_searched', movie_name_searched)
.then(() => bot.say('Search in progress...'))
.then(() => {
return new Promise((res) => YtsHelper.getMoviesList(movie_name_searched, (movies_array) => res(movies_array)));
})
.then((movies_array) => {
var movies_postbacks = "";
for (var i = 0; i < movies_array.length ; i++){
movies_postbacks = movies_postbacks + " %["+movies_array[i]+"](postback:ADD_TO_FIREBASE)";
}
return bot.say(movies_postbacks);
})
.then(() => bot.say("Click any movie to add into firebase."))
.then(() => 'ADD_MOVIE');
}
Note that I'm resolving the very end of the promise chain with 'ADD_MOVIE', which tells your bot to remain in the same state as it was before.
Q2: I see two options.
Option 1: Append the movie ID to the postback payload, eg ADD_TO_FIREBASE.movieid1, ADD_TO_FIREBASE.movieid2 and so on..
If you did this, you would have to define your own behavior inside handlePostback that parses out the movie ID from your postback payload.
You would also have to transition your state amchine into the desired ADD_TO_FIREBASE state yourself. Eg, from your custom handlePostback methdod you would do something like this:
const stateMachine = new StateMachine({
script,
bot: createBot(req.body.appUser)
});
stateMachine.setState('ADD_TO_FIREBASE');
Option 2: The %[foo](postback:bar) message you're using is actually a shorthand syntax. The real inner workings of postback messages are action buttons which you can send to the Smooch API directly. Action buttons also allow you to specify a metadata object. If instead of using the built-in bot.say, you could post messages ot the API directly, and you could store your movie IDs inside the action metadata. You would again have to retrieve the selected movieId from this metadata via your custom handlePostback as you did in option 1.