Bot not replying correctly in a private message - javascript

So, this code block is part of a code that player A (challenger) issues a challenge to player B (target) for a game. The bot sends a private message to player B to tell them they were challenged and to ask if they accept or decline the challenge.
The following code doesn't seem to respond to what player B replies with.
if (message.channel.id === '541736552582086656') return target.send("Do you accept the challenge? Please reply with 'accept' or 'deny'.")
.then((newmsg) => {
newmsg.channel.awaitMessages(response => response.content, {
max: 1,
time: 150000,
errors: ['time'],
}).then((collected) => {
if (collected === 'accept') {
newmsg.channel.send("You have ***accepted*** the challenge. Please wait while your battlefield is made...");
} else if (collected === 'deny') {
newmsg.channel.send("You have ***denied*** the challenge.")
}
}).catch(() => {
newmsg.channel.send('Please Accept or Deny the challenge.');
});
});
}
Prior to this code block, I set up logging a message to a channel on the server, sending the challenger and the target the challenge info. The bot successfully contacts the target that they were challenged via pm, but the reply (even when replying with "accept" will still think it was declined.
Thanks for any and all help!

Expanding upon #Stock Overflaw's answer, awaitMessages will always return a collection of fetched messages, meaning collected === 'accepted' won't work. You're checking if a collection object is the same as a string.
What you need is to grab the first (and in your case only) message from the collection and check its content against a string. Below you will find your .then(...) statement re-written. Give it a go and let me know what the result is.
P.s. your collection filter won't work as you might expect. The filter only checks if a message will be added to the collection or not. Since your 'filter' is response => response.content, it will only check if the response.content is not empty, null or undefined
.then((collected) => {
// Grabs the first (and only) message from the collection.
const reply = collected.first();
if (reply.content === 'accept'){
reply.channel.send("You have ***accepted*** the challenge. Please wait while your battlefield is made...");
} else if (reply.content === 'deny') {
reply.channel.send("You have ***denied*** the challenge.")
} else {
reply.channel.send("Your response wasn't valid.");
/*
* Code here to handle invalid responses
*/
}
})

Related

How to make your bot to listen to your messages after you entered a command?

I am making a Google Assistant Discord bot, but I want to know how your bot will reply to your second message. For example:
first, you say hey google, then the bot says I'm listening, and then you say what time is it and he says 2.40 pm.
I did the first part but I don't know how to make it replying to the second argument. Can someone help me with it?
You can use a message collector. You can send an I'm listening message and in the same channel set up a collector using createMessageCollector.
For its filter, you can check if the incoming message is coming from the same user who want to ask your assistant.
You can also add some options, like the maximum time the collector is collecting messages. I set it to one minute, and after a minute it sends a message letting the user know that you're no longer listening.
client.on('message', async (message) => {
if (message.author.bot) return;
if (message.content.toLowerCase().startsWith('hey google')) {
const questions = [
'what do you look like',
'how old are you',
'do you ever get tired',
'thanks',
];
const answers = [
'Imagine the feeling of a friendly hug combined with the sound of laughter. Add a librarian’s love of books, mix in a sunny disposition and a dash of unicorn sparkles, and voila!',
'I was launched in 2021, so I am still fairly young. But I’ve learned so much!',
'It would be impossible to tire of our conversation.',
'You are welcome!',
];
// send the message and wait for it to be sent
const confirmation = await message.channel.send(`I'm listening, ${message.author}`);
// filter checks if the response is from the author who typed the command
const filter = (m) => m.author.id === message.author.id;
// set up a message collector to check if there are any responses
const collector = confirmation.channel.createMessageCollector(filter, {
// set up the max wait time the collector runs (optional)
time: 60000,
});
// fires when a response is collected
collector.on('collect', async (msg) => {
if (msg.content.toLowerCase().startsWith('what time is it')) {
return message.channel.send(`The current time is ${new Date().toLocaleTimeString()}.`);
}
const index = questions.findIndex((q) =>
msg.content.toLowerCase().startsWith(q),
);
if (index >= 0) {
return message.channel.send(answers[index]);
}
return message.channel.send(`I don't have the answer for that...`);
});
// fires when the collector is finished collecting
collector.on('end', (collected, reason) => {
// only send a message when the "end" event fires because of timeout
if (reason === 'time') {
message.channel.send(
`${message.author}, it's been a minute without any question, so I'm no longer interested... 🙄`,
);
}
});
}
});

How does the .awaitmessage feature work in discord js?

i'm trying to make a command where one user can challenge another user to earn coins. I've done this in a way that the person who wants to challenge is p1 and the person he #s is the p2. How can i await a message from that specific (p2) to check wether he accepts or denies? Here is my code so far.
name: "duel",
description: "Challenge a player to win extra <:kcoin:810191130385317913>",
type: "Shop",
minArgs: 2,
maxArgs: 2,
argsError: `<#user> <amount>`,
callback: async (message, args) => {
if (args < minArgs || args > maxArgs){
message.channel.send(argsError)
return
}
if (message.mentions.members.first() == undefined){
message.channel.send("**Challenge someone by # them.**")
return
}
if (isNaN(args[1])){
message.channel.send("**Use a number for the amount.**")
return
}
const p1id = message.author.id
const p2id = message.mentions.members.first().user.id
const amount = args[1]
message.channel.send(`**<#${p1id}> has challenged <#${p2id}> to a duel for ${amount*2}** <:kcoin:810191130385317913>`)
message.channel.awaitMessages({})
}
}
Refer to the discord.js guide for more info, but if I understand correctly, then all you have to do is create a filter.
In this case, the filter you should implement is simple. To just filter whether or not the message is from the second person, you can just do:
const filter = response => {
//this checks whether the person who wrote the message is the # person
return response.author.id === p2id;
};
This is only a simplistic filter, there's other things you can check, such as if the person who is the target has formatted it properly, and you can respond back.
As for implementing awaitMessages, you can follow the formatting of the discord.js guide has provided.
Code:
const filter = response => {
//this checks whether the person who wrote the message is the # person
return response.author.id === p2id;
};
message.channel.send(`**<#${p1id}> has challenged <#${p2id}> to a duel for ${amount*2}** <:kcoin:810191130385317913>`).then(() => {
//this sets a time constraint, you can remove it, it's not required
message.channel.awaitMessages(filter, { time: 30000 /* time is in ms */, errors: ['time'] })
.then(collected => {
//responds back to channel that the user has responded
message.channel.send(`${collected.first().author.username} has responded!`);
})
.catch(collected => {
message.channel.send("Time to respond timed out.")
});
});

Discord.js Bots // Trying to add multiple bots in main file, setting status', randomize welcome messages, multiple prefixes

I planned to create a discord server with bots. There are quite a lot (6 in total) and are just supposed to be fictional characters with some background story. I'm quite new and all of that is way too complicated for me to code myself, therefor I ask for your help! I just want to have a nice server for my friends and I with enjoyable bots and all of these desperate hours of trying to get some useful code is driving me nuts..
I only managed to get one bot to do stuff, using the prefix "-".
It can change it's status (watching, listening, playing) and the name of the thing he's doing.
I'm not quite sure why streaming doesn't work or if that's possible in general but it would be really cool if it would.
My status code: (1st Problem)
client.once('ready', () => {
console.log('Bot is ready!');
if (config.activity.streaming == true) {
client.user.setActivity(config.activity.game, {type: 'WATCHING'}); //STREAMING, PLAYING, LISTENING
} else {
client.user.setActivity(config.activity.game, {url: 'https://twitch.tv/usrname'});
client.user.setStatus('idle'); //dnd, idle, online, invisible
}
});
config.json
"activity": {
"streaming": true,
"game": "Whatevergame"
}
}
As I said, streaming is not working for some reason and the status (idle, dnd..) is also not working.
2nd Problem
If I try to add other bots with the login, it will log both bots on, but only one of them will work, what's actually pretty logical since the commands are all made for only one bot. So I'm trying to figure out how to get them all packed into the main file.
3rd Problem
I used the try - catch function to execute commands, which I pre- set up, and if theres none, it sends an error message. See for yourself:
client.on('message', message =>{
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
try {
client.commands.get(command).execute(message, args);
}
catch {
message.channel.send("I don't know that, sorry.");
}
});
So everytime I type another command, from which I do not want the bot to respond to, it will respond with "I don't know[...]" It would be sufficient to just set up another prefix for the "other command" to fix that problem so the bot knows that for every prefix starting with a.e "-", it has to send an error message if that command is not existing. But for other prefixes, a.e "?", it's supposed to execute the other command/s.
4th Problem
My (current) last problems are the welcome messages. My code:
index.js
const welcome = require("./welcome");
welcome (client)
welcome.js
module.exports = (client) => {
const channelId = '766761508427530250' // welcome channel
const targetChannelId = '766731745960919052' //rules and info
client.on('guildMemberAdd', (member) => {
console.log(member)
const message = `New user <#${member.id}> joined the server. Please read through ${member.guild.channels.cache.get(targetChannelId).toString()} to gain full access to the server!`
const channel = member.guild.channels.cache.get(channelId)
channel.send(message)
})
}
The code is working perfectly fine, however it would be way more exciting with a little more variety. I'm trying to get multiple welcome messages that get randomly chosen by the bot.. I thought about a Mathfloor as an approach but I'm not quite sure how that would work..
Thank you for reading through my text and I hope that I will soon be able to enjoy the server with my guys!
Cheers!
First problem
I'm not sure why ClientUser.setActivity() and ClientUser.setStatus is not working. In the STREAMING example, it might be because you didn't specify the type of activity. Either way, there's an easier way to what you're doing, which is ClientUser.setPresence(). This method is kind of like a combination of the other two.
client.once('ready', () => {
console.log('Bot is ready!');
config.activity.streaming
? client.user.setPresence({
activity: { name: config.activity.game, type: 'WATCHING' },
})
: client.user.setPresence({
activity: {
name: config.activity.game,
type: 'STREAMING',
url: 'https://twitch.tv/usrname',
},
status: 'idle', // note: the idle icon is overwritten by the STREAMING icon, so this won't do much
});
});
Second Problem
It's pretty hard to make multiple bots, both duplicates of each other, in one file. I would recommend just using a lot of Array.prototype.forEach() loops to apply all events and such to both clients.
[1, 2, 3].forEach((num) =>
console.log(`The element I'm currently iterating a function through is ${num}`)
);
// example main file
const { Client, Collection } = require('discord.js');
const [roseClient, sunflowerClient] = [new Client(), new Client()];
// adding properties:
[roseClient, sunflowerClient].forEach((client) => client.commands = new Collection())
// events
[roseClient, sunflowerClient].forEach((client) =>
client.on('message', (message) => {
// ...
});
);
// login
roseClient.login('token1');
sunflowerClient.login('token2');
Third problem
Again, forEach() loops save the day (❁´◡`❁). This time, however, you should actually use Array.prototype.every(), which will return true if every element of an array passes the given test.
Basically, if we were to use a normal forEach() loop, then even if one of the prefixes found the match, the other wouldn't and the error message would always be sent out. So instead we'll use every() to only send out an error message if both prefixes find no match.
// what if we ony wanted the error message if *every* number was 3
[1, 2, 3].forEach((num) => {
if (num === 3) console.error('Error message');
});
console.log('--------------------');
// now it will only send if all numbers were three (they're not)
if ([1, 2, 3].every((num) => num === 3))
console.error('Error message');
client.on('message', (message) => {
if (['-', '?'].every((prefix) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
try {
// it did not pass the test (the test being finding no match), and the error message should not be displayed
return false;
client.commands.get(command).execute(message, args);
} catch {
// it did pass the test (by finding no match). if the next test is failed too, the error message should be displayed
return true;
message.channel.send("I don't know that, sorry.");
}
});
});
Fourth Problem
You're on the right track! Math.floor() is definitely the right way to get a random element from an array.
function chooseFood() {
// make an array
const foods = ['eggs', 'waffles', 'cereal', "nothing (●'◡'●)", 'yogurt'];
// produce a random integer from 0 to the length of the array
const index = Math.floor(Math.random() * foods.length);
// find the food at that index
console.log(`Today I will eat ${foods[index]}`);
};
<button onClick="chooseFood()">Choose What to Eat</button>
module.exports = (client) => {
client.on('guildMemberAdd', (member) => {
console.log(member);
const welcomes = [
`Welcome ${member}!`,
`Woah! Didn't see you there ${member}; welcome to the server!`,
`${member} enjoy your stay at ${member.guild}!`,
];
const message = `${
welcomes[Math.floor(Math.random() * welcomes.length)]
} Please read through ${member.guild.channels.cache.get(
'766731745960919052'
)} to gain full access to the server!`;
member.guild.channels.cache.get('766761508427530250').send(message);
});
};
That was a mouthful (╯°□°)╯︵ ┻━┻

Trying to fetch a member based on their nickname/displayname. Discord.js BOT

Hi am i trying to take a user specified nickname for a member and fetch that members and get additional information about that user. Right now i have confirmed the ARGS[0] sent by the user is correct but i am getting a NULL response to the matched user. Not sure what i am missing. Thanks
This is my current code. Just trying to get the match working right now. I also need to consider if the person doesnt have a nickname to check the username. Or think would displayname property be better. Thanks
if(command === "memberinfo") {
let sentNickname = args[0];
message.channel.send(`Sent Nickname: ${sentNickname}`);
const discordserver = client.guilds.get(DragonTS); // Define server to get information from
discordserver.fetchMembers() // Fetch guild members
.then() //.then(console.log)
.catch(console.error);
}
let matchedMember = discordserver.members.find(m => m.nickname === sentNickname);
message.channel.send(`Matched Member ${matchedMember}`);
Looks like some parts of your code aren't executed. You need to put all your code into the .then():
if(command === "memberinfo") {
let sentNickname = args[0];
message.channel.send(`Sent Nickname: ${sentNickname}`);
const discordserver = client.guilds.get(DragonTS); // Define server to get information from
discordserver.fetchMembers() // Fetch guild members
.then((serverWithFetchedMembers) => {
let matchedMember = serverWithFetchedMembers.members.find(m => m.nickname === sentNickname);
message.channel.send(`Matched Member ${matchedMember}`);
}) //.then(console.log)
.catch(console.error);
}
It will wait for the fetchMembers() function and execute your code after it!

Discord.js - Getting users last activity?

I'm trying to find out if its possible to get the time/information of users last activity retrospectively using discord.js
Say I have something like
client.guilds.find('id', 'SERVER ID').fetchMembers().then(members => {
const role = members.roles.find('name', 'Newbies')
role.members.forEach(member => {
console.log(member.user.lastMessage) // null
})
})
Unless the member has posted, since the client is listening, the lastMessage is always null.
Is there a way to find the last activity? or a workaround, like a query to return all the users messages, which I can then take the most recent one from?
Effectively I want to know what date/time the user last posted so we can monitor non-contributing accounts.
Thanks
After looking thought the documentation I didn't find something neither so I came up with a manual search function.
Basically, it will scan every channels until finding a message from X user, or the end of the messages in the channel. It then compare the last messages of the users from every channels and print the last one.
It can be very long if the user hasn't write since a long time. Of course, you have to check lastMessage before trying this.
I would add a time limit maybe. Because if you have thousand of messages, the function will run eternally.
You can stop the function if the last message found is in the accepted time to not be kick/do whatever.
I made the search stop if the first message found in the pack of fetched messaged is older than the ban limit, however, if the first message is not older, remember that it means for the other, so we still need to check them (it can be avoided by checking the last message of the pack as well).
async function fetchMessageUser(chan, id, res) {
let option = {};
if (typeof res !== 'undefined'){
option = {before: res.id};
}
return await chan.fetchMessages(option)
.then(async msgs => {
if (msgs.size === 0){
return {continue: false, found: false};
};
if ((Date.now() - (msgs.first().createdTimestamp)) > 86400000 ) { // 1 day
return {continue: false, found: false};
}
let msgByAuthor = msgs.find(msg => {
return msg.author.id === id;
});
if (msgByAuthor === null){
return {continue: true, id: msgs.last().id};
} else {
return {continue: false, found: true, timestamp: msgByAuthor.createdTimestamp};
}
})
.catch(err => console.log('ERR>>', err));
}
client.on('message', async (msg) => {
let timestamp = [];
for (let [id, chan] of msg.guild.channels){
if (chan.type !== 'text'){ continue; }
let id = '587692527826763788'; // id of the user, here a non verified account
let res;
do {
res = await fetchMessageUser(chan, id, res);
} while(res.continue);
if (res.found) {
timestamp.push(res.timestamp);
}
}
console.log(timestamp);
let first = timestamp.sort((a,b) => (b-a))[0];
console.log(new Date(first));
});
A better variant would be to run it for an array of users, checking all 50 last messages from every channel, and associating each users with his most recent messages if he wrote one, and doing this until all the messages in all the channels are too old to avoid a kick/whatever. And then do something for all the users who don't have an associated messages.
I think what you need is one of Discord's built in features, namely: pruning. This feature will grab inactive members and lets you kick them. Luckily, discord.js has an API call for it and even lets you get the number of members first without actually kicking them by setting the dry parameter to true. The feature will also allow you to specify the amount of days a user has to be inactive.
Have a look at the docs: https://discord.js.org/#/docs/main/stable/class/Guild?scrollTo=pruneMembers
Hope that helps out!

Categories