I began working on my discord bot few days ago, and while creating my rps (rock, paper, scissors) command I encountered a minor problem, when I run the command twice without finishing the game my bot crushes. Here's my code:
module.exports = {
name: 'rps',
cooldown: 0,
aliases: [],
permissions: [],
description: 'Rock Paper Scissors.',
async execute(message, args, client, Discord) {
let hand = [{ txt: 'Rock', emoji: 'โ', index: 0 }, { txt: 'Paper', emoji: '๐ค', index: 1 }, { txt: 'Scissors', emoji: 'โ๏ธ', index: 2 }]; // Defining Moves
let botMove = hand[Math.floor(Math.random() * 3)]; // Making a random move
let rpsMsg = await message.channel.send({ // Prompting user to make a move
embeds: [
new Discord.MessageEmbed() // RPS embed
.setColor('RANDOM')
.setTitle('Rock Paper Scissors')
.setDescription('Choose a handsign')
],
components: [
new Discord.MessageActionRow() // Rock, paper, scissors buttons
.addComponents(
new Discord.MessageButton()
.setCustomId(`rps_rock`)
.setLabel("โ Rock")
.setStyle('PRIMARY'),
new Discord.MessageButton()
.setCustomId(`rps_paper`)
.setLabel("๐ค Paper")
.setStyle('PRIMARY'),
new Discord.MessageButton()
.setCustomId(`rps_scissors`)
.setLabel("โ๏ธ Scissors")
.setStyle('PRIMARY')
)
]
});
let win = 0; // 0 = Loss; 1 = Tie; 2 = Win
let userMove;
// If you already / want to handle this in your interactionCreate.js, ignore this part.
let f = async (interaction) => {
if (!interaction.isButton()) return; // Checking if the interaction is a button
if (interaction.customId.startsWith('rps')) {
await interaction.deferUpdate() // Deffering the interaction
let move = interaction.customId.split('_')[1]
userMove = hand.find(v => v.txt.toLowerCase() == move)
switch (move) { // Calculating if player wins, losses, or a tie
case 'rock':
win = botMove.index == 0 ? 1 : (botMove.index == 1 ? 0 : 2); break;
case 'paper':
win = botMove.index == 0 ? 2 : (botMove.index == 1 ? 1 : 0); break;
case 'scissors':
win = botMove.index == 0 ? 0 : (botMove.index == 1 ? 2 : 1); break;
}
let embed = rpsMsg.embeds[0];
// Editing the embed
embed.description = `I chose ${botMove.txt}! ${win == 0 ? 'You lost!' : (win == 1 ? 'We tied!' : 'You win!')} (${userMove.emoji} ${win == 0 ? '<' : (win == 1 ? '=' : '>')} ${botMove.emoji})`;
let components = rpsMsg.components
// Disabling all buttons
components[0].components.forEach(comp => {
if (comp.customId == interaction.customId) { comp.disabled = true; comp.style = 'SECONDARY' }
else comp.disabled = true
})
// Editing the message
interaction.message.edit({ embeds: [embed], components: components })
// Removing this event.
client.off('interactionCreate', f)
}
}
client.on('interactionCreate', f) // Creates the event
}
}
Does anyone know how to check for an existing interaction or to delete it so my bot wont crush?
BTW, that's the terminal's output:
throw new DiscordAPIError(data, res.status, request);
^
DiscordAPIError: Interaction has already been acknowledged.
at RequestHandler.execute (C:\Users\bar_m\Desktop\DiscordBot\node_modules\discord.js\src\rest\RequestHandler.js:350:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async RequestHandler.push (C:\Users\bar_m\Desktop\DiscordBot\node_modules\discord.js\src\rest\RequestHandler.js:51:14)
at async ButtonInteraction.deferUpdate (C:\Users\bar_m\Desktop\DiscordBot\node_modules\discord.js\src\structures\interfaces\InteractionResponses.js:185:5)
at async Client.f (C:\Users\bar_m\Desktop\DiscordBot\commands\rps.js:45:17) {
method: 'post',
path: '/interactions/996846575148081182/aW50ZXJhY3Rpb246OTk2ODQ2NTc1MTQ4MDgxMTgyOm1lOUhSNzhYY2JVcm8yYzk3S056UmJHdG5SNTZOQ0ZDQVlTVXMyZnNralVlc2l0bGNPYm8zUFNJaWFEY0hHTXpTT043TUZkUHc2ckNJRDJ5ZnVEY2o0RFZvVTQ4QUw1UURQODYybndFSzBRTE5pb0ROQ0VyNHdmdmt3MXdSU3U5/callback',
code: 40060,
httpStatus: 400,
requestData: { json: { type: 6 }, files: [] }
} ```
client.on('interactionCreate', f) is not what you need here. This results in any interaction the client receives being routed only to your function f, which can cause problems receiving interactions with your client.
Probably the best thing to use here is message.createMessageComponentCollector(...), check this (discord.js docs) for more information.
You can add the following code instead of client.on('interactionCreate', f):
const filter = (interaction) => interaction.customId.startsWith("rps"); // Only handle interactions whose id start with "rps"
const collector = rpsMsg.createMessageComponentCollector({ filter }); // Create a messageComponentCollector on the rps message
collector.on("collect", (interaction) => f(interaction)); // Execute the `f` function when a button is clicked
Using a component collector, you won't need to run client.off('interactionCreate', f), so you can remove that from your code as well.
I'm storing the xp in a json file for practice. How can I display the top 10 users? The first time I
would have wanted to sort but I don't know how it would be more efficient.
let xpAdd = Math.floor(Math.random() * 9) + 8;
if(!xp[message.author.id]) {
xp[message.author.id] = {
xp:0,
level:1
};
}
let curxp = xp[message.author.id].xp;
let curlvl = xp[message.author.id].level;
let nxtLevel = xp[message.author.id].level * 300;
xp[message.author.id].xp = curxp + xpAdd;
fs.writeFile("./xp.json", JSON.stringify(xp), (error) => {
if(error) console.log(error);
});
This is the code I store
And with this I show the level of xp
if(!xp[message.author.id]) {
xp[message.author.id] = {
xp: 0,
level:1
};
}
let curxp = xp[message.author.id].xp;
let curlvl = xp[message.author.id].level;
let nxtLevelXp = curlvl * 300;
let difference = nxtLevelXp - curxp;
I'd suggest you should convert your object to an array so that you can format it and sort it however you prefer.
// sample object
// I'm going to show the top three in this example for the interest of space
const xp = {
"ID #1": {
level: 3,
xp: 300,
},
"ID #2": {
level: 4,
xp: 400,
},
"ID #3": {
level: 2,
xp: 200,
},
"ID #4": {
level: 1,
xp: 100,
},
};
// sort entries in order of exp (descending), then single out the top three
let entries = Object.entries(xp).sort(([, a], [, b]) => b.xp > a.xp ? 1 : -1).slice(0, 3);
// map entries to the prefered format using the data in their objects
// (in your actual command, you should use `client.users.cache.get(id).tag`,
// but since I don't have access to the client object here, I'm just writing `id`
entries = entries.map(([id, { level, xp }], idx) => `${idx + 1} - ${id} (level ${level}; ${xp} xp)`);
// join each user by a line break
console.log(entries.join('\n'));
Working With Objects
Object.entries()
Array.prototype.sort()
Array.prototype.map()
Array.prototype.join()
I'm scaling a displacement map on click but would like that map to fade away once it reaches almost full scale. The idea is that the filter should be non-existent after a couple of seconds.
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage("https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80");
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.05)
displacement.alpha = 1
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
app.ticker.add(function() {
displacement.scale.x += 0.05
displacement.scale.y += 0.05
if (displacement.scale.x > 10) app.ticker.stop()
});
logo.on('mousedown', function() {
displacement.scale.set(0.05)
app.ticker.start()
});
Here's what I have so far:
https://codepen.io/mariojankovic/pen/pojjNae?editors=0111
I've only just started looking at Pixi but I think you want to use the scale property of the displacement filter. This value says how far to shift. Reducing this value to 0 will lessen its effect to none.
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html#scale
The way it works is it uses the values of the displacement map to look
up the correct pixels to output. This means it's not technically
moving the original. Instead, it's starting at the output and asking
"which pixel from the original goes here". For example, if a
displacement map pixel has red = 1 and the filter scale is 20, this
filter will output the pixel approximately 20 pixels to the right of
the original.
https://codepen.io/PAEz/pen/BaoREwv
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage(
"https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80"
);
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.0);
displacement.alpha = 1;
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
const displacementScaleFrom = 0.05;
const displacementScaleTo = 10 ;
const displacementStep = 0.05;
const filterScaleFrom = 20;// the default value for the filter is 20
const filterStep = filterScaleFrom / ((displacementScaleTo-displacementScaleFrom) / displacementStep);
app.ticker.add(function () {
displacement.scale.x += displacementStep;
displacement.scale.y += displacementStep;
filter.scale.x -= filterStep;
filter.scale.y -= filterStep;
if (displacement.scale.x >= displacementScaleTo) {
app.ticker.stop();
filter.scale.x = 0;
filter.scale.y = 0;
}
});
logo.on("mousedown", function () {
displacement.scale.set(displacementScaleFrom);
filter.scale.x = filterScaleFrom;
filter.scale.y = filterScaleFrom;
app.ticker.start();
});
I am using the loadbalance module. It works fine for randomEngine but does not work for RoundRobinEngine. Can someone help me as to where the issue is Code :-
const loadbalance = require('loadbalance');
const engine = loadbalance.roundRobin([
{ object: 'a', weight: 2 },
{ object: 'b', weight: 1 }
]);
const pick = engine.pick();
console.log(pick);
In the above case i am getting output as 'a' everytime. It does not throw output 'b'. Can someone help me out in this
You have set the weight param to 2 for the a entry. So the roundRobin will pick a twice as often as b. If you will put more .pick() calls, you will see how it works:
const loadbalance = require('loadbalance');
const engine = loadbalance.roundRobin([
{ object: 'a', weight: 2 },
{ object: 'b', weight: 1 }
]);
let pick = engine.pick();
for (let i = 0; i < 6; i++) {
console.log(pick);
pick = engine.pick();
}
and the output will be:
a
a
b
a
a
b
Se live demo: https://repl.it/repls/UntimelyDelayedScope
pick pick is a constance, when you print out pick value it is the same in everywhere.
Just call engine.pick() when you want to choose next object with your rule.
In example, I all .pick with 6 times:
const loadbalance = require('loadbalance');
const engine = loadbalance.roundRobin([
{ object: 'a', weight: 2 },
{ object: 'b', weight: 1 }
]);
//const pick = engine.pick();
for (let i = 0; i < 6; i++) {
console.log(engine.pick());
}
// 'a', 'a', 'b', 'a', 'a', 'b'
Is there a way to convert the os.cpus() info to percentage? Just like the output of iostat (on the CPU section).
My code:
var os = require('os');
console.log(os.cpus());
The output:
[ { model: 'MacBookAir4,2',
speed: 1800,
times:
{ user: 5264280,
nice: 0,
sys: 4001110,
idle: 58703910,
irq: 0 } },
{ model: 'MacBookAir4,2',
speed: 1800,
times:
{ user: 2215030,
nice: 0,
sys: 1072600,
idle: 64657440,
irq: 0 } },
{ model: 'MacBookAir4,2',
speed: 1800,
times:
{ user: 5973360,
nice: 0,
sys: 3197990,
idle: 58773760,
irq: 0 } },
{ model: 'MacBookAir4,2',
speed: 1800,
times:
{ user: 2187650,
nice: 0,
sys: 1042550,
idle: 64714820,
irq: 0 } } ]
I would like to have the "times" metric converted to percentage, just like is show on the iostat command:
cpu
us sy id
6 3 91
I understand that the values in the nodejs function are in CPU ticks, but I have no idea what formula should I use to convert them to percentage :)
Thanks.
According to the docs, times is
an object containing the number of CPU ticks spent in: user, nice, sys, idle, and irq
So you should just be able to sum the times and calculate the percentage, like below:
var cpus = os.cpus();
for(var i = 0, len = cpus.length; i < len; i++) {
console.log("CPU %s:", i);
var cpu = cpus[i], total = 0;
for(var type in cpu.times) {
total += cpu.times[type];
}
for(type in cpu.times) {
console.log("\t", type, Math.round(100 * cpu.times[type] / total));
}
}
EDIT: As Tom Frost says in the comments, this is the average usage since system boot. This is consistent with the question, since the same is true of iostat. However, iostat has the option of doing regular updates, showing the average usage since the last update. Tom's method would work well for implementing that.
This module, that caN be installed using NPM provides what you need:
https://github.com/oscmejia/os-utils
calle the cpuUsage(callback) method and you will get what you need.
This is my Solution
Interval is in Seconds.
10 will calculate load over the last 10 seconds!
var _ = require("underscore");
var os = require("os");
var interval = 1;
var old = _.map(os.cpus(),function(cpu){ return cpu.times;})
setInterval(function() {
var result = [];
var current = _.map(os.cpus(),function(cpu){ return cpu.times; })
_.each(current, function(item,cpuKey){
result[cpuKey]={}
var oldVal = old[cpuKey];
_.each(_.keys(item),function(timeKey){
var diff = ( parseFloat((item[timeKey]) - parseFloat(oldVal[timeKey])) / parseFloat((interval*100)));
var name = timeKey;
if(timeKey == "idle"){
name = "CPU"
diff = 100 - diff;
}
//console.log(timeKey + ":\t" + oldVal[timeKey] + "\t\t" + item[timeKey] + "\t\t" + diff);
result[cpuKey][name]=diff.toFixed(0);
});
});
console.log(result);
old=current;
}, (interval * 1000));
Outputs something like this on my 8-core every n-seconds
[ { user: '82', nice: '0', sys: '18', CPU: '100', irq: '0' },
{ user: '1', nice: '0', sys: '1', CPU: '3', irq: '0' },
{ user: '1', nice: '0', sys: '1', CPU: '3', irq: '0' },
{ user: '9', nice: '0', sys: '2', CPU: '11', irq: '0' },
{ user: '1', nice: '0', sys: '0', CPU: '1', irq: '0' },
{ user: '1', nice: '0', sys: '1', CPU: '2', irq: '0' },
{ user: '1', nice: '0', sys: '2', CPU: '2', irq: '0' },
{ user: '1', nice: '0', sys: '2', CPU: '3', irq: '0' } ]
Pushing this via socket.io into my Flow-Charts ;)
If you are looking at the CPU Usage per process try node-usage
a simple hack:
var os = require('os')
var samples = []
var prevCpus = os.cpus()
setInterval(sample,100)
setInterval(print,1000)
function print() {
var result = {last10:null, last50:null, last100:null}
var percent = 0
var i = samples.length
var j = 0
while (i--) {
j++
if (samples[i].total > 0)
percent += (100 - Math.round(100 * samples[i].idle / samples[i].total))
if (j == 10) result.last10 = percent/j
else if (j == 50) result.last50 = percent/j
else if (j == 100) result.last100 = percent/j
}
console.log(result)
}
function sample() {
currCpus = os.cpus()
for (var i=0,len=currCpus.length;i<len;i++) {
var prevCpu = prevCpus[i]
var currCpu = currCpus[i]
var deltas = {total:0}
for (var t in prevCpu.times)
deltas.total += currCpu.times[t] - prevCpu.times[t]
for (var t in prevCpu.times)
deltas[t] = currCpu.times[t] - prevCpu.times[t]
}
prevCpus = currCpus
samples.push(deltas)
if (samples.length>100) samples.shift()
}
you could use a metrics-lib like https://github.com/felixge/node-measured to plumb something more prolific
Here how I did it:
var OS = require('os');
var oldCPUTime = 0
var oldCPUIdle = 0
function getLoad(){
var cpus = OS.cpus()
var totalTime = -oldCPUTime
var totalIdle = -oldCPUIdle
for(var i = 0; i < cpus.length; i++) {
var cpu = cpus[i]
for(var type in cpu.times) {
totalTime += cpu.times[type];
if(type == "idle"){
totalIdle += cpu.times[type];
}
}
}
var CPUload = 100 - Math.round(totalIdle/totalTime*100))
oldCPUTime = totalTime
oldCPUIdle = totalIdle
return {
CPU:CPUload,
mem:100 - Math.round(OS.freemem()/OS.totalmem()*100)
}
}
i'm using this code:
var cpu_used = function(){
var cpu = os.cpus();
var counter = 0;
var total=0;
var free=0;
var sys=0;
var user=0;
for (var i = 0; i<cpu.length ; i++) {
counter++;
total=parseFloat(cpu[i].times.idle)+parseFloat(cpu[i].times.sys)+parseFloat(cpu[i].times.user)+parseFloat(cpu[i].times.irq)+parseFloat(cpu[i].times.nice);
free+=100*(parseFloat(cpu[i].times.idle)/total);
sys+=100*(parseFloat(cpu[i].times.sys)/total);
user+=100*(parseFloat(cpu[i].times.user)/total);
};
console.log('CPU %s : %s + %s + %s',i,(free/counter),(user/counter),(sys/counter));
}
If you want to watch real time CPU and memory usage, you can try os-usage.
The basic usage is like following:
var usage = require('os-usage');
// create an instance of CpuMonitor
var cpuMonitor = new usage.CpuMonitor();
// watch cpu usage overview
cpuMonitor.on('cpuUsage', function(data) {
console.log(data);
// { user: '9.33', sys: '56.0', idle: '34.66' }
});
You can also get processes that use most cpu resources:
cpuMonitor.on('topCpuProcs', function(data) {
console.log(data);
// [ { pid: '21749', cpu: '0.0', command: 'top' },
// { pid: '21748', cpu: '0.0', command: 'node' },
// { pid: '21747', cpu: '0.0', command: 'node' },
// { pid: '21710', cpu: '0.0', command: 'com.apple.iCloud' },
// { pid: '21670', cpu: '0.0', command: 'LookupViewServic' } ]
});
This variant covers the cpu-usage for a particular core or for all cores calculated between two different CPU usage samples taken within a configurable 1000 ms.
One thing we should consider when implementing such function is reducing the code overhead between CPU samples code block. Otherwise the code execution will affect the second CPU sample and thus the "averaged" result.
Caveat emptor: the following sample implementation does not fully answer the original question (see SY, USER, IDLE), however it can be easily refactored to return the three metrics instead of the summarized one.
var os = require("os");
/**
* #description Measure the CPU usage calculated between two different CPU usage samples
* #param {function} cb A callback function the result is passed to
* #param {number} [core=-1] The CPU core index to measure. When is not greater than -1 then it is "averaged" for all CPU cores.
* #param {number} [sampleMs=1000] The number of milliseconds between the CPU usage samples
*/
function cpuUsage(cb, core, sampleMs) {
var deltaUsed;
var deltaIdle;
var timesKeys = ["user", "nice", "sys", "irq"];
var allCores = null === core || !(core > -1);
var byCore = (cpu, i) => allCores || core === i;
var bySampleKey = (sample, key) => sample.filter(byCore).reduce((sum, cpu) => sum + cpu.times[key], 0);
var sample0 = os.cpus();
setTimeout(function() {
var sample1 = os.cpus();
deltaUsed = timesKeys.reduce(
(diff, key) => diff + bySampleKey(sample1, key) - bySampleKey(sample0, key), 0);
deltaIdle = bySampleKey(sample1, "idle") - bySampleKey(sample0, "idle");
if ("function" === typeof cb) {
cb(100 * (deltaUsed / (deltaUsed + deltaIdle)));
}
}, sampleMs || 1000);
}
Usage example:
cpuUsage(perc => console.log(perc));