sharding-stats
Version:
A cool Dashboard & Manager for showing Live Stats and managing your Discord Bot/Shards
127 lines (120 loc) • 5.39 kB
JavaScript
const fetch = require('node-fetch');
const os = require('node-os-utils');
class Client {
constructor(client, config) {
this.client = client;
this.config = config;
this.shardMessage = new Map();
this.deleteCachedShardStatus();
if(!config.customPoster) {
this._validateOptions();
this._attachEvents();
this._autopost();
}
}
async sendPostData(body) {
return fetch(`${this.config.stats_uri}stats`, {
method: 'POST',
headers: {
'Authorization': Buffer.from(this.config.authorizationkey).toString('base64'),
'Accept': 'application/json, text/plain, */*','User-Agent': '*',
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
}).then(res => res.json()).then((m) => this._handleMessage(m)).catch((e) => console.error(new Error(e)))
}
async post() {
const shards = [...this.client.ws.shards.values()]
const guilds = [...this.client.guilds.cache.values()]
for (let i = 0; i < shards.length; i++) {
const filteredGuilds = guilds ? guilds.filter(x => x.shardId === shards[i].id) : [];
/* // example data
{
id: 0,
status: 0,
ping: 10,
cpu: 3.45,
ram: { rss: 385, heapUsed: 185 }
membercount: 30446,
guildcount: 444,
guildids: [ '907888291725053965', '957913119861129217', '868774602451607572', '...' ],
upsince: 8879483,
cluster: 0, // only gets added, if you have clustering ;)
},
*/
const ram = this.getRamUsageinMB();
const cpu = await this.receiveCPUUsage();
const upsince = this.client.uptime;
const message = (this.shardMessage.get(shards[i] ? shards[i].id : NaN) || `No Message Available`);
const body = {
id: shards[i] ? shards[i].id : NaN,
status: shards[i] ? shards[i].status : 5,
cpu, ram,
message,
ping: shards[i] ? shards[i].ping : NaN,
membercount: filteredGuilds.map(x => x.memberCount || x.members?.cache?.size || 0).reduce((a, b) => a + b, 0),
guildcount: filteredGuilds.filter(x => x.shardId === shards[i].id).filter(Boolean).length,
guildids: filteredGuilds.filter(x => x.shardId === shards[i].id).map(x => x?.id).filter(Boolean),
upsince,
};
if (typeof this.client?.cluster?.id !== "undefined") body.cluster = `${this.client.cluster.id}`;
this.sendPostData(body);
}
}
async receiveCPUUsage() {
try {
return await os.cpu.usage(100);
} catch {
return 0
}
}
getRamUsageinMB() {
const { rss, heapUsed } = process.memoryUsage();
return {
rss: Math.floor(rss / 1024 / 1024 * 100) / 100,
heapUsed: Math.floor(heapUsed / 1024 / 1024 * 100) / 100
}
}
deleteCachedShardStatus() {
return fetch(`${this.config.stats_uri}api/deleteShards`, {
method: 'POST',
headers: {
'Authorization': Buffer.from(this.config.authorizationkey).toString('base64'),
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ kill: true, shards: 'all' }),
}).catch((e) => e)
}
_autopost() {
setInterval(() => {
this.post()
}, this.config.postinterval)
}
_attachEvents() {
this.client.on('debug', (message) => {
if (message.includes(`Shard`)) {
const shards = [...this.client.ws.shards.values()]
for (let i = 0; i < shards.length; i++) {
if (message.includes(`[WS => Shard ${shards[i].id}]`)) {
this.shardMessage.set(shards[i].id, message.replace(`[WS => Shard ${shards[i].id}]`, ''))
}
}
}
})
}
_handleMessage(message) {
if (!message.kill) return;
if (message.shard === undefined) return;
if (this.client.ws.shards.has(message.shard)) return this.client.ws.shards.get(message.shard).destroy();
return false;
}
_validateOptions() {
if (!this.config.authorizationkey) throw new Error('Pls provide your choosen Authorization Key for verifying Requests.');
if (this.config.postinterval && isNaN(this.config.postinterval)) throw new Error('The PostInterval is not a valid Time. Provide the Interval in milliseconds');
if (!this.config.postinterval) this.config.postinterval = 5000;
if (!this.config.stats_uri || typeof this.config.stats_uri !== "string") throw new Error("Pls provide your base stats_uri, e.g: http://localhost:3333, or http:192.168.0.1:3333");
if(!this.config.stats_uri?.endsWith?.("/")) this.config.stats_uri += "/";
}
}
module.exports = Client;