I'm creating a discord bot that will run a function based on questions asked, of which multiple are yes/no questions. Answering "yes" will do a certain function while "no" should end the question.
I do not want the bot to run a random function every time it reads "yes".
The only solution I could think of is once the question has been asked (based on a word trigger), the user gets stuck in a loop where the bot will not respond to any trigger other than "yes" / "no".
If yes, the related function will run.
If no, the user gets out of the loop and is able to trigger another question.
Other than yes/no, the bot will say "please reply with yes or no"
How can I code this in node.js/discord.js?
If this is a command that the users must run to initiate this conversation...I recommend referencing this in the discord.js official documentaion that talks about awaitMessages. Basically, you could use this followed by a series of .then() and if statements that will take the user down multiple paths based on their answers that can be filtered out.
If this is a default function of the bot without needing a trigger command, I recommend you tread carefully in this area as something like this could be quite spammy.
An example:
message.channel.send('Is this a great bot?').then(async (start) => {
message.channel.awaitMessages(filter, { maxMatches: 1, time: 60000, errors: ['time']}).then(async (collected) => {
if (collected.first().content === "yes") {} else if (collected.first().content === "no") {}
})
You can filter messages with the filter or another way so that yes/no are the only acceptable answers.
Related
Is there a way to have a command with buttons be used by two people at the same time in the same channel?
I made an adventure system which is going well so far, but I found out that only one person can use it in a channel. If another person uses it in the same channel, the "DiscordAPIError: Unknown Interaction" error comes as soon as a button is used. I tried adding the message author's id to the custom ID of the buttons, but it still doesn't work for some reason and I am utterly confused.
From what I understand, I think it's unrelated to there being multiple button instances, but I can't think of any other reason. Here is the filter:
const filter = async (interaction) => {
if (interaction.user.id === message.author.id) return true;
await interaction.deferReply()
await interaction.editReply({ content: `**${interaction.user.username}**, You can't use this button!`, ephemeral: true})
return false
}
And the button click detector goes something like this:
GameCollect.on('collect', async ButtonInteraction => {
ButtonInteraction.deferUpdate()
// My code
})
I have tried:
Removing ButtonInteraction.deferUpdate() from all my click collectors.
Making the custom ids for the buttons have the message author's id at the end and making my code compatible with that change.
Smashing my head on my keyboard from confusion. Didn't work either... (satire)
If it's necessary, I can copy my code into a repl so that you can identify the problem easier, but I don't think the other portions would have any effect on this. I think it's just a piece of code/slight adjustment that I have to add for it to work with two people in the same channel.
EDIT: Alright, here is a source bin so that you can identify the problem better. Check the comments on the recent answer for some clarifications on my testing too. I will keep you updated if I find a solution.
DISCLAIMER: I still haven't found a way to make this work in over a month, and I am essentially just giving up on buttons. Thank you to everyone who helped though.
Try it:
Remove await interaction.deferReply() from filter
Create dynamic customIds like Math.random()
EDIT:
description: i had the same issue recently on my own bot, The problem is that when 2 messages that the have collectors(basically they're waiting for a interaction), if you call await interaction.deferReply() soon, it wil get messy, So here's the solution
First of all lets go for the filter function for checking the interaction, We need 2 things:
Checking the button id with the incoming interaction button id
Checking the user to be the real one
After all of these
delete deferReply from the filter func, Because when you didn't check the interactions, calling deferReply, will call both of messages or all of the messages that are waiting for interaction
add the deferReply to the function that is after filter
And don't remember to use random ids for components(Buttons, select menus and ...)
Instead of creating a collector on the text channel, you should create the collector on the actual message
const GameCollect = message.channel.createMessageComponentCollector({ filter, time: 60000 * 15 })
To
const GameCollect = <Message>.createMessageComponentCollector({ filter, time: 60000 * 15 })
I am making a 2-player rock-paper-scissors game using Discord.js.
Sadly the Discord API is really slow and afaik doesn't provide any type of middleware. When someone chooses their shape, the other person sees the reaction (or chat message) for quite a while until the bot deletes it, therefore ruining the whole game.
The only way of secretly getting an input I could think of, was sending the user a message in private chat, to which he can react. But having to switch from the server to private chat and then back to the server just makes the game unplayable in my opinion. It's just too much work for the user, compared to simply clicking a reaction.
Another option would be sending a message in the chat, which only a specific user can see. It could say something like "1 = Scissors, 2 = Rock, 3 = Paper". (The mapping would be randomized for each player). The user then picks the corresponding reaction from the options 1, 2 and 3.
But it seems, Discord does not allow to send a message in chat, which only a specific user can see. Or is there a way?
And is there any way of secetly getting user-input without the user having to switch chats?
Does the API maybe provide any kind of middle-ware for messages or reactions which I have overlooked?
Discord does not allow to send a message in chat, which only a specific user can see. Or is there a way?
No, there isn't. Discord API doesn't allow you to specify users that can see a specific guild message.
And is there any way of secetly getting user-input without the user having to switch chats?
There definitely is!
You could use a fairly new feature, buttons. Below is an example code, you can use to base your game on. Things left to implement are:
Gamestate, e.g. who is on turn, who has how much points, etc.
Update gamestates (identify gamestate by id?) in the interactionCreate event's callback.
Showing the gamestate to the players, e.g. updating the original message.
Don't allow players to modify the gamestate of other playing pairs.
Let the user specify an opponent in !createGame command.
The actual game logic (determining who won, who gets a point, etc.)
That is all I can think of for now. Take those more of a suggestions than requirements. There is no boundary to ones creativity.
// ... Some object to store the gamestates in? ...
client.on("messageCreate", async (message) => {
// Don't reply to bots
if (message.author.bot) return;
// Basic command handler, just for showcasing the buttons
if (message.content.startsWith("!createGame")) {
// ... Probably some argument handling for the opponent (e.g. a mention) ...
// Create an action row component with buttons attached to it
const actionRow = new Discord.MessageActionRow()
.addComponents(
[["Rock", "🗿"], ["Paper", "🧻"], ["Scissors", "✂️"]].map((buttonProperties) => {
return new Discord.MessageButton()
.setStyle("PRIMARY")
.setLabel(buttonProperties[0])
.setEmoji(buttonProperties[1])
.setCustomId(`rpsgame_${buttonProperties[0].toLowerCase()}`);
})
);
// Send the game message/playground
message.channel.send({
content: "Rock, Paper and Scissors: The game!",
components: [actionRow]
});
}
});
To handle the button clicks, we use the interactionCreate event.
client.on("interactionCreate", (interaction) => {
// If the interaction is a button
if (interaction.isButton()) {
// If the button belongs to our game
if (interaction.customId.startsWith("rpsgame")) {
// Log what the user has selected to console, as a test
console.log(`A user '${interaction.member.user.tag}' selected ${interaction.component.emoji.name}.`);
// Don't forget to reply to an interaction,
// otherwise an error will be shown on discord.
interaction.update("Update GameState...");
}
}
});
No one will see what the other users have selected, unless you want them to.
Using discord.js ^13.0.1. If you are on v12, there is a nice package called discord-buttons.
I'm working on a quiz bot which is setup in the following way:
User type the answer to my questions using !q <answer>. The <answer> gets sent to a text channel that only me can see and the !q command is delete immediately.
However, I tested with 15+ people and sometime answers were visible, even if briefly.
Now, I give points according to the fastest correct answer, so if you wait to see the correct answer you will get few points, if not none.
Nevertheless, I would like to hide somehow the <answer>.
Here are my ideas that I don't know if are possible or how to do them yet:
1) hide the command !q: the bot will get it but it is not shown in chat
2) store the command !q, store the <answer>, change the <answer> then send it to chat (I will get the non-changed answer)
3) I am aware of the fact that I could create a channel for each player, but it will be long to setup. It is a solution, but I would like to try other option before this one.
This is my progress so far:
client.on('message', msg => {
let args = msg.content.substring(PREFIX.length).split(" ");
switch (args[0]){
case 'q':
msg.delete();
msg.reply(":star: Risposta inviata!"); //Answer sent!
client.channels.get("<channel id>").send(msg.author + " " + args[1]); //where args[1] is the answer given
break;
}
});
Thank you for your help and ideas!
Have a nice Sunday
It depends on the speed of the connection from wherever you're hosting your bot and the current ping of the Discord API how long a message takes to delete. You could try to only accept commands in PMs, so if someone wanted to submit an answer, they would have to PM the bot, which means nobody would be able to see their answer.
You cannot modify a users message though, so deleting them or PM commands are the only things I can think of.
I am currently coding my first discord bot, it can already play YouTube music.
if (message.content.includes("Good Job") ||
message.content.includes("good job")) {
message.channel.sendMessage("Good Job everyone :smirk:");
}
As you see, if someone types "good job" (this is just an example) then the bot will reply with "good job everyone :smirk:), but then the spam will begin: the bot reads his own message and replies to it.
How can I prevent the bot from answering itself?
Use this in the on message event:
if (message.author.bot) return;
for more info:
https://anidiotsguide.gitbooks.io/discord-js-bot-guide/coding-guides/a-basic-command-handler.html
The reason why your bot replies to yourself is because where you put:
if (message.content.includes("Good Job") ||
message.content.includes("good job"))
It is basically checking the chat if a piece of text includes the words "Good Job" or "good job". As your bot sends:
message.channel.sendMessage("Good Job everyone :smirk:");
as an answer, it creates a loop because that message includes the words "Good Job", basically running the code again and again and again.
Of course, the easiest way of fixing this is for you to change the answer it gives to not include the words Good Job, but there is better solution to make sure it doesn't happen for all of the commands you might make.
As #Jörmungandr said, under the message event include:
if (message.author.bot) return;
to make sure it doesn't happen. It essentially checks if the author of the message is a bot and if it is, it ignores it.
discord.js
// In message event
if(message.author.id === client.user.id) return;
// I would recommend a variable like this for splits on words
// const args = message.content.trim().split(/\s+/g);
// You could also .slice() off a prefix if you have one
if(/good job/i.test(message.content)) {
message.channel.send('Good job everyone :smirk:'); // sendMessage is deprecated, use send instead
}
You can simply just check if the user sending the message is a bot. For example:
if (!msg.author.bot) {
<Your code to execute if the user is not a bot.>
}
Hope this was helpful, thank you!
You may use this code which avoids doing anything if the author is a bot:
if(message.author.bot) return;
There are quite a lot of ways to solve this.
1: Stopping the Bot from Responding to ANY bot.
Right under your message callback (the client.once('message')), you can just add:
if (message.author.bot) return;
2: Stopping the Bot from Responding to itself
Once again, right under your message callback, you just add:
if (message.author.id == client.user.id) return;
Please note the client that I am referring to is your bot's client, created when you type
const client = new Discord.Client()
...or whatever your client is. It's basically the thing that you login to (ex: client.login(yourtokenhere))
Most tutorials will call this Client, client, or maybe even bot. The discord.js guide will call it a Client.
I'm building a chatbot. Some example of the chat scripts are as follows:
var convpatterns = new Array (
new Array (".*ask me a question*", Question1),
new Array ("I need (.*)" , "Why do you need $1?" , "Are you sure you need $1?"),
new Array (".*sorry.*", "Please dont apologise", "Apologies are not necessary", "it is ok, it didn't bother me")
);
So basically if the user types "ask me a question", it will direct the user to the Question1() function. And if the user types "I need a friend", the chatbot will reply by asking "why do you need a friend?" or "Are you sure you need a friend?".
function Question1(){
var textbox=document.getElementById("messages").value;
window.iSpeech.speak(speakPluginResultHandler,nativePluginErrorHandler,"Do you smoke?");
if (textbox="Yes"){
window.iSpeech.speak(speakPluginResultHandler,nativePluginErrorHandler,"Oh, do you know smoking is bad for your health?");
} else if (textbox="No"){
window.iSpeech.speak(speakPluginResultHandler,nativePluginErrorHandler,"That's great to hear that you don't smoke!");
}
}
The window.ispeech.speak will allow the chatbot to verbally speak the words.
So when the chatbot asks the question, "Do you smoke?", and the user types either "yes" or "no" in the textbox, the chatbot then responds based on the reply.
What I want is for the Question1() function to finish running before the chatbot goes about and asks other things (so it waits for the user to type either "yes" or "no" before the function can be finished and a new script can begin), as I don't want the user's response to clash with the other scripts available in the arrays (e.g. the input "yes" to the textbox might clash with another "Yes" array in the script).
You actually need something like a state machine. But can always hold the current -let's say- command in a variable. Then you always check for the command to be finished. As long as a current command is not set to null(means no current command) you can not listen to another command. Each of you command should have at least one final state. It can be a value that will be entered by user. When the final state condition(s) of current command were meet you can set it to null.