Hewo everyone. I created my own bot
I have a lot of great things like games etc.
However, I want to do a couting game.
I have a channel called "Counting"
And i want to setup my bot do like:
User 1: 456
User 2: 457
Bot: 458
My question is, How can I make the bot count when no one else is counting? But just once. (Look the example^^)
If you can, can you give me an example code please? Thanks!!
Try this:
const {Client} = require('discord.js')
const client = new Client()
// Stores the current count.
let count = 0
// Stores the timeout used to make the bot count if nobody else counts for a set period of
// time.
let timeout
// Discord.js v12:
// client.on('message', ({channel, content, member}) => {
// Discord.js v13:
client.on('messageCreate', ({channel, content, member}) => {
// Only do this for the counting channel of course
// If you want to simply make this work for all channels called 'counting', you
// could use this line:
// if (client.channels.cache.filter(c => c.name === 'counting').has(channel.id))
if (channel.id === 'counting channel id') {
// You can ignore all bot messages like this
if (member.user.bot) return
// If the message is the current count + 1...
if (Number(content) === count + 1) {
// ...increase the count
count++
// Remove any existing timeout to count
if (timeout) clearTimeout(timeout)
// Add a new timeout
timeout = setTimeout(
// This will make the bot count and log all errors
() => channel.send(++count).catch(console.error),
// after 30 seconds
30000
)
// If the message wasn't sent by the bot...
} else if (member.id !== client.user.id) {
// ...send a message because the person stuffed up the counting (and log all errors)
channel.send(`${member} messed up!`).catch(console.error)
// Reset the count
count = 0
// Reset any existing timeout because the bot has counted so it doesn't need to
// count again
if (timeout) clearTimeout(timeout)
}
}
})
client.login('your token')
Explanation
When a user (that isn't the bot) sends a message in the counting channel, the bot checks if the user is counting correctly (if (Number(content) === count + 1)).
If they are, it increments count, removes the timeout if it exists (if (timeout) clearTimeout(timeout)), and schedules the bot to count after 30 seconds (setTimeout(() => channel.send(++count), 30000)).
If they aren't, the bot sends a message, resets count, and clears the timeout (if it exists).
When the bot sends a message, it won't trigger any of that. When the bot counts, Number(content) === count because it was already incremented.
Related
im trying to make a purge command for my discord bot in node.js but everytime it removes one less than it should do(cus the bot thinks the message i send is also one of the messages i want 2 delete) but when i try to do some simple math to just make the number one higher i get something like this:
10 + 1 = 101 at least that is what the code does this is the code for the purge command i currently use:
const args = msg.content
.slice(prefix.length)
.trim()
.split(" ");
const Args2 = args[2];
const DeleteAmount = Args2;
if (!args[2]) return msg.channel.send("Please Specify a amount");
if (args[2] > 100) return msg.channel.send("You cant delete a amount higher than 100")
if (args[2] < 1) return msg.channel.send("You cant delete less than one message")
msg.channel.bulkDelete(DeleteAmount);
msg.channel.send(`${DeleteAmount} messages have been deleted`);
} else if(msg.content.startsWith(`${prefix}PurgeTest`)) {
msg.channel.send("You Do not have Permission to delete messages");
msg.channel.send("Only people with the role CustomBotAdmin can delete messages");
}
sorry for the messy text...
edit: ah i got it fixed
This question already has answers here:
Discord.js - Cooldown for a command for each user not all users
(3 answers)
Closed 2 years ago.
So i want to upload my discord bot online but i want to prevent users from spamming it's commands.
So let the delay be 5 seconds, if user runs !help the bot answers, but if user run !help before the delay is expired bot says: wait some time before using this command again.
Also i want the delay to work only for message author and not affect other users.
I'm using command handler so is it possible to make something like module.exports.delay?
to do that you can use timestamps like:
let msgTimestamp = [];
if(message.content === "?" + "help") {
if(typeof msgTimestamp[0] !== "undefined") {
if(msgTimestamp[0] + 5000 < Date.now()) {
message.channel.send("Here embed of command help.");
msgTimestamp = [];
} else {
message.channel.send("wait some time before using this command again.");
}
} else {
msgTimestamp.push(Date.now());
message.channel.send("Here embed of command help.");
}
}
In this coode you use timestamps, you check if the content of the message is eualt to the command you want to use, if the user had already sent a message, the variable which containing the message timestamp is full, you check then if the the typeof of the variable is not undefined and then you check id the timestamp of when the user sent the message is lower than the actual timestamp + how many delay you want!
Code for publishing the messages here:
async function publishMessage(topicName) {
console.log(`[${new Date().toISOString()}] publishing messages`);
const pubsub = new PubSub({ projectId: PUBSUB_PROJECT_ID });
const topic = pubsub.topic(topicName, {
batching: {
maxMessages: 10,
maxMilliseconds: 10 * 1000,
},
});
const n = 5;
const dataBufs: Buffer[] = [];
for (let i = 0; i < n; i++) {
const data = `message payload ${i}`;
const dataBuffer = Buffer.from(data);
dataBufs.push(dataBuffer);
}
const results = await Promise.all(
dataBufs.map((dataBuf, idx) =>
topic.publish(dataBuf).then((messageId) => {
console.log(`[${new Date().toISOString()}] Message ${messageId} published. index: ${idx}`);
return messageId;
})
)
);
console.log('results:', results.toString());
}
As you can see, I am going to publish 5 messages. The time to publish is await Promise.all(...), I mean, for users, We can say send messages at this moment, but for internal of pubsub library maybe not. I set maxMessages to 10, so pubsub will wait for 10 seconds(maxMilliseconds), then publish these 5 messages.
The exuection result meets my expectations:
[2020-05-05T09:53:32.078Z] publishing messages
[2020-05-05T09:53:42.209Z] Message 36854 published. index: 0
[2020-05-05T09:53:42.209Z] Message 36855 published. index: 1
[2020-05-05T09:53:42.209Z] Message 36856 published. index: 2
[2020-05-05T09:53:42.209Z] Message 36857 published. index: 3
[2020-05-05T09:53:42.209Z] Message 36858 published. index: 4
results: 36854,36855,36856,36857,36858
In fact, I think topic.publish does not directly call the remote pubsub service, but pushes the message into the memory queue. And there is a window time to calculate the count of the messages, maybe in a tick or something like:
// internal logic of #google/pubsub library
setTimeout(() => {
// if user messages to be published gte maxMessages, then, publish them immediately
if(getLength(messageQueue) >= maxMessages) {
callRemotePubsubService(messageQueue)
}
}, /* window time = */ 100);
Or using setImmediate(), process.nextTick()?
Note that the conditions for sending a message to the service is an OR not an AND. In other words, if either maxMessages messages are waiting to be sent OR maxMilliseconds has passed since the library received the first outstanding message, it will send the outstanding messages to the server.
The source code for the client library is available, so you can see exactly what it does. The library has a queue that it uses to track messages that haven't been sent yet. When a message is added, if the queue is now full (based on the batching settings), then it immediately calls publish. When the first message is added, it uses setTimeout to schedule a call that ultimately calls publish on the service. The publisher client has an instance of the queue to which it adds messages when publish is called.
This issue on GitHub pretty much sums it up. I'am using a timer() with a recurring schedule of 1 second to perform a certain task. I pair it up with a Subscriber to subscribe the intervals. When a certain model runs out of data, I unsubscribe it & wait for new arrivals. When they data is populated again, I try to subscribe again but it doesn't work. It turns out whena Subscriber has been unsub'd, I can't use it again. So I must replace it with an Observer. A newbie here, I've no idea how to do that. tried looking at examples, they just confused me further.
How do I replace the following code to function with an Observer instead?
private timer = timer(1000, 1000);
// A timer subscription that keeps sending new images to the observer
timerSubscription = new Subscriber(() => {
// Check if there is an element in the list
if (this.head != null) {
// If the current node at head is a folder, unsubscribe the listener
if (this.head.data['id'].startsWith('folder')) {
this.timerSubscription.unsubscribe();
}
// Pop a node from the list and pass on to observer
this.observer.next(this.this$PiFrame.pop());
} else {
// If no nodes are left, unsubscribe from the timer
this.timerSubscription.unsubscribe();
console.log('No items left on the queue. Deactivating timer subscription.');
}
}, e => {}, () => {});
and I subscribe like so :
...
// Setup a timer to pop every 1000 ms
this.timer.subscribe(this.this$PiFrame.timerSubscription);
...
// If no nodes are left, unsubscribe from the timer
this.timerSubscription.unsubscribe();
...
Instead of creating the subscription the way you do, let the Observable return the subscription.
Keep your logic in a function, like so:
doWhatever() {
console.log("tick")
// Check if there is an element in the list
if (this.head != null) {
// If the current node at head is a folder, unsubscribe the listener
if (this.head.data['id'].startsWith('folder')) {
this.timerSubscription.unsubscribe();
}
// Pop a node from the list and pass on to observer
this.observer.next(this.this$PiFrame.pop());
} else {
// If no nodes are left, unsubscribe from the timer
this.timerSubscription.unsubscribe();
console.log('No items left on the queue. Deactivating timer subscription.');
}
}
Then, when you want to subscribe:
this.timerSubscription = this.timer.subscribe(() => this.doWhatever());
This can be used repeatedly, as each subscribe generates a new Subscription
So I'm developing simple game with the following scenario:
2 users need to start a game (so I'm creating new room for 3rd user and so on)
2 users is maximum per room
When game is started, event is sent to client, after 60 seconds server needs to end the game.
Code which I wrote will work only for 1 room, but for multiple rooms, cancelling is not correct because my lastRoom variable is incorrect.
I'm new to node.js so I'm not really sure how to deal with this.
Some code:
var lastRoom = 'default'; //this is the first created room
function tryToStartGame(socket){
var clients = io.sockets.adapter.rooms[lastRoom];
console.log("Size of room "+lastRoom+" is: "+getObjectSize(clients));
//we are checking size of last room
if (getObjectSize(clients) == 2){
//players in the room should be different
games['default']= {
'leftPlayer': getFromObject(clients, 0),
'rightPlayer': getFromObject(clients, 1),
'stuff': "some data here"
'roomName': lastRoom
};
console.log("We can start the game");
//let all the people in that room
io.to(lastRoom).emit('game', games['default']);
//game to cancel
gameToCancel = lastRoom;
//client needs to be aware when game is ended
//but if we have simultaneous games this will not work
setTimeout(function(){
console.log("Cancelling game in: "+gameToCancel);
io.to(gameToCancel).emit('gameEnded', "Winner logic is todo ;) ");
}, 8000); //after 8 seconds for test
//reset the room name, so next time when this function is called in second room
//we will have something different
lastRoom = 'game'+new Date().getTime();
}
//we have less then 2 players, wait for another player
if (getObjectSize(clients)<2){
console.log("Less then 2 players");
socket.emit('waiting', 'Waiting for another user to join the game');
}
}
And tryToStartGame(socket) function is called always at connection like this:
io.on('connection', function(socket){
//when client says he wants to play
socket.on('joinGame', function(username){
//add user
addUser(socket, username);
//send list of players
io.emit('playersList', getFormatedPlayers());
//figure out in which room this player bellongs
socket.join(lastRoom);
//try to start the game
tryToStartGame(socket);
});
Problematic part is that lastRoom variable is overwritten and then setTimeout picks the wrong room, so what happens that basically last game is canceled, and the previous ones are not.
How should I correctly track and cancel the game in correct rooms ?
As you noted, lastRoom is a global variable and might/will change between the time you set it and the timeout
You can create a closure on the timeout function to keep the room as a locale variable:
var lastRoom = 'default'; //this has to be set in the same function than the call to setTimeout
//client needs to be aware when game is ended
//but if we have simultaneous games this will not work
setTimeout(function(){
var gameToCancel = lastRoom;
console.log("Cancelling game in: "+gameToCancel);
io.to(gameToCancel).emit('gameEnded', "Winner logic is todo ;) ");
}, 8000); //after 8 seconds for test