Why does my Discord.js bot take more than five minutes to respond on a server with less than 500 users? - node.js

I've just developed a bot for adding roles to a user who starts streaming on my server. When I tested the bot on a isolated server with only a few channels and users the bot responded instantly. But now I have set up my bot on my primary Discord server with roughly 25 channels and about ~500 users and now the response time is more than five minutes before I see it add or remove a role.
I am connecting via FIOS gigabit and running on a I9 with DDR4 4266MHz RAM.
When I first start the node server I notice the delay is much less, however still very slow responses of around a minute or two before it adds or removes the roles. If I allow the server to run for ten minutes then the response time is around five minutes before it adds a role. I do not know if it continues to get worse over time. But what could be causing this delay? I see my user status change to streaming (purple dot) in the online users list immediately, but my discord bot is not aware of this for a very long time!
This is my code, I have tried my best to optimize it but I've only been learning Discord.js for about a week and am still very unfamiliar with it:
// require the discord.js module
const Discord = require('discord.js');
// create a new Discord client
const client = new Discord.Client();
let role = '';
client.once('ready', () => {
const myGuild = client.guilds.cache.get('123456789123456789');
role = myGuild.roles.cache.find(role => role.name === 'streaming');
console.log('Ready!');
});
client.on("presenceUpdate", (oldPresence, newPresence) => {
if (!newPresence.activities){ return false; }
newPresence.activities.forEach(activity => {
if (activity.type == "STREAMING") {
//console.log(`${newPresence.user.tag} is streaming at ${activity.url}.`);
newPresence.member.roles.add(role)
} else {
newPresence.member.roles.remove(role)
};
});
});
// login to Discord with your app's token
client.login('123456789123456789.123456789.123456789123456789');
Can someone please explain why my bot is becoming so sluggish? It is almost unusable and kind of discouraging.

The default maxListeners is set to 10 and it may be worth exploring setting that value higher.
Additionally, I am curious if there is a memory leak on your application.
It sounds like it from the sluggishness that is experienced over time.
Try using a profiling tool like Clinic.js to monitor the state of the application over time.

Related

Is there a point where Node.js becomes the bottleneck for a Discord bot?

I recently coded a discord bot which can track the user engagement on a server once the user has opted in. I coded it primarily using the Discord.js framework It accepts events emitted from the Discord API when users of a server are active under certain conditions (message send, message reaction, etc.)
How would I be able to tell when, if ever, the single threaded nature of Node.js becomes the bottleneck for this application? Alternatively, how could I test the speed of my application response time to see if my actual code is the inefficient section? While the event-based nature of the application seems like a perfect fit for Node, I want to understand a bit more about the theory behind it and when it'd be a good idea to use another framework.
As you have said, nodejs scales up really well for this kind of application. So, as long as your code runs on a machine with a decent amount of CPU power it will be a long time until you hit a bottleneck. It will run fine with one or two vCPUs, but if your cloud provider throttles the cpu allocated to the virtual machine you're renting, your bot might not keep up with a heavy workload.
If your event handlers use a database, that is just as likely to become a bottleneck as discord.js. Nodejs itself can handle hundreds of concurrent event processors without breaking a sweat (as long as your vendor doesn't throttle your CPU).
How to know you're not keeping up with events? Try this. Each event is processed by a function looking something like this (your code style will certainly be different, this is just the general idea).
execute(client) {
try {
await someOperation(client)
await someOtherOperation(client)
}
catch (err) {
/* handle the error */
}
}
You can time these event handlers like so
execute(client) {
const startTime = Date.now()
try {
await someOperation(client)
await someOtherOperation(client)
}
catch (err) {
/* handle the error */
}
finally {
const elapsedTime = Date.now() - startTime
if (elapsedTime > 1000){ /*one second, you may want another value */
/* report it somehow */
}
}
This will keep track of event handlers taking more than a second.
If your bot does get busy enough to require refactoring, I suspect your best approach will be to shard your application; direct some clients to nodejs instance and others to another.

Better strategy for bot posting messages to multiple channels with DiscordJs

I am trying to execute bot functions from a node express server and it seems the best option is the DiscordJs package (correct me if I'm wrong for this use-case). For example, a user takes some action on my web application that would grant them a role to access different channels. The DiscordJs docs all seem to focus on actions like this being prompted from the discord server through bot commands or detecting changes with members.
I would like to execute bot functions from my express server by instantiating one client, logging that client in, and executing functions from the server. Something to the effect of
const client = new Discord.Client({intents: [Discord.Intents.FLAGS.GUILDS, "GUILD_MESSAGES", "GUILD_MEMBERS"]});
client.on("<some event on express server>", (event) =>{
//event actions
});
client.login(token)
Is DiscordJs the right tool for this use case? Should I be using something other than Discord.Client to do this? Any suggestions/input would be appreciated.
DiscordJs is a great package, I have a fair amount of experience using it.
For your use case I think it would work great.
client.on(event, function) will only take Discord events as event arguments. These are listed here:
https://discord.js.org/#/docs/discord.js/stable/class/Client
You could however use events from your web server using something similar to below
const client = new Discord.Client({intents: [Discord.Intents.FLAGS.GUILDS, "GUILD_MESSAGES", "GUILD_MEMBERS"]});
yourWebServer.on("yourEvent", (event) => {
client.doSomething()
}
client.login(token)
The main problem to tackle if you wish to use the discord client in this way is identifying the Guild/Message/Role/Whatever you want to change.
Normally, discord bots react to messages so they have access to a lot of data very quickly. In the below example, the bot can get a great deal of information from the message data alone, like the author, the guild it was posted in, the roles in that guild etc. You can see a list of properties here: https://discord.js.org/#/docs/discord.js/stable/class/Message
const client = new Discord.Client({intents: [Discord.Intents.FLAGS.GUILDS, "GUILD_MESSAGES", "GUILD_MEMBERS"]});
client.on("messageCreate", (message) => {
client.doSomething()
});
client.login(token)
If you plan to use this discord bot on just 1 server messing around with your friends or whatever (i do this a lot), you can find your server's Guild ID and hardcode it somewhere into your bot. You can then fetch all roles/messages/users/whatever as you wish.
For any wider usage you'd have to get the context from somewhere else, e.g. inputting ids on a form on your web server.
Have a good read of the discord.js documentation, its very well made in my opinion. Get an idea of what information you want to access, and how you can access it. It will then be easier to determine what context you need to give the bot to find this information.
For those wondering how to use discordjs client commands outside of an event listener it is possible but requires diligent use of async/await. Check this thread for more insight https://github.com/discordjs/discord.js/issues/3100

How to get all non bot users in discord js using a discord bot in nodejs

I have created a discord bot by taking reference from this digital ocean link.
Now I can send message to any channel using the bot but my requirement is to send dm to user of that server.
For that I have tried many SO answers and followed other links, but all the solutions end up to be same.
I have tried this two way to get the users of a guild and send dm to any one selected user.
1st way - Get all users of guild (server)
const client_notification = new Discord.Client();
client_notification.on('ready', () => {
console.log("Notification manager ready");
let guild = client_notification.guilds.cache.get("Server ID");
guild.members.cache.forEach(member => console.log("===>>>", member.user.username));
});
client_notification.login("login");
Output
Notification manager ready
===>>> discord notification
By this way it only returns me the bot name itself. Although the membersCount is 6.
2nd way - send dm to user directly (server)
client.users.cache.get('<id>').send('<message>');
It gives me undefined in output.
My configs,
Node version: 10.16.3
discord.js version: 12.5.1
My question is how to get all the guild members in discord.js?
Discord added privileged gateway intents recently. So to fetch all member data you need to enable that in the developer portal. After that you need to fetch all the members available, for that we can use the fetchAllMembers client option, and then we need to filter out bot users, so that we don't message them.
const client_notification = new Discord.Client({fetchAllMembers:true}); //Fetches all members available on startup.
client_notification.on('ready', () => {
console.log("Notification manager ready");
let guild = client_notification.guilds.cache.get("Server ID");
guild.members.cache.filter(member => !member.user.bot).forEach(member => console.log("===>>>", member.user.username));
});
client_notification.login("login");
The .filter() method filters out all the bots and gives only the real users.
You should avoid using fetchAllMembers when your bot gets larger though, as it could slow down your bot and use lots of memory.
I think the problem is related to updating the bot policy for discord. Do you have this checkbox checked in the bot settings?
https://discord.com/developers/applications
Some info about client.users.cache:
Its a cache collection, so if you restart bot, or bot never handle user message or actions before, this collection will be empty. Better use guild.members.cache.get('')

Send an unsolicited message to Discord channel using nodejs

Looking for a way to send completely unsolicited messages to my discord channel using discord.js.
Background: I have a nodejs app running on my desktop that is monitoring a few websites and I need to notify my team so they can deal with things as the problem evolves. It's an ongoing issue which I'm not getting into right now. Yes, I want to pay for a pro website monitor, but the bean-counters yadda yadda...
I've read through the docs but it seems everything is geared towards event-driven behaviour from the discord side e.g. a new user arrives, a typed command is entered etc
I don't want that. I just want to send a notification when the server that I'm monitoring does X thing.
Here is what I have so far:
const Discord = require('discord.js');
config.discord.client = new Discord.Client();
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
if(shitgoesdown){
new Discord.Message(config.discord.client, data, config.discord.channel);
}
I can't seem to find anything on how to just point this thing at my server. The docs are great and all but they are super dense and right now it's just information overload. If someone could just point at the sign that would be great.
You can fetch the channel using client.channels.fetch(/* Channel ID */) and send a message in the callback using channel.send().
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
// Code to execute when something happens
client.channels.fetch(/* Channel ID */).then(channel => {
channel.send('Some message');
});

Runtime application

Can I use heroku for nodejs application, which must be running always? Now I want create telegram bot without webhook and it must be running always.
const TelegramBot = require('node-telegram-bot-api');
const token = 'MY TELEGRAM BOT KEY';
const bot = new TelegramBot(token, {polling: true});
bot.on('message', (msg) => {
const chatId = msg.chat.id;
// send a message to the chat acknowledging receipt of their message
bot.sendMessage(chatId, 'Received your message');
});
Yes, Deploying and running Node.js apps on Heroku is much easier than we think. You can find a guide about realtime Node.js app on Heroku here
You have to use a paid plan to run realtime apps. As we know the free Heroku Dyno will sleep after 30 mins of inactivity. While we access our app after being inactive we have to wait for a couple of seconds to get back to the active stage. More details about Heroku plans and pricing can be found here
PS: As I am a beginner please don't stop with my answer. You can wait for answers from expert StackOverflow users they are yet to come. Or if you would like, open a ticket on Heroku support and ask your queries there

Resources