How to get user's join position in Discord - javascript

I need a code showing user's server join position. For example:
User: !userinfo
Bot: You're 23rd member of server.

Here is a very basic example. I've tried to explain it with comments as much as I can.
// Import the Client class from Discord.js
const {Client} = require('discord.js')
// Initialise a new client
const client = new Client()
// This will be used later on to determine the ordinal suffix to use
// for 1st, 2nd, and 3rd
const digitToOrdinalSuffix = {
1: 'st',
2: 'nd',
3: 'rd'
}
// Add a listener for the 'message' event (which is emitted when a message is
// sent)
// The {author, channel, content, guild} destructures the message object.
// author is the message's author, channel is the message's channel etc.
client.on('message', async ({author, channel, content, guild}) => {
try {
// If the message content is '!userinfo'...
if (content === '!userinfo') {
// Send a message if it's a DM
if (!guild) return await channel.send('This command can’t be used in DMs!')
// This is the actual part that gets the user's 'join position'
// Get the message server's members...
const result = guild.members.cache
// sort them in ascending order by when they joined...
.sorted((a, b) => a.joinedAt - b.joinedAt)
// convert the Collection to an array so we can use findIndex...
.array()
// and find the position of the member in the array.
// For example, the first member to join the server will be the first
// member of the array (index 0), so they will be the
// 0 + 1 = 1st member to join.
.findIndex(member => member.id === author.id) + 1
// The last digit of the result (0-9)
const lastDigit = result % 10
// The last 2 digits of the result (0-99)
const last2Digits = result % 100
// Send the message
await channel.send(
`You’re the ${result}${
// 4th, 5th, 6th, 7th, 8th, 9th, 10th, 14th, 15th...
lastDigit === 0 || lastDigit > 3 ||
// 11th, 12th, 13th
last2Digits >= 11 && last2Digits <= 13
? 'th'
// 1st, 2nd, 3rd, 21st, 22nd, 23rd...
: digitToOrdinalSuffix[lastDigit]
} member of the server.`
)
}
} catch (error) {
// Log any errors
console.error(error)
}
})
// Login to Discord using your bot token. It is highly recommended that you don't
// literally put your token here as a string; store it somewhere else like a
// config or .env file.
client.login('your token')
If you are planning on adding a lot more commands, I recommend using a better command handler that trims the user input and handles arguments, instead of using if/else if to check if the input matches a string. A good example of this is on the Discord.js guide, which is a very good resource for learning how to code a bot using Discord.js.

Related

Make Web MIDI API listen to a specific channel

I am making a proyect where I'm using the web MIDI API and a loopback MIDI port, but I want to know if there is any way tomakeit listen to different channel other than the first?
Here is my code:
var notes=[]
navigator.requestMIDIAccess()
.then(onMIDISuccess, onMIDIFailure);
function onMIDISuccess(midiAccess) {
for (var input of midiAccess.inputs.values()){
input.onmidimessage = getMIDIMessage;
}
}
function getMIDIMessage(message) {
var command = message.data[0];
var note = message.data[1];
var velocity = (message.data.length > 2) ? message.data[2] : 0; // a velocity value might not be included with a noteOff command
console.log(message)
switch (command) {
case 144: // noteOn
if (velocity > 0) {
noteOn(note, velocity);
} else {
noteOff(note);
}
break;
case 128: // noteOff
noteOff(note);
break;
}
}
function onMIDIFailure() {
console.log('Could not access your MIDI devices.');
}
Also, I haven't seen any property in the MIDI messages related to the channels, so I really don't know how to do it.
The channel is encoded in the first byte. If the value is 144 it means the message is for the first channel. A value of 145 would be for the second channel. Up to 159 which is a note on message for the 16th channel.
The same principle applies to note off messages. A value of 128 is for the first channel. A value of 129 is for the second channel. ...
You can get the index of the channel with a binary operation like this:
const channelIndex = message.data[0] & 0x0F;
The type of event can be checked by shifting the value by four bits.
const eventType = message.data[0] >> 4;
A value of 8 would be a note off message. A value of 9 is a note on message.

How do I import values ​from MongoDB into a Discord embed?

I have all the data I need in the mongoDB Database, I just dont know how I can send parts of the data in an Discord embed.
for example the data:
new birthdaySchema({
User: username.username,
Birthday: birthdayString,
Month: showMonth,
Day: showDay,
Year: showYear
Year, Month and Day are just there to sort the birthdays and birthdayString = Day of Month
I also use moment-timezone (UTC)
const dateTime = moment.utc().format(`DD MM YYYY, hh:mm:ss`);
My goal is with a slash commad like /next-birthdays the data in the embed should look like
${birthdayString} ${Year}\n${User} (and this 10 times, so the the upcoming birthdays of 10 users)
And if the birthday and the actual date and time from moment-timezones (UTC) is matching.
Then the Bot should make a new embed with a title (Happy Birthday) and in the description (Today is ${User}'s Birthday, blah blah blah) and at last it should give that User the birthday role on this server.
Hope someone can help me :)
I have something similar to what might be able to help you :)
This is achieved by using the mongoose library (which I'm guessing you are likely using).
In my code I have a reference to the JS file where my schema is held:
const Messages = require("../models/benSchema2");
Then, you could have a variable and store all entries from the database inside it in an array form, like this:
const messageOrdered = await Messages.find({}).sort({ messages: -1 });
(In my case, I get all entries from the database, sorting by descending order of messages. Perhaps you might be able to sort it based on birthdayString?)
Then you can have a for loop, starting at i=0 and stopping at 9 which should get the nearest 10 birthdays. I have my code in here but it could be refactored to be more efficient. Also, my code works based off user ID data being stored in the database but it should be able to work by refining it to work on usernames.
let content = "";
for (let i = 0; i < messageOrdered.length; i++) {
if (i === 5) {
break;
}
const userID = await client.users.fetch(messageOrdered[i].UserID);
let user = userID.username;
if (i === 0) {
content += `πŸ₯‡ ${i + 1}: ${user} - ${messageOrdered[i].messages}\n`;
} else if (i === 1) {
content += `πŸ₯ˆ ${i + 1}: ${user} - ${messageOrdered[i].messages}\n`;
} else if (i === 2) {
content += `πŸ₯‰ ${i + 1}: ${user} - ${messageOrdered[i].messages}\n`;
} else {
content += `πŸŽ–οΈ ${i + 1}: ${user} - ${messageOrdered[i].messages}\n`;
}
} (bit spaghetti code but oh well :))
Once you get the nearest 10 and they are all added to the content string then in your final MessageEmbed you can do:
.addField("Global Positions (Text):", content)
Then finally send it.
I hope this has helped somehow :)

Random number generator for my discord bot in discord.js

else if (parts[0] === Prefix + 'number') {
message.channel.send(message.author.username + ' What is the minimum number');
if (parts[0] === Math.int) {
var MinNum = (Discord.Message);
console.log("minimum number is " + MinNum);
message.channel.send(message.author.username + ' what is the maximum number');
} if (parts[0] === Math.int) {
var MaxNum = (Discord.Message);
console.log("Maximum number is " + MaxNum);
const RandomNum = Random.int(MinNum, MaxNum);
message.channel.send(Message.author.username + " number is " + RandomNum);
} else if (parts[0] === Math.int == false) {
message.channel.send("Sorry " + message.author.username + " that is an invalid number");
}
}
So this is some code for a random number generator, when someone says -number, my bot asks the user what is the minimun number, when the user puts a number that number is set to the MinNum variable, and the minimun number is logged in my console. After this, it asks the user what is the maximum number, next it is suppose to do the same thing but with the maximum number, after the user inputs the maximum number the random number generator spits out a random number between those two values, and if the user does not put a number my bot says sorry (username) that is an invalid number.
I'm not sure what Math.int is, and why you think that if (parts[0] === Prefix + 'number') it will also be (parts[0] === Math.int). Unfortunately, it won't work like this. You can't accept a command and accept any follow-up messages as a response to your bot's question.
However, this is a nice use case for message collectors. First, you can send the first question and ask for the first limit. Once this message is sent, you can set up a message collector in the channel using channel.createMessageCollector.
It accepts a filter and an options object. With the filter you can check if the incoming message is from the author who typed the command and if their response is a valid number.
createMessageCollector returns a MessageCollector, so you can subscribe to the collect and end events.
The collect event is emitted whenever a message is collected. If this is the first response, you can store it in an array (limits) and send a new message asking for the maximum.
Once the user sends a second valid number, the end event fires, so you can generate a random number and send it to the user. The end event also fires when the max time is reached, so you can check if the reason is the timeout and if it is, send an error message.
You could also create a helper function to generate a random integer between two numbers:
function randomInt([min, max]) {
if (min > max) {
[min, max] = [max, min];
}
return Math.floor(Math.random() * (max - min + 1) + min);
}
And here is the full code:
if (command === 'number') {
const embedMin = new MessageEmbed()
.setTitle(`🎲 Get a random number 🎲`)
.setColor('GREEN')
.setDescription('What is the minimum number?');
await message.channel.send(embedMin);
// filter checks if the response is from the author who typed the command
// and if the response is a valid number
const filter = (response) =>
response.author.id === message.author.id && !isNaN(response.content.trim());
const collector = message.channel.createMessageCollector(filter, {
// set up the max wait time the collector runs
time: 60000, // ms
// set up the max responses to collect
max: 2,
});
// it stores the user responses
const limits = [];
collector.on('collect', async (response) => {
const num = parseInt(response.content.trim(), 10);
limits.push(num);
// if this is the first number sent
if (limits.length === 1) {
const embedMax = new MessageEmbed()
.setTitle(`🎲 Get a random number 🎲`)
.setColor('GREEN')
.addField('Minimum', limits[0])
.setDescription('What is the maximum number?');
// send the next question
message.channel.send(embedMax);
}
});
// runs when either the max limit is reached or the max time
collector.on('end', (collected, reason) => {
console.log({ collected });
if (reason === 'time') {
const embedTimeout = new MessageEmbed()
.setTitle(`🎲 Get a random number 🎲`)
.setColor('RED')
.setDescription(
`I'm sorry, I haven't received a response in a minute, so I'm off now. If you need a new random number again, type \`${prefix}number\``,
);
message.channel.send(embedTimeout);
}
if (limits.length === 2) {
// get a random number
const embedRandom = new MessageEmbed()
.setTitle(`🎲 Get a random number 🎲`)
.setColor('GREEN')
.addField('Minimum', limits[0], true)
.addField('Maximum', limits[1], true)
.setDescription(`Your random number is: ${randomInt(limits)}`);
message.channel.send(embedRandom);
}
});
}

adding regex int to a var

I'm trying to make a irc bot that takes input like this user: +1 I want to have a end result where I can have a main number being added to, from the # someone types with +#.
expected output: #x added: 1 rep: 1 executed[+] second execution
#x added: 1 rep: 2 executed[+]
actual output #x added: +1 rep: +1undefined executed[+] second is identical.
I've tried using Number(commandName), along with toString().replace(/\D\s/g,'') I got some promising results but they seemed to have some problems so I scrapped that code...
so in conclusion how can I add the numbers together and avoid the +?
const tmi = require('tmi.js');
// Define configuration options
const opts = {
identity: {
username: "x",
password: "x"
},
channels: [
"#x"
]
};
// Create a client with our options
const client = new tmi.client(opts);
// Register our event handlers (defined below)
client.on('message', onMessageHandler);
client.on('connected', onConnectedHandler);
// Connect to Twitch:
client.connect();
const totalnum = 0;
// Called every time a message comes in
function onMessageHandler(target, context, msg, self) {
if (self) {
return;
} // Ignore messages from the bot
// Remove whitespace from chat message
let commandName = msg.trim();
var regexadd = /([+]\d*)[^+\s]/;
// If the command is known, let's execute it
if (regexadd.exec(commandName)) {
var totalnum = addem(commandName, totalnum);
console.log(target, `added:`, commandName, `rep:`, totalnum, `executed[+]`)
} else {
console.log(`* Unknown command ${commandName}`);
}
function addem(x, y) {
return (x + y);
}
}
// Called every time the bot connects to Twitch chat
function onConnectedHandler(addr, port) {
console.log(`* Connected to ${addr}:${port}`);
}
I found a few things that appear to be wrong with your code:
You're not adding numbers. addem()'s first parameter is the name of the command, it should be the number captured in your regex capture group.
Your regex includes the + sign in the capture group, you probably wanted to exclude it
You should parse the result of exec to a hint either with ParseInt() or implicitly with +
You use RegExp.prototype.exec() instead of RegExp.prototype.match() to retrieve a capture group.
Here's what this could look like
var regexadd = /\+(\d*)[^+\s]/;
if (regexadd.exec(commandName)) {
var totalnum = addem(+commandName.match(regexadd)[1], totalnum);
console.log(target, `added:`, commandName, `rep:`, totalnum, `executed[+]`)
}
I also think it would be best to use RegExp.prototype.test() instead of RegExp.prototype.exec() for your if statement - you will limit results to true or false.

Firebase dont write

I have a function that deletes matches from users if the match is not played after 15 minutes. if the user that create the match add coins to the match, the function retrieves that coins to the user profile.
here is the code
function deleteMatch(key,user,bet){
MatchesRef.child(key).remove();
UsersRef.child(user).once('value').then(userSnapshot => {
const credits = userSnapshot.val().credits || 0;
return UsersRef.child(user).child('credits').set(credits + parseInt(bet, 10));
})
}
The problem is if the function recieve in the parameters a user that create 2 matchs in the same minute, only add 1 coin.
User1 have 9 coins and user2 have 8 coins.
Example parameters: match1(key1, user1, 1), match2(key2,user2,1)
with this parameters the code work, and now user1 have 10 coins and user2 have 9.
But with differente paramenters
user1 have 9 coins, user2 have 8 coins.
Example parameters with problems:
match1(key1,user1,1), match2(key2,user2,1) match3(key3,user1,1).
With this the code read all the matches, but after the code the user1 have 10 coins, and user 2 have 9 coins.
The code is not giving 2 coins, 1 for every match that user1 create.
I think this is because firebase are trying to write to the user node 2 times at once. But no idea how to solve
The code that starts the action
module.exports = () =>
MatchesRef.once('value').then(snap => {
const now = new Date().getTime();
const records = [];
snap.forEach(row => {
const { date, hour, adversary1, bet } = row.val();
console.log("el adversario es "+ adversary1);
if (!date || !hour) return;
const [day, month] = date.split('/').map(p => parseInt(p, 10));
const [h, min] = hour.split(':').map(p => parseInt(p, 10));
const scheduledTime = (new Date((new Date()).getFullYear(), month - 1, day, h, min, 0, 0))
.getTime() + expireMinutes * 60 * 1000;
if (scheduledTime <= now) {
records.push({ key: row.key, user: adversary1, bet });
deleteMatch(row.key,adversary1,bet);
//records.map(({key,user,bet}) => deleteMatch({key,user, bet}))
}
});
return Promise.all(
// records.map(({key, user, bet}) => expireMatch(key, user, bet))
);
});
The problem was firebase try to write 2 times at the same location and to avoid this problem I implement transactions, who guarantee that firebase wait until the previous write was finished.
UsersRef.child(user).child('credits').transaction(function(currentCredits){
console.log(currentCredits);
return currentCredits + parseInt(bet, 10);
})

Categories