What i am after:
I am developing a discord.js (V14) bot at the moment. The idea is the following. If a member executes the following / command:
/mute target:#member time:1d reason:spamming
Check if target:#member is equal to the bot it self.
My issue:
The below code should in theory (to my knownledge) be enough to check if the target:#member is equal to discord bot. However, my code completely skips that step. Even if i verify via console.log that both values are equal.
The Code:
10258xxxx - console.log(user.id);
10258xxxx - console.log(interaction.guild.members.me.id);
module.exports = {
data: new SlashCommandBuilder()
.setName("mute")
.setDescription("Mute a member.")
.addUserOption(option =>
option.setName("target")
.setDescription("Select the user you wish to mute.")
)
.addStringOption(option =>
option.setName("time")
.setDescription("How long should the mute last?")
)
.addStringOption(option =>
option.setName("reason")
.setDescription("What is the reason of the mute?")
),
async execute(interaction) {
const user = options.getUser("target");
if (user.id === interaction.guild.members.me.id){
return interaction.reply("I can't mute my self.");
}
return interaction.reply("Success");
}
I believe you should use interaction.options.getUser().
const user = interaction.options.getUser('target')
if(user.id == interaction.guild.members.me.id) return await interaction.reply('I can\'t mute myself');
await interaction.reply('Success!'); // don't use return here
Not completely familiar with discord, but it looks like you might need to add the await keyword
return await interaction.reply("I can't mute my self.");
Without going into detail of how async await works, what you could test is the following to see that the line is running:
if (user.id === interaction.guild.members.me.id){
console.log("Can you see me?");
return interaction.reply("I can't mute my self.");
}
If you can see the log, what is happening is the code stops running before waiting for the async function to finish. If you want to understand this better, learn more about async await!
Related
Trying to make a tempmute command on discord and im getting the following error: TypeError: users.hasPermission is not a function
I want to make it so that you cannot use this command on the user who has MANAGE_MESSAGES perms
heres the code Im using, I have been trying to fix this for hours now, any help woks
Thank you :)
const Discord = require('discord.js');
const ms = require('ms');
module.exports = {
category: 'Moderation',
name: 'tempmute',
commands: ['tempmute', 'tm', 'mute'],
description: 'tempmutes the user for a specified time',
requiredPermissions: [
'MANAGE_MESSAGES'
],
callback: (message, args, member, users, tag, id) => {
if(users.hasPermission('MANAGE_MESSAGES')) return msg.reply('You cannot mute that person!');
if (message.member.roles.cache.find(r => r.name === "Muted")) {
message.channel.send(`**${message.mentions.members.first()} is already muted`)
}
var member = message.guild.member(message.mentions.users.first() || message.guild.members.cache.get(args[0]));
if(!member) return message.reply(':nomark: Please Provide a Member to TempMute.')
let reason = args.slice(2).join(" ")
let role = message.guild.roles.cache.find(role => role.name === "Muted");
if (!role) return message.reply(":nomark: Couldn't find the 'Muted' role.")
let time = args[1];
if (!time){
return message.reply(":nomark: You didnt specify a time!");
}
if(!reason) {
return message.reply("Please provide a reason")
}
member.roles.add(role.id);
const embed = new Discord.MessageEmbed()
embed.setDescription(`<:yesmark:791577741002539029> **${message.mentions.members.first()} has been muted for \`${reason}\` for \`${time}\`**`)
member.send(`You are muted in **${message.guild.name}** for \`${reason}\` for \`${time}\``)
message.channel.send(embed);
setTimeout( function () {
member.roles.remove(role.id);
}, ms(time));
},
permissions: 'MANAGE_MESSAGES',
}
The Problem
I want to make it so that you cannot use this command on the user who has MANAGE_MESSAGES perms
Ok, so you want to prevent users from muting people who have the MANAGE_MESSAGES perm. Then what is the users variable? Is it a list of all users with that perm? I'm assuming so, since the variable is a plural users and not user. So how would the list of users have a .hasPermission() method (only a single user has that function property)? And assuming that users is a list of all users with the MANAGE_MESSAGES perm, what is the point in checking if the users in that list have the MANAGE_MESSAGES perm? You already know that all of the users in users have that perm, because users is supposed to be the list of users with that perm.
So let's look at what you want to do again, and build a solution from there: "so you want to prevent users from muting people who have the MANAGE_MESSAGES perm". So let's dissect that further. The user specifies a person they want to mute. You don't want them to be able to mute people who have the MANAGE_MESSAGES perm. So all you need to do is check if the person they specified has that perm. Checking users is not going to work; you need to get the person they specified in args[0] and check if that person has the perm.
Though this solves the problem you are currently experiencing, your code still has some other errors in it which I will also address below.
First of all, when you are checking whether or not the specified person is already muted, you're doing: message.member.roles.cache.find(r => r.name === "Muted"). That doesn't check if the specified person is already muted, it checks if the user that sent the command is muted (and obviously they aren't, otherwise they wouldn't be sending the command).
Second, some of your code isn't really in a logical order. You're defining reason, then checking if the muted role exists, then checking if the time exists, and only after all of that are you checking if reason exists. This isn't technically an error, but it makes it more difficult for people reading the code (and potentially yourself) to follow along. I will be reordering some of this in the below solution.
Third, you aren't adding .catch() statements to your role addition, role removal, and DM-sending lines. If an error occurs when trying to mute someone, you need to catch that error and notify the user that an error occurred. Otherwise, you risk your bot going offline or restarting (due to the error) at a time when your users need your bot to mute someone, and preventing them from muting that person.
The Solution
These are the changes you need to make to your callback in order to fix this. Also note that this assumes that users is a list (array or Collection) of users who have the MANAGE_MESSAGES perm. If that is not the case, I apologize for misunderstanding and this solution may not work for you. But given the information you've presented in your question, I am assuming that you do not need to use the users variable at all for what you are trying to achieve.
callback: (message, args) => {
//Get member by mention or ID
var member = message.guild.member(message.mentions.users.first() || message.guild.members.cache.get(args[0]));
if(!member)
return message.reply(':nomark: Please Provide a Member to TempMute.');
//Prevent muting users with MANAGE_MESSAGES perms
if(member.hasPermission('MANAGE_MESSAGES'))
return message.reply('You cannot mute that person!');
if (member.roles.cache.find(r => r.name === "Muted"))
return message.channel.send(`**${member} is already muted`);
let role = message.guild.roles.cache.find(role => role.name === "Muted");
if (!role) return message.reply(":nomark: Couldn't find the 'Muted' role.")
//Get time
let time = args[1];
if (!time) return message.reply(":nomark: You didnt specify a time!");
//Get reason
let reason = args.slice(2).join(" ")
if(!reason) return message.reply("Please provide a reason");
//Send embed
const embed = new Discord.MessageEmbed()
.setDescription(`<:yesmark:791577741002539029> **${message.mentions.members.first()} has been muted for \`${reason}\` for \`${time}\`**`);
message.channel.send(embed);
//Tell member they are muted
member.send(`You are muted in **${message.guild.name}** for \`${reason}\` for \`${time}\``)
.catch(err => console.log(`Cannot DM user: ${member.user.tag}.`));
//Mute member
member.roles.add(role.id)
.catch(err => message.channel.send(`Could not mute the user: ${member.user.tag}.\nError:\n\`\`\`\n${err.stack}\n\`\`\``));
//Unmute member
setTimeout( function () {
member.roles.remove(role.id)
.catch(err => message.channel.send(`Could not unmute the user: ${member.user.tag}.\nError:\n\`\`\`\n${err.stack}\n\`\`\``));
}, ms(time));
}
I've added comments to several parts of the code to make it a bit more readable, as well.
I'm having a problem with discord-player. When I execute !play URL, it joins my channel, it plays the song and it's perfectly fine, but when instead of passing a link I pass a search query ex. !play Despacito, it gets frozen and then cancels the play event. What is going on? On the video I have a program called NetLimiter4 that is monitoring how much bandwidth is used by any app. You can see that when I type in despacito it tries to search it and then gives up, but when I provide a link, it starts to play it. I checked everything, I even tried to put breakpoints in the Player class. From my observations, it tries to search the query, and it's successful but for some reason, it fails. No stack trace, no nothing
import * as Discord from "discord.js";
import {Player} from "discord-player";
const client = new Discord.Client();
const player = new Player(client);
client.on("ready", ()=>{
console.log("ready");
})
client.on("message", async (message)=>{
let args = message.content.split(" ");
if(args[0] == "!play" && args[1]){
console.log("playin0");
await player.play(message, args[1]);
console.log("playing");
}else if(args[0] == "!stop"){
if(!player.isPlaying(message)) return message.channel.send("Can't stop playing nothing");
player.stop(message);
}
})
player.on("trackStart", (message, track) =>{
message.channel.send(`Started playing ${track.title}!`);
})
client.login("token");
You might be using discord-player above v2 , and yeah discord-player has built in search feature so you need to add this code -
player.on('searchResults', (message, query, tracks) => {
const embed = new Discord.MessageEmbed()
.setAuthor(`Search Reults`)
.setDescription(tracks.map((k, x) => `${x}. ${k.title}`))
.setFooter('Send the number of the song you want to play!')
message.channel.send(embed);
})
where x is the number and k belongs to Track class, Now once the user enters a number like 1 it starts playing the song!
I am implementing a dialogRootBot that will call a skillDialogBot.
My dialogRootBot is able to invoke the skillDialogBot. The conversation can progress until the point where the skillDialogBot is supposed to return the results to the dialogRootBot.
The skillDialogBot has the following setup
this.addDialog(new TextPrompt(TEXT_PROMPT))
.addDialog(new ConfirmPrompt(CONFIRM_PROMPT))
.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
this.processStep.bind(this)
]));
The processStep is laid out like this
async processStep(stepContext) {
const details = stepContext.options;
details.result = {
status: 'success'
};
return await stepContext.endDialog(stepContext.options);
}
I was expecting the dialogRootBot to get the result from the skillDialogBot after processStep has called endDialog, but that never happens. Instead, the user is stuck with the skillDialogBot until the user manually types in "abort", which is the command the dialogRootBot is monitoring to cancel all dialogs in its onContinueDialog() implementation
Here is how the onContinueDialog() looks like
async onContinueDialog(innerDc) {
const activeSkill = await this.activeSkillProperty.get(innerDc.context, () => null);
const activity = innerDc.context.activity;
if (activeSkill != null && activity.type === ActivityTypes.Message && activity.text) {
if (activity.text.toLocaleLowerCase() === 'abort') {
// Cancel all dialogs when the user says abort.
// The SkillDialog automatically sends an EndOfConversation message to the skill to let the
// skill know that it needs to end its current dialogs, too.
await innerDc.cancelAllDialogs();
return await innerDc.replaceDialog(this.initialDialogId, { text: 'Request canceled!' });
}
}
return await super.onContinueDialog(innerDc);
}
I modeled this after the botbuilder-samples\samples\javascript_nodejs\81.skills-skilldialog sample. If I were to change the skillDialogBot and have it do a ConfirmPrompt() before the finalStep()'s endDialog(), then the conversation ends correctly with the skillDialogBot() posting the dialog's results to the rootDialogBot.
For the sake of clarity, this is how the bookingDialog in the skills-skilldialog sample looks like
/**
* Confirm the information the user has provided.
*/
async confirmStep(stepContext) {
const bookingDetails = stepContext.options;
// Capture the results of the previous step.
bookingDetails.travelDate = stepContext.result;
const messageText = `Please confirm, I have you traveling to: ${ bookingDetails.destination } from: ${ bookingDetails.origin } on: ${ bookingDetails.travelDate }. Is this correct?`;
const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput);
// Offer a YES/NO prompt.
return await stepContext.prompt(CONFIRM_PROMPT, { prompt: msg });
}
/**
* Complete the interaction and end the dialog.
*/
async finalStep(stepContext) {
if (stepContext.result === true) {
const bookingDetails = stepContext.options;
return await stepContext.endDialog(bookingDetails);
}
return await stepContext.endDialog();
}
Is it not possible to endDialog() without a prompt? Why is my skillDialogBot unable to endDialog and pass the results to the rootDialogBot?
Thank You
For the sake of reference, when using stepContext.beginDialog(), it appears that we cannot reliably endDialog() if the waterfall dialog does not have a prompt step added. If there is a use case where we want to use a skillDialogBot and call a specific dialog in the skillDialogBot via stepContext.beginDialog(), and the called dialog is only doing processing (eg. call REST APIs) without the need to prompt users for further information, at the end of the processing, we can opt to end the conversation instead.
Hence, assuming we have finalStep() bound to the waterfall dialog as the last step, just use the following in finalStep()
return await stepContext.context.sendActivity({
type: ActivityTypes.EndOfConversation,
code: EndOfConversationCodes.CompletedSuccessfully,
value: stepContext.options
});
At least for my use case, I was able to terminate the dialog without needing a user prompt and got the results back to the rootDialog.
Right now I am making a discord bot (in version 12 of discord.js).
It works like this:
Someone sends and insult
If the insult is included in a list (stored in insultes.json) the bot send a message and adds a reaction
If we add the same reaction the bot sends another message
The problem I'm facing is that if I keep adding the reaction the bot keeps replying 2, 3, 4 times and so on: every time (n) I check the reaction it replies with n+1 messages.
This is the code:
bot.on('message', message => {
const insulte = require('./insultes.json');
for (let p = 0; p < insulte.length; p++) {
// Check if the insult is in the list and make sure it's not from the bot itself
if (message.content.toLowerCase().includes(insulte[p]) && message.author.id !== "711337757435363468") {
message.channel.send("First message").then(messageReaction => {
messageReaction.react("➡️");
});
bot.on('messageReactionAdd', (reaction, user) => {
if (reaction.emoji.name === "➡️" && user.id !== "711337757435363468") {
message.channel.send("Additional message");
}
});
}
}
});
I think your problem comes from the fact that you're using bot.on('messageReactionAdd', ...: that means that every time you run that part of the code the code will add another listener that adds up to the ones that you used before.
Also, that code will trigger when a reaction to any message is added, not just the one you sent.
From your question, I don't understand if the bot is supposed to reply with one message every time you hit the reaction on that message, or just do it once and then ignore the message. I'll assume it's the latter.
Here's my take on this:
bot.on('message', message => {
const insults = require('./insultes.json')
if (insults.some(i => message.content.toLowerCase().includes(i)) && message.author.id !== "711337757435363468") {
message.channel.send("First message").then(myMsg=> {
myMsg.react("➡️");
let reactionFilter = (reaction, user) => reaction.emoji.name === '➡️' && user.id !== "711337757435363468"
myMsg.awaitReactions(reactionFilter, { max: 1 }).then(() => {
myMsg.channel.send('Additional message')
})
});
}
})
As you can see, I'm using Array.some() to check whether any insult is in the message, instead of a for loop. I'm using Message.awaitReactions() to fetch the first reaction and respond to that: after that, the bot will just ignore any other reaction on that message, but will still work on others.
Feel free to let me know if something is not clear or doesn't work :)
Azure BotFramework
SDK4
NodeJS
In dialog state I am using 'return await step.prompt' inside async function. But once user enters the value it is not considering the user input value as a input for prompt instead it is going to luisrecognizer for the match.
I have written the similar code for different dialog where it works fine.
Kindly requesting you provide some valuable input.
Azure BotFramework
SDK4
NodeJS
this.addDialog(new WaterfallDialog('OrderStatusDialog', [
async function(step) {
return await step.context.sendActivity(reply);
},
async function(step) {
var cardValue = step.context.activity.value;
if (cardValue) {
if (cardValue.action == "Order_Number") {
return await step.prompt('textPrompt', 'enter order number');
} else if (cardValue.action == "Serial_Number") {
return await step.prompt('textPrompt', 'enter serial number');
} else {
}
}
return await step.next();
// return await step.endDialog();
},
async function (step) {
var cardValue = step.context.activity;
console.log("****** cardValue");
console.log(cardValue);
console.log("****** step After");
console.log(step);
return await step.endDialog();
}
]));
at prompt step it should read the value what user is entering. Also stack is empty when i console the step ..
Unfortunately, you have a few issues with your code. But, hopefully this will help iron them out. I had no issues running the below code.
One, double check which version of Botbuilder you have installed. At least one call ('this.addDialog') I am unfamiliar with, doesn't appear to be a part of the current SDK, and didn't work for me when testing.
Two, configure your bot like below. Technically, you should be able pass the various steps in as you have them in your question. However, something about yours was not work and I couldn't figure out what. The below setup DOES work, however, and conforms to already accepted practices.
Three, your first step calls 'step.context.sendActivity(reply)'. In the next step you are trying to read the returned value of that call. That isn't going to work as sendActivity simply sends a statement from the bot to the user ("Welcome to my bot!", for example). You need to perform a prompt to capture the user's input response (see below).
It looks like you are trying to read a value from a card. Since you didn't supply that bit of code, I faked the value by supplying the 'Order_Number' and 'Serial_Number' via a text prompt from the user.
You should also take advantage of the bot state options. Rather than use the variable 'cardValue', consider using UserState or DialogState to store user values important to the conversation.
Lastly, in this simple example, order and serial numbers will overwrite each other if you pass thru multiple times.
const START_DIALOG = 'starting_dialog';
const DIALOG_STATE_PROPERTY = 'dialogState';
const USER_PROFILE_PROPERTY = 'user';
const ACTION_PROMPT = 'action_prompt';
const ORDER_PROMPT= 'order_prompt';
const SERIAL_PROMPT= 'serial_prompt';
...
class ExampleBot {
constructor(conversationState, userState) {
this.conversationState = conversationState;
this.userState = userState;
this.dialogState = this.conversationState.createProperty(DIALOG_STATE_PROPERTY);
this.userData = this.userState.createProperty(USER_PROFILE_PROPERTY);
this.dialogs = new DialogSet(this.dialogState);
this.dialogs
.add(new TextPrompt(ACTION_PROMPT))
.add(new TextPrompt(ORDER_PROMPT))
.add(new TextPrompt(SERIAL_PROMPT));
this.dialogs.add(new WaterfallDialog(START_DIALOG, [
this.promptForAction.bind(this),
this.promptForNumber.bind(this),
this.consoleLogResult.bind(this)
]));
}
async promptForAction(step) {
return await step.prompt(ACTION_PROMPT, `Action?`,
{
retryPrompt: 'Try again. Action??'
}
);
}
async promptForNumber(step) {
const user = await this.userData.get(step.context, {});
user.action = step.result;
if (user) {
if (user.action == 'Order_Number') {
return await step.prompt(ORDER_PROMPT, 'enter order number');
} else if (user.action == 'Serial_Number') {
return await step.prompt(SERIAL_PROMPT, 'enter serial number');
} else {
}
}
return await step.next();
}
async consoleLogResult(step) {
const user = await this.userData.get(step.context, {});
user.orderNumber = step.result;
console.log('****** cardValue');
console.log(user);
console.log('****** step After');
console.log(step);
return await step.endDialog();
}
async onTurn(turnContext) {
... //Other code
// Save changes to the user state.
await this.userState.saveChanges(turnContext);
// End this turn by saving changes to the conversation state.
await this.conversationState.saveChanges(turnContext);
}
}
Hope of help!
somehow it worked for me after saving conversation state where ever i am replacing the dialog.
await this.conversationState.saveChanges(context);