i have this code:
function isAdmin(idOfChat, IdOfUser, ctx) {
//function
return isAdminBoolean
}
bot.command("test", ctx => {
if (isAdmin(ctx.message.chat.id, ctx.message.from.id) == true) {
ctx.reply("Admin")
}else{
ctx.reply("._.")
}
})
how to make it work?
sorry for my bad English)))
You should re-write your method as a promise (because Telegraf queries from Telegram API, so, your isAdmin method could be like this)
function isAdmin(idOfChat, IdOfUser, ctx) {
return new Promise((resolve, reject) => {
//Get user information first
ctx.telegram.getChatMember(idOfChat, IdOfUser).then((user) => {
//Then check if user is admin (or creator)
resolve(user.status == "administrator" || user.status == "creator");
})
.catch((error) => {
//Reject if it's an error
reject(error);
});
});
}
Then, for use it into your main function, you should have to handle it like this:
bot.command("test", ctx => {
isAdmin(ctx.message.chat.id, ctx.message.from.id, ctx).then((result) => {
if (result) {
ctx.reply("Admin");
} else {
ctx.reply("._.");
}
})
.catch((error) => {
ctx.reply("An error has ocurred trying to get user rank: " + JSON.stringify(error));
});
});
Related
In short, I am making a Discord bot with discord.js and I am having trouble with asynchronous and synchronous functions.
In order to assign variables, the last part of the function loops through each variable and then converts it to its desired type, which can be seen here:
argsList.forEach((argument, index) => {
let finalArgument = argument
const type = args[index].type
if (type === UserArgument) {
new UserArgument(argument).result
.then(userObject => {
finalArgument = userObject
})
.catch(error => {
throw error
})
} else if (type === MemberArgument) {
new MemberArgument(argument, guild).result
.then(memberObject => {
finalArgument = memberObject
})
.catch(error => {
throw error
})
} else if (type === ChannelArgument) {
new ChannelArgument(argument, guild).result
.then(channelObject => {
finalArgument = channelObject
})
.catch(error => {
throw error
})
} else if (type === RoleArgument) {
new RoleArgument(argument, guild).result
.then(roleObject => {
finalArgument = roleObject
})
.catch(error => {
throw error
})
}
finalArgList.push(finalArgument)
})
return finalArgList
}
And here is an example of how the UserArgument class looks like (all other argument bases basically look the same)
class UserArgument {
constructor(user) {
this.result = new Promise((resolve, reject) => {
if (typeof(user) === "string") {
if (user.match(/^<#!([0-9]+)>$/)) {
user = user.match(/[0-9]+/)
}
if (isNumeric(user)) {
ArgumentBase.client.users.fetch(user)
.then(userObject => {
resolve(userObject)
return userObject
})
}
this.#getUserFromName(user)
.then(userObject => {
resolve(userObject)
return userObject
})
} else if (user instanceof DiscordJS.User) {
resolve(user)
return user
} else if (user instanceof DiscordJS.GuildMember || user instanceof DiscordJS.ThreadMember) {
resolve(user.user)
return user.user
}
let userObject = ArgumentBase.client.users.resolve(user)
if (userObject) {
resolve(userObject)
return userObject
}
reject(new UserNotFound(toString(user)))
})
}
async #getUserFromName(username) {
return new Promise((resolve, reject) => {
for (const user in ArgumentBase.client.users.cache) {
if (user.username === username) {
resolve(user)
return user
}
}
throw new UserNotFound(username)
})
}
}
The issue that I am coming across is that the code that handles each argument does not wait for the function to be finished and instead skips over it. This of course causes the command to be executed before the arguments are even processed. For my tests, I was testing throwing errors from the UserArgument class, and the error did get thrown, but only after the command had already executed because that is when it decided to finish.
My assumption is that since Promise is an asynchronous function, the code keeps running and does not wait for it. I tried turning the argument function to an async function and use await, but I kept getting the SyntaxError: await is only valid in async functions and the top level bodies of modules, even when the function is an async function (function declaration is static async getCommandArgs(invokedString, args, guild = undefined)). If someone could help me, that would be amazing. Thank you for your time and help.
I am trying to do some operations using oncall function where,
I am first checking if user exists in database, then if he exists then do some operations like updating the DB.
well the function is working as expected and updating the DB as well, but its returning response as null so I can't tell what exactly happening.
exports.populateCart = functions.https.onCall((data, context) => {
const user = context.auth.uid;
DB.collection('users').doc(user).get().then((usr_resp) => {
if (usr_resp.exists) {
DB.collection('products').doc(data.productKey).get().then((prod_res) => {
DB.collection('carts').add({
//some data
}).then(() => {
return {
response: 'CART UPDATED'
};
}).catch((carts_err) => {
return {
response: 'ERROR WHILE POPULATING CART : ' + carts_err
}
});
}).catch((prod_err) => {
return {
response: 'ERROR WHILE FETCHING PRODUCT : ' + prod_err
};
});
}
else {
return {
response: 'USER DOES NOT EXISTS OR USER NOT AUTHENTICATED'
};
}
}).catch((usr_err) => {
return {
response: 'ERROR WHILE FETCHING USER : ' + usr_err
};
});
});
There is one more similar question on stack overflow, where the solution is proposed to use,
reject(err);
resolve(lists);
methods, Please if the solution is using above methods, then please explain the use cases of this method in detail and if there are more methods like this please explain them as well.
Firebase function not returning data
You need to return every promise that you are running:
exports.populateCart = functions.https.onCall((data, context) => {
const user = context.auth.uid;
return DB.collection('users').doc(user).get().then((usr_resp) => {
if (usr_resp.exists) {
return DB.collection('products').doc(data.productKey).get().then((prod_res) => {
return DB.collection('carts').add({
//some data
}).then(() => {
return {
response: 'CART UPDATED'
};
}).catch((carts_err) => {
return {
response: 'ERROR WHILE POPULATING CART : ' + carts_err
}
});
}).catch((prod_err) => {
return {
response: 'ERROR WHILE FETCHING PRODUCT : ' + prod_err
};
});
}
else {
return {
response: 'USER DOES NOT EXISTS OR USER NOT AUTHENTICATED'
};
}
}).catch((usr_err) => {
return {
response: 'ERROR WHILE FETCHING USER : ' + usr_err
};
});
});
Try copying the above code. Also using async functions will make it look better like this:
exports.populateCart = functions.https.onCall(async (data, context) => {
try {
const usr_resp = await DB.collection('users').doc(user).get()
if (!usr_resp.exists) throw new Error("USER DOES NOT EXISTS OR USER NOT AUTHENTICATED")
const prod_res = await DB.collection('products').doc(data.productKey).get()
await DB.collection('carts').add({ . someData . })
return { response: "Cart Updated" }
} catch (e) {
console.log(e)
return { response: e }
}
});
You are not returning your promise. (see line 3 below)
exports.populateCart = functions.https.onCall((data, context) => {
const user = context.auth.uid;
// add return here before Db.collection()
return DB.collection('users').doc(user).get().then((usr_resp) => {
if (usr_resp.exists) {
return DB.collection('products').doc(data.productKey).get().then((prod_res) => {
return DB.collection('carts').add({
//some data
}).then(() => {
return {
response: 'CART UPDATED'
};
}).catch((carts_err) => {
return {
response: 'ERROR WHILE POPULATING CART : ' + carts_err
}
});
}).catch((prod_err) => {
return {
response: 'ERROR WHILE FETCHING PRODUCT : ' + prod_err
};
});
}
else {
return {
response: 'USER DOES NOT EXISTS OR USER NOT AUTHENTICATED'
};
}
}).catch((usr_err) => {
return {
response: 'ERROR WHILE FETCHING USER : ' + usr_err
};
});
});
What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch
How can I use the answer of a promise outside of. Then what should I do?
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
console.log(resp)
})
.catch(e => console.log(e))
}
});
console.log (resp) inside the .then () knows it and prints correctly, but when I want to know resp out of the forEach to use below, it says undefined
Thanks.
arreglo.forEach((item) => {
if (item.tipoCampo == 3) {
self.campoSelects(item.tablaCampo)
.then(resp => {
logMyData(resp);
})
.catch(e => console.log(e))
}
});
logMyData=(x)=>{
console.log(x);
}
This is just as simple as adding a helper function which executes inside your .then
Guessing that you want to be able to access the value within the forloop. Since self.campoSelects is a promise we can use async await.
// Call campo selects
function getCampoSelects(_self, tablaCampo) {
return new Promise(async (resolve, reject) => {
let campoData;
try {
campoData = await _self.campoSelects(tablaCampo);
} catch (err) {
reject(err);
}
resolve(campoData);
});
}
function happyLittleFunc() {
const arreglo = [];
arreglo.forEach(async (item) => {
if (item.tipoCampo === 3) {
let campoSelect;
// Unsure if you are setting self somewhere but it can be passed in here.
try {
campoSelect = await getCampoSelects(self, item.tipoCampo);
} catch (err) {
console.log(err);
return;
}
console.log(campoSelect);
}
});
}
happyLittleFunc();
I Don't want to make checks as if (!isPresent) then do this ....
Is their any way to bypass all the then when my work is complete
var isPresent=false;
myPromise
.then((employee) => {
if (employee) {
// throw 'employee already exist';
isPresent = true;
return res.data(mapper.toFullModel(employee));
}
return model;
});
.then(model => {
if (!isPresent) {
return new db.employee(model).save();
}
})
.then(employee => {
if (!isPresent) {
employee.token = auth.getToken(employee);
return employee.save();
}
})
.then(employee => {
if (!isPresent) {
res.data(mapper.toFullModel(employee));
}
})
.catch(err => {
res.failure(err);
});
You can rewrite your promise chain so the second part of it gets nested:
myPromise.then(employee => {
if (employee) {
return res.data(mapper.toFullModel(employee));
}
return new db.employee(model).save().then(employee => {
employee.token = auth.getToken(employee);
return employee.save();
}).then(employee => {
return res.data(mapper.toFullModel(employee));
});
}).catch(err => {
res.failure(err);
});