Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
fix: refactored shards command
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusbegby committed Sep 13, 2023
1 parent c689452 commit 9259bc2
Showing 1 changed file with 120 additions and 78 deletions.
198 changes: 120 additions & 78 deletions src/interactions/commands/system/shards.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { EmbedBuilder, EmbedField, SlashCommandBuilder } from 'discord.js';
import {
ChatInputCommandInteraction,
EmbedBuilder,
EmbedField,
EmbedFooterData,
SlashCommandBuilder
} from 'discord.js';
import { BaseSlashCommandInteraction } from '../../../classes/interactions';
import { ExtendedClient } from '../../../types/clientTypes';
import { BaseSlashCommandParams, BaseSlashCommandReturnType, ShardInfo } from '../../../types/interactionTypes';
import { checkValidGuildId } from '../../../utils/validation/systemCommandValidator';
import { Logger } from 'pino';

class ShardsCommand extends BaseSlashCommandInteraction {
constructor() {
Expand Down Expand Up @@ -37,13 +44,49 @@ class ShardsCommand extends BaseSlashCommandInteraction {

await this.runValidators({ interaction, executionId }, [checkValidGuildId]);

const shardInfoList: ShardInfo[] = await this.fetchShardInfo(
client!,
logger,
interaction.options.getString('sort')
);

const currentShardId: number = client!.shard!.ids[0];
const shardCount: number = shardInfoList.length;
const pageIndex: number = this.getPageIndex(interaction);
const totalPages: number = this.getTotalPages(shardCount);

if (pageIndex > totalPages - 1) {
return await this.handleInvalidPage(logger, interaction, pageIndex, totalPages);
}

const currentPageShards: ShardInfo[] = shardInfoList.slice(pageIndex * 10, pageIndex * 10 + 10);
const embedFields: EmbedField[] = this.buildEmbedFields(currentPageShards);

logger.debug('Responding with info embed.');
return await interaction.editReply({
embeds: [
new EmbedBuilder()
.setDescription(
`**${this.embedOptions.icons.server} Shard overview - ${shardCount} total shards**\n`
)
.addFields(...embedFields)
.setColor(this.embedOptions.colors.info)
.setFooter(this.getFooterPageInfo(currentShardId, pageIndex, totalPages))
]
});
}

private async fetchShardInfo(
client: ExtendedClient,
logger: Logger,
sortOption: string | null
): Promise<ShardInfo[]> {
let shardInfoList: ShardInfo[] = [];

logger.debug('Fetching player statistics and client values from each shard.');
try {
await client!
.shard!.broadcastEval((shardClient: ExtendedClient) => {
// Type for generateStatistics?
const playerStats = player.generateStatistics();
const nodeProcessMemUsageInMb: number = parseFloat(
(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)
Expand All @@ -65,89 +108,31 @@ class ShardsCommand extends BaseSlashCommandInteraction {
})
.then((results) => {
shardInfoList = results.filter(Boolean) as ShardInfo[];

switch (interaction.options.getString('sort')) {
case 'memory':
shardInfoList = shardInfoList.sort((a, b) => b.memUsage - a.memUsage);
break;
case 'connections':
shardInfoList = shardInfoList.sort(
(a, b) =>
b.playerStatistics.activeVoiceConnections -
a.playerStatistics.activeVoiceConnections
);
break;
case 'tracks':
shardInfoList = shardInfoList.sort(
(a, b) => b.playerStatistics.totalTracks - a.playerStatistics.totalTracks
);
break;
case 'listeners':
shardInfoList = shardInfoList.sort(
(a, b) => b.playerStatistics.totalListeners - a.playerStatistics.totalListeners
);
break;
case 'guilds':
shardInfoList = shardInfoList.sort((a, b) => b.guildCount - a.guildCount);
break;
case 'members':
shardInfoList = shardInfoList.sort((a, b) => b.guildMemberCount - a.guildMemberCount);
break;
default:
shardInfoList = shardInfoList.sort((a, b) => a.shardId - b.shardId);
break;
}

shardInfoList = this.sortShardInfoList(shardInfoList, sortOption);
logger.debug('Successfully fetched player statistics and client values from shards.');
});
} catch (error) {
logger.error(error, 'Failed to fetch player statistics and client values from shards.');

logger.debug('Responding with error embed.');
return await interaction.editReply({
embeds: [
new EmbedBuilder()

.setDescription(
`**${this.embedOptions.icons.error} Oops!**\n_Hmm.._ It seems I am unable to fetch player statistics and client values from shards.`
)
.setColor(this.embedOptions.colors.error)
.setFooter({ text: `Execution ID: ${executionId}` })
]
});
throw error;
}

const shardCount: number = shardInfoList.length;
const totalPages: number = Math.ceil(shardCount / 10) || 1;
const pageIndex: number = (interaction.options.getNumber('page') || 1) - 1;

const currentPageShards: ShardInfo[] = shardInfoList.slice(pageIndex * 10, pageIndex * 10 + 10);
return shardInfoList;
}

const evenShardIndexes: ShardInfo[] = currentPageShards.filter((shard, index) => index % 2 === 0);
const oddShardIndexes: ShardInfo[] = currentPageShards.filter((shard, index) => index % 2 !== 0);

function shardInfoToString(shard: ShardInfo) {
let string: string = '';
string += `**Shard ${shard.shardId}** - Guilds: ${shard.guildCount.toLocaleString(
'en-US'
)} (${shard.guildMemberCount.toLocaleString('en-US')})\n`;
string += `**Node.js memory:** ${shard.memUsage.toLocaleString('en-US')} MB\n`;
string += `**┣** Connections: ${shard.playerStatistics.activeVoiceConnections.toLocaleString('en-US')}\n`;
string += `**┣** Tracks: ${shard.playerStatistics.totalTracks.toLocaleString('en-US')}\n`;
string += `**┗** Listeners: ${shard.playerStatistics.totalListeners.toLocaleString('en-US')}\n`;
return string;
}
private buildEmbedFields(shards: ShardInfo[]): EmbedField[] {
const evenShardIndexes: ShardInfo[] = shards.filter((shard, index) => index % 2 === 0);
const oddShardIndexes: ShardInfo[] = shards.filter((shard, index) => index % 2 !== 0);

const evenShardIndexesString: string =
evenShardIndexes.map(shardInfoToString).join('\n') + 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ';
const oddShardIndexesString: string = oddShardIndexes.map(shardInfoToString).join('\n');
evenShardIndexes.map(this.shardInfoToString).join('\n') + 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ';
const oddShardIndexesString: string = oddShardIndexes.map(this.shardInfoToString).join('\n');

const embedFields: EmbedField[] = [];

if (currentPageShards.length === 1) {
if (shards.length === 1) {
embedFields.push({
name: ' ',
value: currentPageShards.map(shardInfoToString).join('\n'),
value: shards.map(this.shardInfoToString).join('\n'),
inline: false
});
} else {
Expand All @@ -165,20 +150,77 @@ class ShardsCommand extends BaseSlashCommandInteraction {
);
}

logger.debug('Successfully gathered and transformed shard information into embed fields.');
return embedFields;
}

private sortShardInfoList(shardInfoList: ShardInfo[], sortOption: string | null): ShardInfo[] {
switch (sortOption) {
case 'memory':
return shardInfoList.sort((a, b) => b.memUsage - a.memUsage);
case 'connections':
return shardInfoList.sort(
(a, b) => b.playerStatistics.activeVoiceConnections - a.playerStatistics.activeVoiceConnections
);
case 'tracks':
return shardInfoList.sort((a, b) => b.playerStatistics.totalTracks - a.playerStatistics.totalTracks);
case 'listeners':
return shardInfoList.sort(
(a, b) => b.playerStatistics.totalListeners - a.playerStatistics.totalListeners
);
case 'guilds':
return shardInfoList.sort((a, b) => b.guildCount - a.guildCount);
case 'members':
return shardInfoList.sort((a, b) => b.guildMemberCount - a.guildMemberCount);
default:
return shardInfoList.sort((a, b) => a.shardId - b.shardId);
}
}

logger.debug('Responding with info embed.');
return await interaction.editReply({
private shardInfoToString(shard: ShardInfo): string {
let string: string = '';
string += `**Shard ${shard.shardId}** - Guilds: ${shard.guildCount.toLocaleString(
'en-US'
)} (${shard.guildMemberCount.toLocaleString('en-US')})\n`;
string += `**Node.js memory:** ${shard.memUsage.toLocaleString('en-US')} MB\n`;
string += `**┣** Connections: ${shard.playerStatistics.activeVoiceConnections.toLocaleString('en-US')}\n`;
string += `**┣** Tracks: ${shard.playerStatistics.totalTracks.toLocaleString('en-US')}\n`;
string += `**┗** Listeners: ${shard.playerStatistics.totalListeners.toLocaleString('en-US')}\n`;
return string;
}

private getPageIndex(interaction: ChatInputCommandInteraction): number {
return (interaction.options.getNumber('page') || 1) - 1;
}

private getTotalPages(shardCount: number): number {
return Math.ceil(shardCount / 10) || 1;
}

private getFooterPageInfo(currentShardId: number, pageIndex: number, totalPages: number): EmbedFooterData {
return { text: `Shard id: ${currentShardId}, page ${pageIndex + 1} of ${totalPages}` };
}

private async handleInvalidPage(
logger: Logger,
interaction: ChatInputCommandInteraction,
pageIndex: number,
totalPages: number
) {
logger.debug('Specified page was higher than total pages.');

logger.debug('Responding with warning embed.');
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setDescription(
`**${this.embedOptions.icons.server} Shard overview - ${shardCount} total shards**\n`
`**${this.embedOptions.icons.warning} Oops!**\n` +
`Page **\`${pageIndex + 1}\`** is not a valid page number.\n\n` +
`There are only a total of **\`${totalPages}\`** pages available.`
)
.addFields(...embedFields)
.setColor(this.embedOptions.colors.info)
.setFooter({ text: `Shard id: ${client!.shard!.ids[0]}, page ${pageIndex + 1} of ${totalPages}` })
.setColor(this.embedOptions.colors.warning)
]
});
return Promise.resolve();
}
}

Expand Down

0 comments on commit 9259bc2

Please sign in to comment.