I have a function to delete a message after I send it:
export function del(interaction) {
setTimeout(() => interaction.deleteReply(), 1000);
}
I want to convert it so this will be working to interaction.channel.send, earlier I used interaction.reply(), but I don't want my bot to continue using reply to every command. I read Discord docs and didn't find anything helpful.
Any help on how to do this will be appreciated!
When you use interaction.channel.send you need to track the new message that you created (as it separate from the interaction).
Here is a quick sample where I have /ping command that creates and deletes a reply and a message:
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with Pong!"),
async execute(interaction) {
await interaction.reply("Pong!");
const msg = await interaction.channel.send("Pong! 2!");
//Delete reply
setTimeout(async () => {
await interaction.deleteReply();
}, 1000);
//delete message
setTimeout(async () => {
await msg.delete();
}, 1500);
},
};
Related
I am currently writing a bot that acts like a timeclock for a friend of mine. Currently I am trying to get the bot to message someone if they have been clocked in for more than 2 hours and have not "refreshed" their clock in time. More of just a reminder of, "hey did you forget to clock out".
Currently the bot sends a correct DM with the correct reaction to the user identified. However, it never detects when a user reacts to that message. My onMessageReactionAdd event fires, but i have blocked that if it is in a DM channel because I would like to perform a task if the user doesnt respond in time(clock them out). I would only like to collect the refresh attached to this method so I can keep methods separate.
I am suspecting i am awaiting on the wrong message, or not understanding exactly what is going on after reading through other answers.
const refresh = require('../database/checkRefresh');
const refrechClockin = require('../commands/refreshClockin');
module.exports = async (client) =>{
let users = [];
const res = refresh.execute()
.then(res => {
console.log(`refresh query executed`);
if(res.rows.length > 0){
res.rows.forEach(row => {
users.push(row.user_id);
})
}
})
.then( () => {
console.log(`users from refresh query: ${JSON.stringify(users)}`);
users.forEach(user => {
console.log(`user_id: ${user}`);
client.users.fetch(user)
.then((user) => {
var message = user.send('Did you forget to clock out? If you are still working click the little bell, after 5 minutes you will be clocked out.')
.then((message, user) => {
message.react('🔔');
const filter = (reaction, user) => reaction.emoji.name === '🔔';
message.awaitReactions(filter, {max:2, time: 5000})
.then(collected => {
console.log(`inside of new then method`);
console.log(`collected: ${JSON.stringify(collected)}`);
})
.catch(console.error)
});
});
});
});
}
After looking through other answers, I double checked my Intents and they seem to be correct.
const client = new Client({
intents: [Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGES,
Intents.FLAGS.DIRECT_MESSAGE_REACTIONS,
Intents.FLAGS.DIRECT_MESSAGE_TYPING,
],
partials: ['MESSAGE', 'CHANNEL', 'REACTION']
});
UPDATE
I opted to change to a reactionCollector on the message. At first I was still having the same issue. After many console.logs, both in the filter and in the collector events, I discovered that my filter would not properly match the emoji that was being used. I even printed the reaction that was used(the bell) to console and copied from there to insert in the comparison and still will not match.
I would like to ultimately only allow them to respond using the bell, but for now I am ok with it. I have attached a default reaction from the bot so hopefully they see that and just click it. putting my updated code below. If anyone has any idea why I could not get the compare to work it would help. All of my other emoji comparisons are working correctly in my commands.
const refresh = require('../database/checkRefresh');
const refrechClockin = require('../commands/refreshClockin');
module.exports = async (client) =>{
let users = [];
const res = refresh.execute()
.then(async res => {
console.log(`refresh query executed`);
if(res.rows.length > 0){
res.rows.forEach(row => {
users.push(row.user_id);
})
}
})
.then( async () => {
console.log(`users from refresh query: ${JSON.stringify(users)}`);
users.forEach(user => {
console.log(`user_id: ${user}`);
client.users.fetch(user)
.then(async (user) => {
var message = await user.send('Did you forget to clock out? If you are still working click the little bell, after 5 minutes you will be clocked out.');
message.react('🔔');
const filter = (reaction, user) => {
console.log(`reaction: ${JSON.stringify(reaction)}`);
console.log(`Reaction Emoji: ${reaction.emoji.name}`);
return (user.bot === false);
};
const collector = message.createReactionCollector({ filter, max: 1, time: 45_000 });
collector.on('collect', r => console.log(`Collected ${r.emoji.name}`));
collector.on('end', collected => console.log(`Collection ended with ${collected.size} Collected items`));
});
});
});
}
Building a NodeJS REST API.
Trying to send load data from FireBase collection, then sending it to the user (as API response).
Looks like the problem is that it's not waits for the firebase fetch to resolve, but send back a response without the collection data. (tried to use ASYNC-AWAIT but its not working)
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {
snapshot.docs.forEach(msg => {
console.log(msg.data().messageContent)
return {
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}
})
})
}
try {
const chatData = await getChatData()
console.log(chatData)
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData
})
} catch (err) {
if (!err.statusCode) {
err.statusCode(500)
}
next(err)
}
}
As you can see, I've used 2 console.logs to realize what the problem, Terminal logs looks like:
[] (from console.logs(chatData))
All messages (from console.log(msg.data().messageContent))
Is there any way to block the code unti the firebase data realy fetched?
If I correctly understand, you want to send back an array of all the documents present in the messages subcollection. The following should do the trick.
exports.getChatMessages = async (req, res, next) => {
const chatId = req.params.chatId;
const collectionRef = db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc');
try {
const chatsQuerySnapshot = await collectionRef.get();
const chatData = [];
chatsQuerySnapshot.forEach((msg) => {
console.log(msg.data().messageContent);
chatData.push({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
});
});
console.log(chatData);
res.status(200).json({
message: 'Chat Has Found',
chatData: chatData,
});
} catch (err) {
if (!err.statusCode) {
err.statusCode(500);
}
next(err);
}
};
The asynchronous get() method returns a QuerySnapshot on which you can call forEach() for enumerating all of the documents in the QuerySnapshot.
You can only await a Promise. Currently, getChatData() does not return a Promise, so awaiting it is pointless. You are trying to await a fixed value, so it resolves immediately and jumps to the next line. console.log(chatData) happens. Then, later, your (snapshot) => callback happens, but too late.
const getChatData = () => new Promise(resolve => { // Return a Promise, so it can be awaited
db.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot(resolve) // Equivalent to .onSnapshot((snapshot) => resolve(snapshot))
})
const snapshot = await getChatData();
console.log(snapshot)
// Put your transform logic out of the function that calls the DB. A function should only do one thing if possible : call or transform, not both.
const chatData = snapshot.map(msg => ({
authorID: msg.data().authorID,
messageContent: msg.data().messageContent,
timeStamp: msg.data().timeStamp,
}));
res.status(200).json({
message: 'Chat Has Found',
chatData
})
Right now, getChatData is this (short version):
const getChatData = () => {
db
.collection('chats')
.doc(chatId)
.collection('messages')
.orderBy('timeStamp', 'asc')
.onSnapshot((snapshot) => {}) // some things inside
}
What that means is that the getChatData function calls some db query, and then returns void (nothing). I bet you'd want to return the db call (hopefully it's a Promise), so that your await does some work for you. Something along the lines of:
const getChatData = async () =>
db
.collection('chats')
// ...
Which is the same as const getChatData = async() => { return db... }
Update: Now that I've reviewed the docs once again, I see that you use onSnapshot, which is meant for updates and can fire multiple times. The first call actually makes a request, but then continues to listen on those updates. Since that seems like a regular request-response, and you want it to happen only once - use .get() docs instead of .onSnapshot(). Otherwise those listeners would stay there and cause troubles. .get() returns a Promise, so the sample fix that I've mentioned above would work perfectly and you don't need to change other pieces of the code.
I'm currently setting up a CI environment to automate e2e tests our team runs in a test harness. I am setting this up on Gitlab and currently using Puppeteer. I have an event that fires from our test harness that designates when the test is complete. Now I am trying to "pool" the execution so I don't use up all resources or run out of listeners. I decided to try out "puppeteer-cluster" for this task. I am close to having things working, however I can't seem to get it to wait for the event on page before closing the browser. Prior to using puppeteer-cluster, I was passing in a callback to my function and when the custom event was fired (injected via exposeFunction), I would go about calling it. That callback function is now being passed in data though now and therefore not waiting. I can't seem to find a way to get the execution to wait and was hoping someone might have an idea here. If anyone has any recommendations, I'd love to hear them.
test('Should launch the browser and run e2e tests', async (done) => {
try {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 10,
monitor: false,
timeout: 1200000,
puppeteerOptions: browserConfig
});
// Print errors to console
cluster.on("taskerror", (err, data) => {
console.log(`Error crawling ${data}: ${err.message}`);
});
//Setup our task to be run
await cluster.task( async ({page, data: {testUrl, isLastIndex, cb}, worker}) => {
console.log(`Test starting at url: ${testUrl} - isLastIndex: ${isLastIndex}`);
await page.goto(testUrl);
await page.waitForSelector('#testHarness');
await page.exposeFunction('onCustomEvent', async (e) => {
if (isLastIndex === true){ ;
//Make a call to our callback, finalizing tests are complete
cb();
}
console.log(`Completed test at url: ${testUrl}`);
});
await page.evaluate(() => {
document.addEventListener('TEST_COMPLETE', (e) => {
window.onCustomEvent('TEST_COMPLETE');
console.log("TEST COMPLETE");
});
});
});
//Perform the assignment of all of our xml tests to an array
let arrOfTests = await buildTestArray();
const arrOfTestsLen = arrOfTests.length;
for( let i=0; i < arrOfTestsLen; ++i){
//push our tests on task queue
await cluster.queue( {testUrl: arrOfTests[i], isLastIndex: (i === arrOfTestsLen - 1), cb: done });
};
await cluster.idle();
await cluster.close();
} catch (error) {
console.log('ERROR:',error);
done();
throw error;
}
});
So I got something working, but it really feels hacky to me and I'm not really sure it is the right approach. So should anyone have the proper way of doing this or a more recommended way, don't hesitate to respond. I am posting here shoudl anyone else deal with something similar. I was able to get this working with a bool and setInterval. I have pasted working result below.
await cluster.task( async ({page, data: {testUrl, isLastIndex, cb}, worker}) => {
let complete = false;
console.log(`Test starting at url: ${testUrl} - isLastIndex: ${isLastIndex}`);
await page.goto(testUrl)
await page.waitForSelector('#testHarness');
await page.focus('#testHarness');
await page.exposeFunction('onCustomEvent', async (e) => {
console.log("Custom event fired");
if (isLastIndex === true){ ;
//Make a call to our callback, finalizing tests are complete
cb();
complete = true;
//console.log(`VAL IS ${complete}`);
}
console.log(`Completed test at url: ${testUrl}`);
});
//This will run on the actual page itself. So setup an event listener for
//the TEST_COMPLETE event sent from the test harness itself
await page.evaluate(() => {
document.addEventListener('TEST_COMPLETE', (e) => {
window.onCustomEvent('TEST_COMPLETE');
});
});
await new Promise(resolve => {
try {
let timerId = setInterval(()=>{
if (complete === true){
resolve();
clearInterval(timerId);
}
}, 1000);
} catch (e) {
console.log('ERROR ', e);
}
});
});
i am making a script that will take a picture of my desktop (for server disk stuff) but it doesn't take the picture. But it will when i create another script and run that.
const screenshot = require('screenshot-desktop')
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('message', msg => {
if (msg.content === '!Photo') {
screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' })
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
}
});
If i only put screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' }) In another script, it works fine. But it doesn't when i try the code above this one.
Whenever a function writes/reads a file (or query database) it is most likely asynchronous which means you need to wait for it to write/read the file then continue.
As you can see in the docs https://www.npmjs.com/package/screenshot-desktop#usage
screenshot() returns a Promise so you have 2 options.
First, call Bot.sendFile in the then function like so
client.on('message', msg => {
if (msg.content === '!Photo') {
screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' }).then(function(){
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
})
}
});
or use await like so
client.on('message', msg => {
if (msg.content === '!Photo') {
await screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' })
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
}
});
We are building a facebook messenger bot with Botkit version 4.
We have tried different ways to add typing delay with the messages but none of them is working properly.
As we are using the conversations we are unable to use this format in middleware
bot.reply(message, { type: "typing" });
So we are using something like the following:
Solution 1
controller.middleware.send.use(async function (bot, message, next) {
if (bot._config && bot._config.activity && bot._config.activity.conversation && bot._config.activity.conversation.id) {
await typingIndicator(bot,bot._config.activity.conversation.id)
setTimeout(async () => { next(); }, 2000);
} else {
next();
}
});
async function typingIndicator(bot, id) {
await bot.api.callAPI('/me/messages', 'post', {
recipient: { id: id }, sender_action: 'typing_on'
})
}
But this implementation is not working as expected.
I have around 200 - 300 threads so I tried adding the delay using convo.before()
But even this is breaking in between
Solution 2
let dialog = new BotkitConversation('dialog', controller);
dialog.addMessage({text: 'Message 1', action: 'thread_2'}, 'thread_1');
dialog.addMessage({text: 'Message 2', action: 'thread_3'}, 'thread_2');
dialog.addMessage({text: 'Message 3', action: 'thread_4'}, 'thread_3');
dialog.addMessage({text: 'Message 4'}, 'thread_4');
for (let key in dialog.script) {
dialog.before(`${key}`, async (convo, bot) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, 2000);
});
});
}
Any help will be appreciated.
I solved it like this now:
async function middlewareTest(bot, message, next) {
if(message.text.length > 0){
let time = message.text.length * 30;
await setTimeout(async ()=> {await next();}, time);
} else {
await next();
}
}
controller.middleware.send.use(middlewareTest);
I check if the messages text that is going to be sent has more than 0 characters. If thats the case I do a timeout.
Hope this helps.