I have a command serverinfo and it shows how many bans there are on the server. If the server has 1200 bans, the bot will show that the server has 1000 bans.
Is it possible to somehow make the bot shows how many bans really are?
In the documentation the following parameters are specified limit?| number | number of users to return (up to maximum 1000) | default 1000
How to change default 1000 to e.g. 1500?
I am using discord.js vers 13.8.0 and node.js version 16.15.1.
For a v13 discord.js solution to your problem, this is what I did.
I edited this to include the while loop.
let completeBanIdList = await (async (a = [], last = 0, limit = 1000) => {
while(limit === 1000){
let bans = await guild.bans.fetch({after: last, limit: limit});
let banlist = bans.map(user => user.user.id);
last = bans.last().user.id;
limit = banlist.length;
for(let i = 0; i < limit; i++){a.push(banlist[i]);}
}
return a;
})();
// below is just console logging to show that it worked properly...
let banIdObj = ((o = {}) => {
for(let i = 0; i < completeBanIdList.length; i++){
o[completeBanIdList[i]] = 1;
}
return o;
})();
console.log(`unique ban id count:`, completeBanIdList.length, `should equal number of id keys in object:`, Object.keys(banIdObj).length);
Although the max limit is 1000, you can use the limit and before options to fetch the previous bans, too. before is a snowflake, it tells Discord to consider only bans before this id.
So, if you have around 1200 bans, you can fetch the first 1000, grab the id of the last item in the returned collection and use its id as the before option in your second fetch:
let firstHalf = await guild.bans.fetch({ limit: 1000 });
let secondHalf = await guild.bans.fetch({ before: firstHalf.last().id, limit: 1000 });
You can also create a new function that fetches more than 1000 bans. There is an example below. This function returns a collection of GuildBans, so you can use the usual collection methods, like filter, each, etc.
async function fetchMoreBans(guild, limit = 2000) {
if (!guild || typeof guild !== 'object')
throw new Error(`Expected a guild, got "${typeof guild}"`);
if (!Number.isInteger(limit))
throw new Error(`Expected an integer, got ${typeof limit}.`);
if (limit <= 1000) return guild.bans.fetch({ limit });
let collection = new Collection();
let lastId = null;
let options = {};
let remaining = limit;
while (remaining > 0) {
options.limit = remaining > 1000 ? 1000 : remaining;
remaining = remaining > 1000 ? remaining - 1000 : 0;
if (lastId) options.before = lastId;
let bans = await guild.bans.fetch(options);
if (!bans.last()) break;
collection = collection.concat(bans);
lastId = bans.last().id;
}
return collection;
}
Example usage:
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
let banList = await fetchMore(message.guild, 1500);
let filteredBanList = banList.filter(({ user }) =>
user.username.toLowerCase().startsWith('e'),
);
console.log(`There are ${banList.size} bans fetched`);
console.log(
`Number of bans where the user's username starts with the letter "e": ${filteredBanList.size}`,
);
});
I currently have some code to detect how many times you can click a reaction in a certain amount of seconds.
I am trying to make a leaderboard for everyone that saves the top 10 with the highest CPS (clicks per second). The code works perfect otherwise, but I am stuck on the leaderboard.
This is my code:
let CPStest = new Discord.MessageEmbed()
.setColor("#8f82ff")
.setTitle("CPS Test")
.setDescription(`Alright, ${message.author.username}. Ready to begin your CPS test? All you have to do is spam click this reaction above ^ as fast as you can, you have 10 seconds before the timer runs out. Good Luck!`)
.setFooter("Note: This may be inaccurate depending on Discord API and Rate Limits.")
message.channel.send(CPStest)
message.react('🖱️');
// Create a reaction collector
const filter = (reaction, user) => reaction.emoji.name === '🖱️' && user.id === message.author.id;
var clicks = 1 * 3; // (total clicks)
var timespan = 10; // (time in seconds to run the CPS test)
const collector = message.createReactionCollector(filter, { time: timespan * 1000 });
collector.on('collect', r => {
console.log(`Collected a click on ${r.emoji.name}`);
clicks++;
});
collector.on('end', collected => {
message.channel.send(`Collected ${clicks} raw clicks (adding reactions only)`);
clicks *= 2;
message.channel.send(`Collected ${clicks} total approximate clicks.`);
var cps = clicks / timespan / 0.5; // Use Math.round if you want to round the decimal CPS
message.channel.send(`Your CPS averaged ~${cps} clicks per second over ${timespan} seconds.`);
let userNames = '';
const user = (bot.users.fetch(message.author)).tag;
userNames += `${user}\n`;
let cpsLeaderboard = new Discord.MessageEmbed()
.setColor("#8f82ff")
.setTitle("Current CPS Record Holders:")
.addFields({ name: 'Top 10', value: userNames, inline: true },
{ name: 'CPS', value: cps, inline: true })
.setTimestamp()
message.channel.send(cpsLeaderboard)
});
If you want to have a leaderboard, you either need to save it to a file, use a database, or (if you're okay with it that you'll lose it when you restart your bot) you can declare a variable outside of your client.on('message'). It seems to be good enough for testing purposes.
You can create a topUsers array, push the player to that every time the game ends, then sort it and create a leaderboard.
Check the following snippet:
const topUsers = [];
client.on('message', (message) => {
if (message.author.bot) return;
let CPStest = new Discord.MessageEmbed()
.setColor('#8f82ff')
.setTitle('CPS Test')
.setDescription(
`Alright, ${message.author.username}. Ready to begin your CPS test? All you have to do is spam click this reaction above ^ as fast as you can, you have 10 seconds before the timer runs out. Good Luck!`,
)
.setFooter(
'Note: This may be inaccurate depending on Discord API and Rate Limits.',
);
message.channel.send(CPStest);
message.react('🖱️');
// Create a reaction collector
const filter = (reaction, user) =>
reaction.emoji.name === '🖱️' && user.id === message.author.id;
let clicks = 1 * 3; // (total clicks)
const timespan = 10; // (time in seconds to run the CPS test)
const collector = message.createReactionCollector(filter, {
time: timespan * 1000,
});
collector.on('collect', (r) => {
console.log(`Collected a click on ${r.emoji.name}`);
clicks++;
});
collector.on('end', (collected) => {
message.channel.send(
`Collected ${clicks} raw clicks (adding reactions only)`,
);
clicks *= 2;
message.channel.send(`Collected ${clicks} total approximate clicks.`);
const cps = clicks / timespan / 0.5;
message.channel.send(
`Your CPS averaged ~${cps} clicks per second over ${timespan} seconds.`,
);
topUsers.push({ tag: message.author, cps });
let cpses = '';
let usernames = '';
topUsers
// sort the array by CPS in descending order
.sort((a, b) => b.cps - a.cps)
// only show the first ten users
.slice(0, 10)
.forEach((user) => {
cpses += `${user.cps}\n`;
usernames += `${user.tag}\n`;
});
const cpsLeaderboard = new Discord.MessageEmbed()
.setColor('#8f82ff')
.setTitle('Current CPS Record Holders:')
.addFields(
{ name: 'Top 10', value: usernames, inline: true },
{ name: 'CPS', value: cpses, inline: true },
)
.setTimestamp();
message.channel.send(cpsLeaderboard);
});
});
I'm trying to search Discord User input for integers attached to attributes (Number of attacks, skill of attack, strength, toughness, save) and then run those args through some calculations.
At the moment the function uses the order of args in the command:
!odds 5 3 4 5 3
which works, but I want something like this:
!odds at: 5 sk: 3 st: 4 t: 5 sv: 3
And the bot will search the user input for values attached to those attributes in any order and use those integers for its calculations.
In addition, if attributes aren't present the bot will not include them in the output (or assign default values) and by adding additional, single phrase attributes with no attached integer (for example fnp or res) it would then add another standard calculation and output.
This is what I have so far:
const { Message } = require('discord.js')
module.exports = {
name: 'odds',
aliases: ['stats', 'probability'],
usage: '[#attacks (1 = auto hit)] [skill] [strength] [toughness] [save (7 = no save)]',
description: 'What are the odds?',
execute(message, args) {
let attack = args[0]
let skill;
if (args[1]>=7) skill = 7;
else skill = args[1];
let strength = args[2]
let toughness = args[3]
let save;
if (args[4]<=6) save = args[4];
else save = 7;
let hits = parseFloat((attack*((7-skill)/6)).toFixed(2))
let hitSixes = parseFloat((attack*(1/6)).toFixed(2))
let woundFactor;
if (strength>=(2*toughness)) woundFactor = 2;
else
if (strength>toughness) woundFactor = 3;
else
if (strength==toughness) woundFactor = 4;
else
if (toughness>=(2*strength)) woundFactor = 6;
else
if (strength<toughness) woundFactor = 5;
let wounds = parseFloat((hits*((7-woundFactor)/6)).toFixed(2))
let woundSixes = parseFloat((hits*(1/6)).toFixed(2))
let unsavedWounds = parseFloat(((wounds-(wounds*((7-save)/6))).toFixed(2)))
message.channel.send(`On average you will make:\n${hits} successful hits (with ${hitSixes} sixes)\n${wounds} successful wounds (with ${woundSixes} sixes)\nGiving a total of ${unsavedWounds} unsaved wounds.`)
}}
Any and all help greatly appreciated!
// what you accept:
const inputs = {
at: 0,
sk: 0,
st: 0,
t: 0,
sv: 0,
}
for (let i = 0, arg; arg = args[i]; i++) {
const index = arg.indexOf(':'); // it must have an ":" as last character
if (index === -1) {
continue;
}
const keyword = arg.substr(0, index - 1); // get the word
if (!inputs.hasOwnProperty(keyword)) { // check if it's an element of inputs
continue;
}
if (i + 1 === args.length) { // make sure enough args are available
continue;
}
inputs[keyword] = isNaN(args[i + 1]) ? 0 : +args[i + 1]; // make sure it's a number
i++; // skip the next element
}
let attack = inputs.at;
let skill = inputs.sk >= 7 ? 7 : inputs.sk;
let strength = inputs.st;
let toughness = inputs.t;
let save = inputs.sv <= 6 ? inputs.sv : 7;
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
OneR, short for "One Rule", is a simple yet accurate classification algorithm that generates one rule for each predictor in the data, then selects the rule with the smallest total error as its "one rule".
I tried to find code samples on GitHub, but found only one, developed with R language. How could I implement this algorithm in Javascript?
What I have tried?
I am trying to implement following this sample article:
https://www.saedsayad.com/oner.htm
class OneR {
/**
* Pass dataset which will be an array of values.
* Last value is classifcator's value.
* All other values are predictors.
*
* Example
*
* The meaning of sequence values:
* |Outlook|Temp|Humidity|Windy|Play Golf|
*
* Representation of a sequence:
* ['rainy', 'hot', 'high', 0, 0]
*
* True and False are represented as zeros or ones
*/
constructor(data = []) {
this.data = data;
this.frequences = {};
}
predict() {
if (this.data && this.data.length > 0) {
const firstRow = this.data[0];
const predictorCount = firstRow.length - 1;
let classifcator;
// For each predictor,
for (let i = 0; i < predictorCount; i++) {
// For each value of that predictor, make a rule as follos;
for (let y = 0; y < this.data.length; y++) {
// Count how often each value of target (class) appears
classifcator = this.data[y][predictorCount];
console.log(classifcator);
// Find the most frequent class
// Make the rule assign that class to this value of the predictor
}
// Calculate the total error of the rules of each predictor
}
// Choose the predictor with the smallest total error
} else {
console.log("Cannot predict!");
}
}
}
module.exports = {
OneR
};
I have loaded data from csv
rainy,hot,high,0,0
rainy,hot,high,1,0
overcast,hot,high,0,1
sunny,mild,high,0,1
sunny,cool,normal,0,1
sunny,cool,normal,1,0
overcast,cool,normal,1,1
rainy,mild,high,0,0
rainy,cool,normal,0,1
sunny,mild,normal,0,1
rainy,mild,normal,1,1
overcast,mild,high,1,1
overcast,hot,normal,0,1
sunny,mild,high,1,0
If I understand correctly how the frequency tables must be compared (lowest error rate, highest accuracy), you could use Maps so to cope with non-string types if ever necessary.
Although your example has target values that are booleans (0 or 1), in general they could be from a larger domain, like for example "call", "fold", "raise", "check".
Your template code creates a class, but I honestly do not see the benefit of that, since you can practically only do one action on it. Of course, if you have other actions in mind, other than one-rule prediction, then a class could make sense. Here I will just provide a function that takes the data, and returns the number of the selected predictor and the rule table that goes with it:
function oneR(data) {
if (!data && !data.length) return console.log("Cannot predict!");
const predictorCount = data[0].length - 1;
// get unique list of classes (target values):
let classes = [...new Set(data.map(row => row[predictorCount]))];
let bestAccuracy = -1;
let bestFreq, bestPredictor;
// For each predictor,
for (let i = 0; i < predictorCount; i++) {
// create frequency table for this predictor: Map of Map of counts
let freq = new Map(data.map(row => [row[i], new Map(classes.map(targetValue => [targetValue, 0]))]));
// For each value of that predictor, collect the frequencies
for (let row of data) {
// Count how often each value of target (class) appears
let targetValue = row[predictorCount];
let predictorValueFreq = freq.get(row[i]);
let count = predictorValueFreq.get(targetValue);
predictorValueFreq.set(targetValue, count+1);
}
// Find the most frequent class for each predictor value
let accuracy = 0;
for (let [predictorValue, predictorValueFreq] of freq) {
let maxCount = 0;
let chosenTargetValue;
for (let [targetValue, count] of predictorValueFreq) {
if (count > maxCount) {
// Make the rule assign that class to this value of the predictor
maxCount = count;
chosenTargetValue = targetValue;
}
}
freq.set(predictorValue, chosenTargetValue);
accuracy += maxCount;
}
// If this accuracy is best, then retain this frequency table
if (accuracy > bestAccuracy) {
bestAccuracy = accuracy;
bestPredictor = i;
bestFreq = freq;
}
}
// Return the best frequency table and the predictor for which it applies
return {
predictor: bestPredictor, // zero-based column number
rule: [...bestFreq.entries()]
}
}
let data = [
["rainy","hot","high",0,0],
["rainy","hot","high",1,0],
["overcast","hot","high",0,1],
["sunny","mild","high",0,1],
["sunny","cool","normal",0,1],
["sunny","cool","normal",1,0],
["overcast","cool","normal",1,1],
["rainy","mild","high",0,0],
["rainy","cool","normal",0,1],
["sunny","mild","normal",0,1],
["rainy","mild","normal",1,1],
["overcast","mild","high",1,1],
["overcast","hot","normal",0,1],
["sunny","mild","high",1,0]
];
let result = oneR(data);
console.log(result);
I'm trying to learn javascript and how to use the developer api provided by discord.
I do believe everything I want so far is working except I wanted to create a system like how a database works. Where each player in the table has a unique id key. I'm not sure if this is possible without using a db.
[Index.js]
/* Discord API Information */
const Discord = require('discord.js');
const token = '';
const Game = require('./game.js');
const client = new Discord.Client();
let playersName = []; // tracks each player that joins
let currPlayers = 0; //tracker for total players online
let INIT_GAME;
let userJoined = false;
let inBattle = false;
client.on('message', (msg) => {
if(msg.content === '!join'){
// Prevents multiple instances of the same person from joining
for(var x = 0; x < playersName.length; x++){
if(playersName[x]===msg.author.username){
return playersName[x];
}
}
currPlayers++;
userJoined = true;
playersName.push(msg.author.username);
//My attempt at having the question im asking
function convertToID(arr, width) {
return arr.reduce(function (rows, key, index) {
return (index % width == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
}
console.log(convertToID(playersName,1)); /* Tracks players by ID in developer tools */
INIT_GAME = new Game(playersName, client, 'bot-testing', currPlayers);
let myRet = INIT_GAME.startGame();
const embed = new Discord.RichEmbed()
.setTitle("Welcome To Era Online")
.setColor(0xFF0000)
.addField(`${msg.author.username} has Joined`, myRet);
msg.channel.send(embed);
msg.channel.send(`${msg.author} has joined the game.`);
return;
}
if(userJoined == true){
if(msg.content === '!fight' && (!inBattle)){
let grabCurrPlayer = msg.author.username;
msg.channel.send(`${INIT_GAME.initBattle(grabCurrPlayer)}`);
}
else if(msg.content === '!leave'){
let tempLeave = msg.author.username;
for(var y = 0; y < playersName.length; y++){
if(playersName[y] == msg.author.username){
playersName[y] = [`${playersName[y]} was previously ID: ` + [y]];
currPlayers--;
}
}
msg.channel.send([`${tempLeave} has left the server.`]);
userJoined = false;
}
else if(msg.content === '!newgame'){
msg.channel.send(INIT_GAME.newGame());
}
/* Simply checks the bonus damage. command for developer*/
else if(msg.content === '!bonus'){
msg.channel.send(INIT_GAME.bonusAttack());
}
}
/* checks whose currently online. command for developer*/
if(msg.content === '!online'){
msg.channel.send(INIT_GAME.getOnline());
}
});
client.on('ready', () => {
console.log('Bot is now connected');
});
client.login(token);
[game.js]
class Game {
constructor(player, client, channelName='bot-testing', playersOnline){
this.client = client;
this.channelName = channelName;
this.currentPlayer = player;
this.playersOnline = [];
this.hitpoints = 120;
this.damage = '';
this.chance = 3;
this.inBattle = false;
this.online = playersOnline;
this.monster = [{
hp: Math.floor(Math.random() * 200),
temphp: 0,
damage: 10
}];
};
/* main menu information, players online */
startGame(){
for(var x = 0; x < this.currentPlayer.length; x++){
this.playersOnline.push(this.currentPlayer[x]);
if(this.playersOnline[x] === this.currentPlayer[x]){
return [`Players Online: ${this.online}\n`];
}
}
}
/* Battle system */
initBattle(currPlayer){
this.inBattle = true;
let npcHP = this.monster[0].hp;
let numberOfAttacks = 0;
let totalDamage=0, totalBonusDamage=0;
while( this.monster[0].hp > 0 ){
let playerDamage = Math.floor(Math.random() * (npcHP / 4));
if(this.bonusAttack() === 2){
console.log(`Bonus Attack: ${this.bonusAttack()}`);
console.log(`Regular damage without bonus attack: ${playerDamage}`);
playerDamage = playerDamage + 2;
}
this.monster[0].hp -= playerDamage;
this.hitpoints -= this.monster[0].damage;
console.log('Monster: ' + this.monster[0].hp);
console.log('Player: ' + this.hitpoints);
console.log(`${currPlayer} has attacked for ${playerDamage}`);
console.log(`NPC health: ${this.monster[0].hp}`);
if(this.hitpoints <= 0){
return [`You lost the battle.`];
}
this.inBattle = false;
numberOfAttacks++;
totalDamage += playerDamage;
totalBonusDamage = playerDamage + this.bonusAttack();
}
if(this.monster[0].hp <= 0 && this.inBattle !== true){
let maxDamage = totalDamage + totalBonusDamage;
return [`${currPlayer} has attacked ${numberOfAttacks} times dealing ${totalDamage} + (${totalBonusDamage}) bonus damage for a total of ${maxDamage} damage. The monster is dead.\n
Your Health: ${this.hitpoints}`];
}
else{
this.newGame();
return [`You rejuvenated your hitpoints and are ready for battle. \nType !fight again to start a new battle!`];
}
}
/* bonus attack damage [ 1 in 3 chance ] */
bonusAttack(bonusDamage){
let chance = Math.floor(Math.random() * 3);
return chance === 2 ? bonusDamage = 2 : false;
}
/* displays players currently online */
getOnline(){
console.log(this.currentPlayer);
return this.currentPlayer;
}
/* refresh stats */
newGame(){
this.monster[0].hp = Math.floor(Math.random() * 50);
this.hitpoints = 150;
}
}
module.exports = Game;
[My Question]
The only real important part within those 2 files is in index.js at the line that speaks about when a player leaves. So !leave.
I had a problem where one player typed !leave, both people would leave. That is the solution I used to fix it.
I could not get it to empty the array for ONLY the person who typed the command.
Example:
Person A types !join
Players online = [PlayerA]
Person B types !join
Players online = [PlayerA, PlayerB]
Player A types !leave
Players online = [[], PlayerB]]
It would always insert a empty array in the spot. So what I did was just fill that spot with the users previous name and their array id.
What I want is so that it completely deletes the person from the array AND removes that empty spot.
I would also like to know if it's possible that every time someone types !join, I would be able to insert them into an new array that is multidimensional and has the id for each player so that when I type !online, it would display
[[0, PlayerA], [1, PlayerB]] . Like a database where I can always see their index if needed.
What I have so far:
https://i.imgur.com/lWrtEtB.png
It only tracks the last index after they leave. How do I make it display the current index of the players online?
Use findIndex() to find the position of the name in your array. Then use the splice() method to remove a value from your array. You don't need to use that for loop, as findIndex will run a similar loop.
var playerIndex = playersName.findIndex(function(index) {
return index === tempLeave
})
playersName.splice(playerIndex, 1)
After reading the second part of your question though, I think you should be creating objects inside of your array. An example would be:
[
{
playerName: "Foo",
id: indexNumber,
isOnline: true
},
{
playerName: "Bar",
id: indexNumber,
isOnline: true
}
]
When someone joins, you check if their name is already assigned to an object (you can use findIndex again here). If not you create a new player, else you will change the players isOnline attribute to true. I'm sure this is the best way to go about storing user info, but it might work for you.