Catch the user that responses to an awaitMessages action - node.js

in my discord bot, i have a command that scrambles a word, and the first user that responds in the chat gets a reward. i already made the command to await a message response, but how can i save who responded? heres my code so far
authorID = msg.author.id;
const filter = response => {
return response.author.id === authorID;
}
msg.channel.awaitMessages(filter, {
max: 1,
time: 5000
})
.then(mg2 => {
if (mg2.first().content === "1") {
msg.channel.send("you said 1");
}
});

With the filter, you are only allowing the original msg author to enter, you should make it so it detects if the msg.content equals the unscrabbled word. Also you don't have errors: ["time"].
const filter = m => m.content === unscrabledWord;
msg.channel.awaitMessages(filter, { max: 1, time: 5000, errors: ["time"] })
.then(collected => {
const winner = collected.first().author;
msg.channel.send(`${winner} Won!`)
})
.catch(() => msg.channel.send("Nobody guessed it"));

Related

Update large number of documents

I am trying to update the content of large number of documents in firebase, I have tried the following:
First, reading all documents on client side and looping over the documents and updating them by refence.
the problem here is that I am doing intensive operation on the client side and that would be unpredictable, therefore I switched to Firebase's Functions.
Second, reading all documents in firebase functions and then updating them using bulkwriter
here's the code:
exports.testingFunction1 = functions.runWith({
timeoutSeconds: 540,
memory: "8GB",
}).https.onCall(async (data, context) => {
const storeId = data.text;
if (!(typeof storeId === 'string') || storeId.length === 0) {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with ' +
'one arguments containing the storeId.');
}
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;
let bulk1 = new admin.firestore().bulkWriter();
let products = await admin.firestore().collection("Products").get(); //here's the problems's source
products.forEach((document) => {
bulk1.update(document.ref, { "IsStorePublished": true });
});
await bulk1.flush().then(() => {
return { "result": "Success!" };
})
.catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
return { "Result": "Success" }
});
the problem here appears when I try to read more that about 8000 documents at a single time, I get the following error although I have changed the memory limitations for the function to the max possible:
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap
out of memory
Is there a good way to achieve this task ?
For anyone interested, I have solved the issue as following:
the problem with reading a large amount of data to update it in firebase functions is that the memory is filling up, so I have made a recursive function enables reading 500 items at a time and apply the "BulkWrite" to only 500 documents at the time, and to achieve this I used the "startAfter" reading method.
This is the main firebase v1 function:
where it reads the first 500 items and apply the function "operationToDo" to them, and then calls the recursive function to continue the process.
exports.testingFunction1 = functions.runWith({
timeoutSeconds: 540,
memory: "8GB",
}).https.onCall(async (data, context) => {
const storeId = data.text;
if (!(typeof storeId === 'string') || storeId.length === 0) {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with ' +
'one arguments "storeId" containing the storeId.');
}
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;
let first = admin.firestore()
.collection("Products")
.orderBy("Name").where("Store", '==', storeId)
.limit(500);
await first.get().then(
async (documentSnapshots) => {
if (documentSnapshots.docs.length == 0) {
return;
} else {
await operationToDo(documentSnapshots, "update", { "IsStorePublished": false })
}
let lastVisible =
documentSnapshots.docs[documentSnapshots.size - 1];
await recursivePublishingTheStore(lastVisible, storeId);
},
);
return { "Result": "Success" }
});
The recursive function:
async function recursivePublishingTheStore(lastVisible, storeId) {
let next = admin.firestore()
.collection("Products")
.orderBy("Name").where("Store", '==', storeId)
.startAfter(lastVisible)
.limit(500);
await next.get().then(
async (documentSnapshots) => {
if (documentSnapshots.docs.length == 0) {
return;
} else {
await operationToDo(documentSnapshots, "update", { "IsStorePublished": false })
let lastVisible =
documentSnapshots.docs[documentSnapshots.size - 1];
await recursivePublishingTheStore(lastVisible, storeId);
}
}
);
}
The operation can be anything but in my case it would be "update":
async function operationToDo(documents, operation, value) {
let bulk1 = new admin.firestore().bulkWriter();
documents.forEach((document) => {
if (operation == 'update')
bulk1.update(document.ref, value);
});
await bulk1.flush().then(() => {
})
.catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
}
The performance of the code above is pretty good, for updating about 15k documents it would take about 2 minutes.
Note: I have chosen the number 500 randomly, different number might work and might perform better, and I will be experimenting with it this week.

Remove a response when reacting with an ❌

I tried doing this and adapting it from discordjs.guide, but I've either adapted something wrongly or it isn't working in my context. I tried this:
botMessage.react('❌');
const filter = (reaction, user) => {
return ['❌'].includes(reaction.emoji.name);
};
botMessage.awaitReactions({ filter, max: 1, time: 60000, errors: ['time'] }).then(collected => {
const reaction = collected.first();
console.log('a')
if(reaction.emoji.name === '❌'){
console.log('x')
botMessage.delete(1);
}
}).catch(collected => {});
botMessage is the response from the bot, since I am using a message command.
neither of the console.log() output anything.
any help is grateful and thank you
All you have to do is add the GUILD_MESSAGE_REACTIONS intent:
const client = new Client({ intents: [Intents.FLAGS.GUILD_MESSAGE_REACTIONS /* other intents */] });

msg.author.dmChannel does not exist

so i need to get msg.author.dmChannel
it doesnt work
the msg.author object is the following:
User {
id: '0000000000000000',
bot: false,
system: false,
flags: UserFlags { bitfield: 0 },
username: 'username' ,
discriminator: '0000',
avatar: '8eb21315f800000000000000000000008a78ab77e',
banner: undefined,
accentColor: undefined
}
so there really is no dmChannel? how do i make one? msg.author.createDM() doesnt work
EDIT:
msg.author.send('What is the password?' + '\n If this is not you or you did not use the confim-subscription command please disregard this message. You have 1 minute'); has already been used
msg.author.send('What is the password?' + '\n If this is not you or you did not use the `confim-subscription` command please disregard this message. You have 1 minute');
console.log(msg.author);
let channel = msg.author.dmChannel;
console.log(channel);
channel.awaitMessages(m => m.author.id == msg.author.id, {
max: 1,
time: 60000,
errors: ['time']
})
.then(dm => {
dm = dm.first()
if (dm.content == password || dm.content.toUpperCase() == 'Y') {
dm.channel.send(`Your respone has been processed :thumbsup:`);
var role = message.guild.roles.find(role => role.name === "Member");
msg.guild.members.cache.get(msg.author.id).roles.add(role)
} else {
dm.channel.send(`Terminated: Invalid Response`)
}
})
In v13, DMs need their own intent (DIRECT_MESSAGES). Also, User.dmChannel can be null. To force a DM, you can use User.createDM which returns the channel. Here is what could be the final code:
let channel = await msg.author.createDM();
channel.send('What is the password?' + '\n If this is not you or you did not use the `confim-subscription` command please disregard this message. You have 1 minute');
channel.awaitMessages(m => m.author.id == msg.author.id, {
max: 1,
time: 60000,
errors: ['time']
})
.then(dm => {
dm = dm.first()
if (dm.content == password || dm.content.toUpperCase() == 'Y') {
dm.channel.send(`Your respone has been processed :thumbsup:`);
var role = message.guild.roles.find(role => role.name === "Member");
msg.guild.members.cache.get(msg.author.id).roles.add(role)
} else {
dm.channel.send(`Terminated: Invalid Response`)
}
})
Note your client does need the DM intents. You can provide it like this:
const client = new Client({
intents: [
Intents.FLAGS.DIRECT_MESSAGES //you can add other intents as needed
]
})

Clear reactions edited embed message

I want to do is that when the embed message is changed, the reactions are reset, not deleted.
I mean that when someone reacts to the emoji and embed its changed, the reaction returns to 1 and does not stay at 2.
and when it returns to 1 I can send a third embed
ty
This is the code I am using:
const embed = new MessageEmbed()
.setTitle("Test1")
.setFooter("Test1");
message.channel.send(embed).then(sentEmbed => {
sentEmbed.react("➡");
const filter = (reaction, user) => {
return (
["➡"].includes(reaction.emoji.name) &&
user.id === message.author.id
);
};
sentEmbed
.awaitReactions(filter, { max: 1, time: 30000, errors: ["time"] })
.then(collected => {
const reaction = collected.first();
if (reaction.emoji.name === "➡") {
const embed2 = new MessageEmbed()
.setTitle('test2')
.setDescription('test2')
sentEmbed.edit(embed2);
}
})
});
You can remove all reactions by using message.reactions.removeAll().catch(error => console.error('Failed to clear reactions: ', error)); and then add reactions back if you want. I would do this in a function so you don't have to add all the reactions over and over each time you want to update the embed.

Deleting all messages in discord.js text channel

Ok, so I searched for a while, but I couldn't find any information on how to delete all messages in a discord channel. And by all messages I mean every single message ever written in that channel. Any clues?
Try this
async () => {
let fetched;
do {
fetched = await channel.fetchMessages({limit: 100});
message.channel.bulkDelete(fetched);
}
while(fetched.size >= 2);
}
Discord does not allow bots to delete more than 100 messages, so you can't delete every message in a channel. You can delete less then 100 messages, using BulkDelete.
Example:
const Discord = require("discord.js");
const client = new Discord.Client();
const prefix = "!";
client.on("ready" () => {
console.log("Successfully logged into client.");
});
client.on("message", msg => {
if (msg.content.toLowerCase().startsWith(prefix + "clearchat")) {
async function clear() {
msg.delete();
const fetched = await msg.channel.fetchMessages({limit: 99});
msg.channel.bulkDelete(fetched);
}
clear();
}
});
client.login("BOT_TOKEN");
Note, it has to be in a async function for the await to work.
Here's my improved version that is quicker and lets you know when its done in the console but you'll have to run it for each username that you used in a channel (if you changed your username at some point):
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
var before = 'LAST_MESSAGE_ID';
var your_username = ''; //your username
var your_discriminator = ''; //that 4 digit code e.g. username#1234
var foundMessages = false;
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
foundMessages = true;
if (
message.author.username == your_username
&& message.author.discriminator == your_discriminator
) {
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}
}));
}).then(() => {
if (foundMessages) {
foundMessages = false;
clearMessages();
} else {
console.log('DONE CHECKING CHANNEL!!!')
}
});
}
clearMessages();
The previous script I found for deleting your own messages without a bot...
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
// If you're in a DM you will receive a 403 error for every message the other user sent (you don't have permission to delete their messages).
var before = 'LAST_MESSAGE_ID';
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}));
}).then(() => clearMessages());
}
clearMessages();
Reference: https://gist.github.com/IMcPwn/0c838a6248772c6fea1339ddad503cce
This will work on discord.js version 12.2.0
Just put this inside your client on message event
and type the command: !nuke-this-channel
Every message on channel will get wiped
then, a kim jong un meme will be posted.
if (msg.content.toLowerCase() == '!nuke-this-channel') {
async function wipe() {
var msg_size = 100;
while (msg_size == 100) {
await msg.channel.bulkDelete(100)
.then(messages => msg_size = messages.size)
.catch(console.error);
}
msg.channel.send(`<#${msg.author.id}>\n> ${msg.content}`, { files: ['http://www.quickmeme.com/img/cf/cfe8938e72eb94d41bbbe99acad77a50cb08a95e164c2b7163d50877e0f86441.jpg'] })
}
wipe()
}
This will work so long your bot has appropriate permissions.
module.exports = {
name: "clear",
description: "Clear messages from the channel.",
args: true,
usage: "<number greater than 0, less than 100>",
execute(message, args) {
const amount = parseInt(args[0]) + 1;
if (isNaN(amount)) {
return message.reply("that doesn't seem to be a valid number.");
} else if (amount <= 1 || amount > 100) {
return message.reply("you need to input a number between 1 and 99.");
}
message.channel.bulkDelete(amount, true).catch((err) => {
console.error(err);
message.channel.send(
"there was an error trying to prune messages in this channel!"
);
});
},
};
In case you didn't read the DiscordJS docs, you should have an index.js file that looks a little something like this:
const Discord = require("discord.js");
const { prefix, token } = require("./config.json");
const client = new Discord.Client();
client.commands = new Discord.Collection();
const commandFiles = fs
.readdirSync("./commands")
.filter((file) => file.endsWith(".js"));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
//client portion:
client.once("ready", () => {
console.log("Ready!");
});
client.on("message", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
const command = client.commands.get(commandName);
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}!`;
if (command.usage) {
reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\``;
}
return message.channel.send(reply);
}
try {
command.execute(message, args);
} catch (error) {
console.error(error);
message.reply("there was an error trying to execute that command!");
}
});
client.login(token);
Another approach could be cloning the channel and deleting the one with the messages you want deleted:
// Clears all messages from a channel by cloning channel and deleting old channel
async function clearAllMessagesByCloning(channel) {
// Clone channel
const newChannel = await channel.clone()
console.log(newChannel.id) // Do with this new channel ID what you want
// Delete old channel
channel.delete()
}
I prefer this method rather than the ones listed on this thread because it most likely takes less time to process and (I'm guessing) puts the Discord API under less stress. Also, channel.bulkDelete() is only able to delete messages that are newer than two weeks, which means you won't be able to delete every channel message in case your channel has messages that are older than two weeks.
The possible downside is the channel changing id. In case you rely on storing ids in a database or such, don't forget to update those documents with the id of the newly cloned channel!
Here's #Kiyokodyele answer but with some changes from #user8690818 answer.
(async () => {
let deleted;
do {
deleted = await channel.bulkDelete(100);
} while (deleted.size != 0);
})();

Resources