My friend has a discord server called Find A Friend and he wants me to code a bot to type a command to ask a series of questions and put it in a channel. People can see if anybody interests them and becomes friends with them. I have already coded part of it.
One of the questions it asks is: What games do you like?
My friend wants to make it able that the bot searches the other answers to this same question and dm the user if they have a game in common. I want to do the same for another question: What are some shows, movies, and anime that you like?
Here is my code for the series of questions:
const { Client, Message, MessageEmbed } = require("discord.js")
module.exports = {
name: "faf",
/**
* #param {Client} client
* #param {Message} message
* #param {String[]} args
*/
run: async (client, message, args) => {
message.delete()
const questions = [
"What is your usernames for any games you have including discord (is fine if you dont put them all but you MUST include your discord)",
"What is your age (not required)",
"What is your gender",
"Do you talk in Voicechannel or through text?",
"What games do you like?",
"What are some shows, movies, and anime that you like",
"Where are you from (not required)",
"Please state anything else you want to say"
]
let collectCounter = 0;
let endCounter = 0;
const filter = (m) => m.author.id === message.author.id;
const appStart = await message.author.send(questions[collectCounter++]);
const channel = appStart.channel;
const collector = channel.createMessageCollector(filter);
collector.on("collect", () => {
if(collectCounter < questions.length) {
channel.send(questions[collectCounter++])
} else {
channel.send('Your Find A Friend thread has been sent, Good Luck!')
collector.stop("fulfilled");
}
});
const appsChannel = client.channels.cache.get("831292911437086760");
collector.on('end', (collected, reason) => {
if(reason === 'fulfilled') {
let index = 1;
const mappedResponses = collected
.map((msg) => {
return `${index++}) ${questions[endCounter++]}\n-> ${msg.content}`;
}).join('\n\n')
appsChannel.send(
new MessageEmbed()
.setAuthor(
message.author.tag,
message.author.displayAvatarURL({ dynamic: true })
)
.setTitle('Friend Thread')
.setDescription(mappedResponses)
.setColor('RED')
.setTimestamp()
)
}
});
},
};
TL;DR I currently have a message collector bot that records responses to questions. How can I make the bot recognize when two people share common interests, such as games, movies, shows, or anime, based on their responses?
Related
I am having issues with my rank system, when I attempted to level up (by spamming), I started to notice that I don't actually reach the required amount of XP before leveling up (I have an image below to further explain this).
I've asked a few people about the issue, but they can't seem to know the problem, so here I am.. asking the wonderful people of stackoverflow!
I have tried a few things to try to fix the issue, but at this point I feel like its a dead-end, perhaps I'm doing something wrong, or just don't fully understand it, or maybe I am calculating the XP wrong, no idea at this time.
Below I have provided my code for the portion I have in the messageCreate event, and I've also provided my code the the entire rank command in hopes that I would get some assistance or a point in the right direction on what I need to do to solve this issue.
messageCreate.js
const Levels = require("discord-xp");
Levels.setURL("");
client.on("messageCreate", async (message) => {
if (!message.guild) return;
if (message.author.bot) return;
const xp = Math.floor(Math.random() * 29) + 1;
const hasLeveledUp = await Levels.appendXp(message.author.id, message.guild.id, xp);
if (hasLeveledUp) {
const user = await Levels.fetch(message.author.id, message.guild.id);
message.channel.send({ content: `<a:CL_Confetti:909438449910681680> ${message.author} has reached **level ${user.level}**!` });
}
...
});
rank.js
const { MessageEmbed, MessageAttachment } = require("discord.js");
const Levels = require("discord-xp");
const canvacord = require("canvacord");
module.exports = {
name: "rank",
aliases: [],
description: "Displays a users current rank!",
usage: "<user>",
run: async (client, message, args) => {
let target = message.mentions.users.first()
? message.mentions.users.first()
: message.author;
const user = await Levels.fetch(target.id, message.guild.id, true);
if (!user) return message.reply("That user does not have any xp.");
const rank = new canvacord.Rank()
.setAvatar(target.displayAvatarURL({ dynamic: false, format: "png", size: 512 }))
.setCurrentXP(user.cleanXp)
.setRequiredXP(Levels.xpFor(user.level + 1))
.setRank(user.position)
.setLevel(user.level)
.setStatus(message.member.presence.status)
.setProgressBar("#DC143C", "COLOR")
.setUsername(target.username)
.setDiscriminator(target.discriminator);
rank.build().then((data) => {
const attachment = new MessageAttachment(data, "rank.png");
message.reply({ files: [attachment] });
});
},
};
Image
I also have this image here of what it is doing inside discord, any help would be greatly appreciated as I have recently taken my first steps into canvacord, and am willing to learn from this issue!
I found the issue.
In messageCreate.js you have an XP mulitpleir. The problem is that its also not reading rank.js/its not required.
const xp = Math.floor(Math.random() * 29) + 1;
.setRequiredXP(Levels.xpFor(user.level + 1)) more or less your multiplier is surpassing the amount required in my opinion, and with the two different files your assigning 2 different values, so it gets called in rank.js and then it checks if it has surpassed the xp limit. rank.js added not messagecreate but messagecreative is updating the value by that multiplier without checking whatever updates the leveling in rank js which is basically 1.
You don't need messagecreate.js you can technically just check if someone has did whatever message.js is doing. and also take out.
ANYWAYS the real issue is that in messagejs its checking if it leveled up and then responding so you are leveling up without being told the rank embed to update nor the value of hasLeveled up when it does level up in messageCreate.
I'm creating a game in ASK (Alexa Skills Kit) where a Alexa will ask the product between two random numbers in a range specified by the user. When the user answers, Alexa should tell them if the answer is correct or incorrect (UserAnswerIntentHandler), and then ask a new question (GamePlayIntentHandler).
const GamePlayIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GamePlayIntent';
},
async handle(handlerInput){
const attributesManager = handlerInput.attributesManager;
const attributes = await attributesManager.getSessionAttributes() || {};
var lowestNum = attributes.lowestNum
var highestNum = attributes.highestNum
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min)
}
var randomOne = getRandomIntInclusive(lowestNum, highestNum)
var randomTwo = getRandomIntInclusive(lowestNum, highestNum)
attributes.randomOne = randomOne
attributes.randomTwo = randomTwo
const speakOutput = `What is ${randomOne} times ${randomTwo}?` //Here, alexa is asking for the product between two random numbers in a range the user previously specified.
return handlerInput.responseBuilder
.addElicitSlotDirective('answer', {
name: 'UserAnswer',
confirmationStatus: 'NONE'
})
.speak(speakOutput)
.reprompt(`Sorry, I didn't get that. What is ${randomOne} times ${randomTwo}?`)
.getResponse();
}
}
const UserAnswerIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'UserAnswerIntent';
},
async handle(handlerInput) {
const {requestEnvelope} = handlerInput
const {intent} = requestEnvelope.request
const attributesManager = handlerInput.attributesManager;
const attributes = await attributesManager.getSessionAttributes() || {};
var userAnswer = parseInt(Alexa.getSlotValue(requestEnvelope, 'answer'))
const randomOne = attributes.randomOne
const randomTwo = attributes.randomTwo
var correctAnswer = randomOne * randomTwo
var points = 0
var speakOutput = ""
if (userAnswer > 0){
if (userAnswer === correctAnswer){
points +=1
speakOutput = `That's correct! You now have ${points} points.`
return handlerInput.responseBuilder
.speak(speakOutput)
.addDelegateDirective({
name: 'GamePlayIntent',
confirmationStatus: 'NONE'
})
.getResponse();
}//If the answer is correct, the user gets a point and Alexa should immediatley ask the next question
else{
speakOutput = `Your answer is incorrect. The correct answer is ${correctAnswer}. You currently have ${points} points.`
return handlerInput.responseBuilder
.speak(speakOutput)
.addDelegateDirective({
name: 'GamePlayIntent',
confirmationStatus: 'NONE'
})
.getResponse();
}//If the answer is incorrect, Alexa will give the correct answer and total points and immediatley go to the next question
}
}
}
What's happening right now is that Alexa isn't telling the user if their answer is correct or incorrect, and is immediately asking the next question.
Is there any way to fix this or create a loop such that as soon as the user answers, Alexa can evaluate the answer, and then immediately ask the next question?
When the GamePlayIntent is triggered, Alexa will ask a question then get the answer provided by the user.
The answer should then trigger UserAnswerIntent.
At this point, UserAnswerIntentHandler is triggered and goes to the handle(handlerInput) where you have your logic to verify the answer.
Once done, Alexa gives the correct answer then chain with the new question in the same message string.
You can build you string message by having one part for verifying the answer and a second part for the next question, you concatenate the strings together and return it in the same responseBuilder .speak(), followed by a repromt to make sure the user is giving an answer.
Once the user's input is captured, it will again triggered the corresponding intent to verify the new answer.
Another way would be to ask the user if they would like to have a new question through YesIntent/NoIntent.
If the answer is yes then you redirect them to the GamePlayIntent using
return GamePlayIntentHandler.handle(handlerInput);
If the answer is no then you add a goodbye message and close the session or so depending on the skill functionality/logic.
Here are some sample skills (1, 2) I found that you can get guided by, one includes questions/answers handled in a different way.
my little problem is the next one :
When i react a message, my bot do nothing :D and it's not the objectives.
My events :
module.exports = async(client, messageReaction, user) => {
const message = messageReaction.message;
const member = message.guild.members.cache.get(user.id);
const emoji = messageReaction.emoji.name;
const channel = message.guild.channels.cache.find(c => c.id === '739163072064651296');
const lol = message.guild.roles.cache.get("763104201588473896"); //id role lol
const wz = message.guild.roles.cache.get("763104236119785584"); //id role wz
if (["lol", "wz"].includes(emoji) && message.channel.id === channel.id) {// lol = emoji name and wz too
switch (emoji) {
case "lol":
member.roles.add(lol);
message.channel.send('test')
break;
case "wz":
member.roles.add(wz);
message.channel.send('test')
break;
};
};
};
My events handler :
fs.readdir("./Events/",(error , f) => {
if(error) console.log(error);
console.log(`${f.length} events chargΓ©s`);
f.forEach((f) => {
const events = require(`./Events/${f}`);
const event = f.split(".")[0];
client.on(event, events.bind(null, client));
});
});
If someone can help it's perfect ! Thank you !
I tried to understand what you wanted to make but couln't understand you at all, your trying to make a autoRole sytem that listen's to reactions? AutoRole is supposed to be in a guildMemberAdd event, so if a member join's, you add a role automatically, but from the code you provided, it looks like it listen's to reactions on some message? If your trying to make a Reaction Role system, then follow this. Other than that, i can't help you.
If your trying to make a autoRole system follow this
From what i know, when you add a role to someone, you will have to do the following
member.roles.add("roleid")
But what your doing is basically this:
member.roles.add(message.guild.roles.cache.get("763104201588473896")) //as lol is defined like that
So, to fix it, instead of getting the role object, then trying to add the role to the user, do this, and be sure to make the file name "guildMemberAdd.js":
module.exports = async(client, member) => {
const lol = "763104201588473896" //id role lol
const wz = "763104236119785584"; //id role wz
member.roles.add(lol);
member.roles.add(wz);
};
if (!args[1]) return message.channel.send('You need to specify a person!')
if (!args[2]) return message.channel.send('Please specify a time for how long the ban council will last.')
var tim = args[2]
const sweden = message.mentions.users.first()
message.react('π').then(() => message.react('π'))
const mappo = ['π', 'π']
if (!args[1]) return message.channel.send('Please specify a person!')
if(message.guild){ //this will check if the command is being called from a server
const embad = new Discord.MessageEmbed()
.setTitle('Ban Council')
.addField('The Convicted:', `${sweden.tag}`)
.addField('Rules:', 'Vote on whether the convicted is guilty or not with the prompted reactions. The ban will end automatically in 5 seconds.')
message.channel.send(embad)
setTimeout(function(){
if(sweden){
const lyft = message.guild.member(sweden)
if(lyft){
if(message.reactions.cache.map(r => `${'π'} ${r.users.cache.size}`)[0] > message.reactions.cache.map(r => `${'π'} ${r.users.cache.size}`)[1]){
lyft.ban({ ression: 'Majority has exiled you from server. '}).then(() => {
message.reply(`The user ${december.tag} was banned as a result of a majority vote.`)
})
} else {
message.channel.send('The ban was cancelled.')
}
}else{
message.reply('The user is not in this server.')
}
}else{
message.reply('You need to specify a person!')
}
}, tim)
} else {
message.channel.send('Banning does not work here!')
}
It sends the "Ban cancelled" before it actually has the chance to take input. I've tried collectors, and it doesn't work because of the max: part, how can I resolve this problem? (Also it returns no errors)
This is in a case. (appending onto what feedback I got)
Firstly you should beautify your code before you post on stackoverflow, you could have removed the break keyword and just explain it was inside of a switch case
The reason it does not work is because you are checking the message's reaction, and not the embed that you send, so to fix this you need to assign a variable to message.channel.send(embad), but this is a promise so you need to await it, which requires an async function
lastly awaitReactions and createReactionCollector are probably better options,
So here's the new code:
(async () => {
if (!args[1]) return message.channel.send('You need to specify a person!');
if (!args[2]) return message.channel.send('Please specify a time for how long the ban council will last.')
if (!message.guild) return message.channel.send('Banning does not work here');
var tim = args[2]
const sweden = message.mentions.member.first()
const mappo = ['π', 'π']
message.react('π').then(() => message.react('π'))
const embad = new Discord.MessageEmbed()
.setTitle('Ban Council')
.addField('The Convicted:', `${sweden.tag}`)
.addField('Rules:', 'Vote on whether the convicted is guilty or not with the prompted reactions. The ban will end automatically in 5 seconds.')
const embedMessage = await message.channel.send(embad);
setTimeout(function () {
if (!sweden) return message.reply('You need to mention a person');
const filter = (reaction) => mappo.includes(reaction.emoji.name);
embad.awaitReactions(filter, { time: 5000 })
.then(collection => {
//not the most optimal way to do it
const emoji1 = collection.find(e => e.emoji.name === 'π');
const emoji2 = collection.find(e => e.emoji.name === 'π');
//both default to 0
const upvotes = emoji1 && emoji1.users.size || 0;
const downvotes = emoji2 && emoji2.users.size || 0;
if (upvotes > downvotes) {
lyft.ban({ reason: 'Majority has exiled you from server. ' })
.then(() => message.reply(`The user ${december.tag} was banned as a result of a majority vote.`));
} else {
message.channel.send('The ban was cancelled.');
}
})
.catch(console.error);
}, tim);
})();
It's been around 8 months since I made this post, and I found the answer, and an even more effective way to do it. I won't post the entire code, as it's pretty long and not very neat. However, this is a much more effective way of counting reactions.
My Original Code was:
const upvotes = message.reactions.cache.map(r => ${'π'} ${r.users.cache.size})[0]
const downvotes = message.reactions.cache.map(r => ${'π'} ${r.users.cache.size})[1]
It doesn't work very well either.
const [upvoteReaction, downvoteReaction] = message.reactions.cache.first(2);
const upvotes = upvoteReaction.users.cache.size;
const downvotes = downvoteReaction.users.cache.size;
It takes the number of reactions from the first two reactions on the message. Seeing as how the only reactions are thumbs up and thumbs down, it will get the numbers from both of them. From there, you can just compare the two.
I tried to find some answer for this, but got nothing relative. I need to implement something like form as row of Q&A, for example:
U: /form
B: Enter your name
U: John
B: Hello, John. Are you 12 years old?
...
and so on. And I'm going to write answers in Google Spreadsheets, but there's no problem, I just don't know how to create a chain on phrases with bot
Thanks for attention
Okay so the basis of my answer is on a Node library I use for making telegram bots in all my projects. It's called telegram-node-bot, the way other libraries would do it will differ but I like this method cause it lifts all of the heavy load for me and easy to understand.
Here we go:
const Telegram = require('telegram-node-bot');
const TelegramBaseController = Telegram.TelegramBaseController;
const TextCommand = Telegram.TextCommand;
const tg = new Telegram.Telegram('YOUR_TOKEN'); //Ask botfather for your token
class FormController extends TelegramBaseController {
/**
* #param {Scope} $
*/
formHandler($) {
const form = {
name: {
q: 'Send me your name',
error: 'Sorry, wrong input',
validator: (message, callback) => {
const answer = message.text;
if (answer) {
callback(true, answer);
return
}
callback(false);
}
},
age: {
q: 'Send me your age',
error: 'sorry, wrong input',
validator: (message, callback) => {
const answer = message.text;
if (answer && IsNumeric(answer)) {
callback(true, toInt(answer))
return
}
callback(false)
}
}
}
$.runForm(form, (result) => {
console.log(result)
})
}
get routes() {
return {
'formCommand': 'formHandler'
}
}
}
tg.router
.when(
new TextCommand('/form', 'formCommand'),
new FormController()
)
The logic/algorithm behind it is simple:
Ask a question.
Wait for a response.
Check if the response fits the criterias you've set for the right answer format
If false ask the question again.
If true move to the next question.