How get array of reactions Discord JS - javascript

I want get info about reactions but i can't. I need get reaction and authors this reactions from old message without listeners or something.
This code make something similar https://gist.github.com/acollierr17/c9e7aaf9eba97d8659b59395b5f2046d but don't work for me, var is empty. But i can get message m.channel.fetchMessage(ID)
I don't understand why this does not work, although the message is received to var. How collect all reactions and their authors of message to array?
I try this and few other ways.
if(calcreact == 1 && msg.content.startsWith('/calcreact')) { //start
calcreact = 0; // защита от флуда
let msgid = finddata(msg.content);
//let channel = 709517297664131082;
msg.channel.fetchMessage(msgid)
.then(m => {
//console.log('Message:', message.content);
let reactions = m.reactions;
let reaction = reactions.first();
let users = reaction.users.map((u) => u.toString());
console.log(users);
//console.log(m.reactions.fetch());
//console.log(m.reactions.forEach((reaction) => {}));
m.reactions.forEach((reaction) => console.log(reaction));
m.reactions.forEach((reaction) => console.log(reaction.users));
m.reactions.forEach((reaction) => console.log(reaction.users.map((u) => u.toString())) );
//console.log('Reactions:', );
});
setTimeout(antiflood, 500, 'calcreact');
} //end
From m.reactions.forEach((reaction) => console.log(reaction)); i can get huge info "array" and in marked current reaction(It seems to me https://i.imgur.com/OazszNR.png) but deep (users) empty. This is horrible...

The function bellow groups reactions by users, like this:
{
userId1: ['reactionId1', 'reactionId2'],
userId2: ['reactionId1', 'reactionId2'],
...
}
You can adapt this to get the users and reactions from messages, or any context you like.
OBS: It was written in TypeScript, but if you remove the typing, it turns into JavaScript.
Function:
import { MessageReaction } from "discord.js";
interface IReactionsByUsers {
[userId: string]: Array<string>;
}
DiscordClient.on("messageReactionAdd", (reaction: MessageReaction) => {
const reducer = (acc: IReactionsByUsers, cur: [string, MessageReaction]) => {
const [reactionId, reaction] = cur;
const usersIds = [...reaction.users.cache].map(([userId]) => userId);
for (const userId of usersIds) {
if (acc[userId]) {
acc[userId].push(reactionId);
} else {
acc[userId] = [reactionId];
}
}
return acc;
};
const messageReactions = [...reaction.message.reactions.cache];
const reactionByUsers = messageReactions.reduce(
reducer,
{} as IReactionsByUsers
);
return reactionByUsers;
});

use the client.on("messageReactionAdd" method and save the reactions in an array.

Related

issues with placeholders, with discord.js v14, mangodb

this is my ../Events/Guild/guildMemberAdd.js https://sourceb.in/iEEfLj7uM7
im trying to set placeholders that will in turn give out an output like
Welcome to OnlyScoped.gg #azz#5271! We're glad to have you as the 500th member.
but output is
Welcome to OnlyScoped.gg <#undefined>! We're glad to have you join us as the undefinedth member.`
../Commands/Moderation/setup-welcome.js
const {Message, Client, SlashCommandBuilder, PermissionFlagsBits} = require("discord.js");
const welcomeSchema = require("../../Models/Welcome");
const {model, Schema} = require("mongoose");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup-welcome")
.setDescription("Set up your welcome message for the discord bot.")
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addChannelOption(option =>
option.setName("channel")
.setDescription("Channel for welcome messages.")
.setRequired(true)
)
.addStringOption(option =>
option.setName("welcome-message")
.setDescription("Enter your welcome message.")
.setRequired(true)
)
.addRoleOption(option =>
option.setName("welcome-role")
.setDescription("Enter your welcome role.")
.setRequired(true)
),
async execute(interaction) {
const {channel, options} = interaction;
const welcomeChannel = options.getChannel("channel");
const welcomeMessage = options.getString("welcome-message");
const roleId = options.getRole("welcome-role");
if(!interaction.guild.members.me.permissions.has(PermissionFlagsBits.SendMessages)) {
interaction.reply({content: "I don't have permissions for this.", ephemeral: true});
}
welcomeSchema.findOne({Guild: interaction.guild.id}, async (err, data) => {
if(!data) {
const newWelcome = await welcomeSchema.create({
Guild: interaction.guild.id,
Channel: welcomeChannel.id,
Msg: welcomeMessage,
Role: roleId.id
});
}
interaction.reply({content: 'Succesfully created a welcome message', ephemeral: true});
})
}
}
../Models/Welcome.js
const { model, Schema } = require("mongoose");
let welcomeSchema = new Schema({
Guild: String,
Channel: String,
Msg: String,
Role: String,
});
module.exports = model("Welcome", welcomeSchema);
im attempting to use string.replace()but its not working as expected
i decided to put it in guildMemberAdd.js since when a member joins this gets runs so it would be unwise to place it in setup-welcome.js or Welcome.js since those are not listening for anything.
for reference here's my package.json:
https://sourceb.in/FMBgygjyoh
for the record i cant find any of the id's like member.id or member.count so those are wild guesses as to what they are. it could very well just be that as im still learning v14 this is my first project in it.
one other way i thought could work is if i just pass it off as an interpolated string in mongodb but it seems that the only string is with "" so i cant use default ones like ${member.count} so i decided to add placeholders
The basics of formatting a template are this:
const string = "Welcome to OnlyScoped.gg {tagUser}! We're glad to have you as the {memberCount} member.";
string = string.replace(/{tagUser}/g, member.toString());
string = string.replace(/{memberCount}/g, '500th');
return string; // "Welcome to OnlyScoped.gg <#123456789012345678>! We're glad to have you as the 500th member.";
To make something extensible, put template strings like this somewhere in your configuration:
{
"welcome_message": "Welcome to OnlyScoped.gg {tagUser}! We're glad to have you as the {ordinal:memberCount} member."
}
and make a function
function formatMessage(template, lookup) {
let output = template;
output = output.replace(/{ordinal:([^}]*)}/g, (_, target) => withOrdinalSuffix(lookup(target)));
output = output.replace(/{([^}]*)}/g, (_, target) => lookup(target));
return output;
}
// https://stackoverflow.com/a/31615643/3310334
// turn 1 into '1st', 500 into '500th', 502 into '502nd'
function withOrdinalSuffix(n) {
var s = ["th", "st", "nd", "rd"],
v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
and then use the function with a template and the lookup function:
client.on('guildMemberAdd', member => {
const welcomeMessageTemplate = config.welcome_message;
const memberCount = member.guild.members.filter(member => !member.user.bot).size;
const lookup = (item) => {
const items = {
memberCount,
tagUser: member.toString()
};
return items[item];
};
const welcomeMessage = formatMessage(welcomeMessageTemplate, lookup);
const welcomeChannel = member.guild.channels.cache.find(channel => channel.name === 'welcome');
welcomeChannel.send(welcomeMessage);
});
The main issue I can see is incorrect property names as you mention in the question.
DiscordJS Docs: GuildMember
member.id => The Members ID
member.user.username => The Members username
member.guild.name => The Server's Name
member.guild.memberCount => Number of users within the Server
I'd advise the user to input data in a specific format like Hello {userName}!. Then you could run a program like
while (string.includes('{userName}')) {
string = string.replace('{userName}', member.user.username);
}

I can't reply or react to collected message. Discord.js V13

So, I wanted to add command /custom and after that, user would enter they nicknames through messages. Bot should check if ther nickname is in game (async function checkIfSummonerExist(m)) and if it does exist, bot should collect their name and data and push to array (async function getSummonerProfile(m.content)). Now I wanted to add bot reactions to those messages, if nickname exist, it should add one reaction (for example thumbs up), and if name does not exist one should add thumbs down. So I tried with m.react(), but it does not work. I also tried with m.reply("User approved")but I don't get reply. I am new to making discord bot.
const { SlashCommandBuilder } = require("#discordjs/builders");
const { getSummonerProfile } = require("./../functions/summonerData");
const { calculateRank } = require("./../functions/calculateRank");
let arrayOfSummoners = [];
async function checkIfSummonerExist(m) {
const test = await getSummonerProfile(m.content);
if (test) {
return true;
} else {
return false;
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName("custom")
.setDescription(
"Enter the name of the user."
),
async execute(interaction) {
await interaction.deferReply();
// `m` is a message object that will be passed through the filter function
const filter = (m) => checkIfSummonerExist(m);
const collector = interaction.channel.createMessageCollector({
filter,
time: 15000,
});
collector.on("collect", (m) => {
m.reply('User approved');
m.react('😄');
arrayOfSummoners.push(getSummonerProfile(m.content));
});
collector.on("end", (collected) => {
// return interaction.editReply(`Collected ${collected.size} items`);
// calculateRank(arrayOfSummoners);
});
// return interaction.editReply();
},
};

How to stop MessageCollector with custom message

I have slash command that collects messages from users. Users should input their nicknames from the game, and then bot should return in embeded 2 teams. I managed to filter and collect nicknames, and call calculation functions. This works if I enter maximum number of nicknames, but now I want to add feature, where I would manually stop collecting by sending message "stop". Now I don't know where to add condition
if(m.content==="stop") {collector.stop();}
const { SlashCommandBuilder } = require("#discordjs/builders");
const { getSummonerProfile } = require("./../functions/summonerData");
const { MessageEmbed } = require("discord.js");
let arrayOfSummoners = [];
let arrayOfNicknames = [];
async function checkIfSummonerExist(m) {
const test = await getSummonerProfile(m.content);
if (test && m.author.bot == false && !arrayOfNicknames.includes(m.content)) {
m.react("👍");
return true;
} else if (test == false && m.author.bot == false) {
m.react("👎");
return false;
}
if (arrayOfNicknames.includes(m.content)) {
m.react("👎");
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName("custom")
.setDescription("Enter the name of the user."),
async execute(interaction) {
await interaction.deferReply();
interaction.editReply("Insert users");
const exampleEmbed = new MessageEmbed()
.setColor("#0099ff")
.setTimestamp();
// `m` is a message object that will be passed through the filter function
const filter = (m) => checkIfSummonerExist(m);
const collector = interaction.channel.createMessageCollector({
filter,
max: 4,
});
if(m.content==="stop")
{
collector.stop();
}
collector.on("collect", (m) => {
arrayOfNicknames.push(m.content);
exampleEmbed.addFields({
name: "Regular field title",
value: m.content, inline:true
});
});
collector.on("end", (collected) => {
interaction.followUp({ embeds: [exampleEmbed] });
});
},
};
If I add this condition in filter fuction (checkIfSummonerExist(m), I get error collector is not defined and I if call it in execute, like in example above, I get error m is not defined
I would put the if statement inside the collector.on callback. Something like this:
collector.on("collect", m => {
if(m.content === "stop") return collector.stop()
// Other code
})

Get discord member's roles by ID

I am looking to get the id of every role a user has. the user being the message author in my case.
i've tried various things suggested such as let roles = message.author.roles.id = [] and message.guild.roles.fetch() however nether seem to do what I need.
var XPRole = {};
if (fs.existsSync('noxprole.json')) {
XPRole = jsonfile.readFileSync('noxprole.json');
}
client.on('message', async (message) => {
const { member } = message;
if (message.author.bot) return;
if (message.guild.id in XPRole === false) {
XPRole[message.guild.id] = {};
}
const RoleID = XPRole[message.guild.id];
if ('...' in RoleID) {
return;
}
Any help would be appreciated.
The intention of this code is to compare a users roles against a JSON file containing a list of roles that wont earn xp when typing.
As Elitezen mentioned in their comment, message.author is a User and Users don't have roles, only GuildMembers. You also can't use message.guild.roles as it returns a manager of the roles belonging to this guild, not just those belonging to the member.
You can however get the author of a message as a guild member by using the message.member property. Now, that you have the member, you can get the roles of them by accessing the member's roles. As the roles property returns a manager, you need to access its cache property:
const roles = message.member.roles.cache;
It returns a collection of the roles of this member, and collections have a .map() method that maps each item to another value into an array. It means that you can easily create an array of role IDs this member has using roles.map:
const memberRoleIDs = roles.map((role) => role.id);
// => ['772088200214723861', '809320713752256548', '8184301957544091683']
As you can see, memberRoleIDs contains all the role IDs the member has.
I'm not sure how you store the role IDs in XPRole[message.guild.id]. It seems you store it as an object and the IDs are the keys. In that case, you can use the Array#some() method that tests if at least one element in the array passes the test implemented by the callback function.
So, if your XPRole[message.guild.id] object with IDs as keys, you can simply compare these keys to the array of role IDs. Check and run the following snippet:
let message = {
guild: {
id: 'guildID'
}
}
const memberRoleIDs = [
'7720882002147238612',
'8093207137522565483',
'8093207137522565481'
];
let XPRole = {
'guildID': {
'7437501734702175013': { /**** */ },
'8093207137522565481': { /**** */ },
'9483513543347850432': { /**** */ },
}
}
const containsRole = memberRoleIDs.some(id => id in XPRole[message.guild.id]);
// returns true as 8093207137522565481 is in both
console.log({ containsRole });
If you instead store an array of role IDs like in the following example, you can still use the same some() method but with a different callback, one that checks if any of the user role IDs is included in your XPRole[message.guild.id] array:
let message = {
guild: {
id: 'guildID'
}
}
const memberRoleIDs = [
'7720882002147238612',
'8093207137522565483',
'8093207137522565481'
];
let XPRole = {
'guildID': [
'7437501734702175013',
'8093207137522565481',
'9483513543347850432',
]
}
const containsRole = memberRoleIDs.some(id => XPRole[message.guild.id].includes(id));
// returns true as 8093207137522565481 is in both
console.log({ containsRole });
Here is the full code:
let XPRole = {};
if (fs.existsSync('noxprole.json')) {
XPRole = jsonfile.readFileSync('noxprole.json');
}
client.on('message', async (message) => {
if (message.author.bot) return;
const roles = message.member.roles.cache;
const memberRoleIDs = roles.map((role) => role.id);
if (message.guild.id in XPRole === false) {
XPRole[message.guild.id] = {};
}
if (memberRoleIDs.some((id) => id in XPRole[message.guild.id])) {
return console.log('member has at least one role id included in XPRole');
}
});
You can iterate through all the roles of the member. So you will need to collect the GuildMember object instead of the User.
const RoleID = XPRole[message.guild.id];
for(var memberRoleID in message.member.roles.cache.array()) {
if (RoleID.includes(message.member.roles.cache.array()[memberRoleID].id)) {
return;
}
}

Firebase Cloud Function error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array

I want to send a notification to all users who are confirmed guests when the object confirmedGuests is created in the Firebase Realtime Database.
So, I first create an array of all the users from confirmedGuests object. Then, I iterate through all these users and push their deviceTokens to an array of deviceTokens. The array allDeviceTokens is expected to be the array of device tokens of all users in confirmedGuests.
However, when confirmedGuests object is created, the function returns an error.
Below is my cloud function
exports.sendNotification = functions.database
.ref('/feed/{pushId}/confirmedGuests')
.onCreate((snapshot, context) => {
const pushId = context.params.pushId;
if (!pushId) {
return console.log('missing mandatory params for sending push.')
}
let allDeviceTokens = []
let guestIds = []
const payload = {
notification: {
title: 'Your request has been confirmed!',
body: `Tap to open`
},
data: {
taskId: pushId,
notifType: 'OPEN_DETAILS', // To tell the app what kind of notification this is.
}
};
let confGuestsData = snapshot.val();
let confGuestItems = Object.keys(confGuestsData).map(function(key) {
return confGuestsData[key];
});
confGuestItems.map(guest => {
guestIds.push(guest.id)
})
for(let i=0; i<guestIds.length; i++){
let userId = guestIds[i]
admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
let userData = tokenSnapshot.val();
let userItem = Object.keys(userData).map(function(key) {
return userData[key];
});
userItem.map(item => allDeviceTokens.push(item))
})
}
return admin.messaging().sendToDevice(allDeviceTokens, payload);
});
You're loading each user's device tokens from the realtime database with:
admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
This load operation happens asynchronously. This means that by the time the admin.messaging().sendToDevice(allDeviceTokens, payload) calls runs, the tokens haven't been loaded yet.
To fix this you'll need to wait until all tokens have loaded, before calling sendToDevice(). The common approach for this is to use Promise.all()
let promises = [];
for(let i=0; i<guestIds.length; i++){
let userId = guestIds[i]
let promise = admin.database().ref(`/users/${userId}/deviceTokens`).once('value', (tokenSnapshot) => {
let userData = tokenSnapshot.val();
let userItem = Object.keys(userData).map(function(key) {
return userData[key];
});
userItem.map(item => allDeviceTokens.push(item))
return true;
})
promises.push(promise);
}
return Promise.all(promises).then(() => {
return admin.messaging().sendToDevice(allDeviceTokens, payload);
})

Categories