Add Guild and CustomActivity auditing

This commit is contained in:
Ean Milligan 2024-05-20 02:54:45 -04:00
parent f1767c915c
commit bc5f7a0473
1 changed files with 131 additions and 17 deletions

View File

@ -1,5 +1,5 @@
import config from '../../config.ts';
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, Bot, DiscordEmbedField, Interaction, InteractionResponseTypes } from '../../deps.ts';
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, BotWithCache, DiscordEmbedField, Interaction, InteractionResponseTypes } from '../../deps.ts';
import { infoColor2, isLFGChannel, somethingWentWrong } from '../commandUtils.ts';
import { dbClient } from '../db/client.ts';
import { queries } from '../db/common.ts';
@ -7,8 +7,20 @@ import { CommandDetails } from '../types/commandTypes.ts';
import utils from '../utils.ts';
import { auditSlashName } from './slashCommandNames.ts';
type DupeAct = {
upperActTitle?: string;
upperActSubtitle?: string;
dupeCount: number;
};
type DBSizeTable = {
table: string;
size: number;
rows: number;
};
const auditDbName = 'database';
const auditCustomActivities = 'custom-activities';
const auditCustomActivitiesName = 'custom-activities';
const auditGuildName = 'guilds';
const details: CommandDetails = {
@ -23,7 +35,7 @@ const details: CommandDetails = {
description: `Developer Command: Checks ${config.name}'s DB size.`,
},
{
name: auditCustomActivities,
name: auditCustomActivitiesName,
type: ApplicationCommandOptionTypes.SubCommand,
description: 'Developer Command: Checks for duplicate custom activities.',
},
@ -35,18 +47,18 @@ const details: CommandDetails = {
],
};
const execute = async (bot: Bot, interaction: Interaction) => {
const execute = async (bot: BotWithCache, interaction: Interaction) => {
if (interaction.member && interaction.guildId && interaction.data?.options?.[0].options) {
dbClient.execute(queries.callIncCnt('cmd-audit')).catch((e) => utils.commonLoggers.dbError('audit.ts@inc', 'call sproc INC_CNT on', e));
const auditName = interaction.data.options[0].name;
switch (auditName) {
case auditDbName: {
// Get DB statistics
const auditQuery = await dbClient.query(`SELECT * FROM db_size;`).catch((e) => utils.commonLoggers.dbError('audit.ts@dbSize', 'query', e));
const auditQuery: Array<DBSizeTable> = await dbClient.query(`SELECT * FROM db_size;`).catch((e) => utils.commonLoggers.dbError('audit.ts@dbSize', 'query', e));
// Turn all tables into embed fields, currently only properly will handle 25 tables, but we'll fix that when group up gets 26 tables
const embedFields: Array<DiscordEmbedField> = [];
auditQuery.forEach((row: any) => {
auditQuery.forEach((row) => {
embedFields.push({
name: `${row.table}`,
value: `**Size:** ${row.size} MB
@ -54,27 +66,129 @@ const execute = async (bot: Bot, interaction: Interaction) => {
inline: true,
});
});
bot.helpers.sendInteractionResponse(
interaction.id,
interaction.token,
{
bot.helpers
.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
embeds: [
{
color: infoColor2,
title: 'Database Audit',
description: 'Lists all tables with their current size and row count.',
timestamp: new Date().getTime(),
fields: embedFields.slice(0, 25),
},
],
},
})
.catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@dbSize', interaction, e));
break;
}
case auditCustomActivitiesName: {
const dupActTitles: Array<DupeAct> = await dbClient.query(
`SELECT UPPER(activityTitle) as upperActTitle, COUNT(*) as dupeCount FROM custom_activities GROUP BY upperActTitle HAVING dupeCount > 1;`,
).catch((e) => utils.commonLoggers.dbError('audit.ts@customActTitle', 'query', e));
const dupActSubTitles: Array<DupeAct> = await dbClient.query(
`SELECT UPPER(activitySubtitle) as upperActSubtitle, COUNT(*) as dupeCount FROM custom_activities GROUP BY upperActSubtitle HAVING dupeCount > 1;`,
).catch((e) => utils.commonLoggers.dbError('audit.ts@customActSubTitle', 'query', e));
const dupActs: Array<DupeAct> = await dbClient
.query(
`SELECT UPPER(activityTitle) as upperActTitle, UPPER(activitySubtitle) as upperActSubtitle, COUNT(*) as dupeCount FROM custom_activities GROUP BY upperActTitle, upperActSubtitle HAVING dupeCount > 1;`,
)
.catch((e) => utils.commonLoggers.dbError('audit.ts@customAct', 'query', e));
bot.helpers
.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
content: 'Duplicate Custom Activity Titles, Subtitles, and Activities:',
embeds: [
{
color: infoColor2,
title: 'Duplicate Activity Titles:',
description: dupActTitles.map((dupAct) => `${dupAct.upperActTitle}: ${dupAct.dupeCount}`).join('\n'),
timestamp: new Date().getTime(),
},
{
color: infoColor2,
title: 'Duplicate Activity Subtitles:',
description: dupActSubTitles.map((dupAct) => `${dupAct.upperActSubtitle}: ${dupAct.dupeCount}`).join('\n'),
timestamp: new Date().getTime(),
},
{
color: infoColor2,
title: 'Duplicate Activities (Title/Subtitle):',
description: dupActs.map((dupAct) => `${dupAct.upperActTitle}/${dupAct.upperActSubtitle}: ${dupAct.dupeCount}`).join('\n'),
timestamp: new Date().getTime(),
},
],
},
})
.catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@dbSize', interaction, e));
break;
}
case auditGuildName: {
let totalCount = 0;
let auditText = '';
bot.guilds.forEach((guild) => {
totalCount += guild.memberCount;
auditText += `Guild: ${guild.name} (${guild.id})
Owner: ${guild.ownerId}
Tot mem: ${guild.memberCount}
`;
});
const b = await new Blob([auditText as BlobPart], { 'type': 'text' });
const tooBig = await new Blob(['tooBig' as BlobPart], { 'type': 'text' });
bot.helpers
.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n),
embeds: [{
color: infoColor2,
title: 'Database Audit',
description: 'Lists all tables with their current size and row count.',
title: 'Guilds Audit',
description: `Shows details of the guilds that ${config.name} serves.
Please see attached file for audit details on cached guilds and members.`,
fields: [
{
name: 'Total Guilds:',
value: `${bot.guilds.size}`,
inline: true,
},
{
name: 'Uncached Guilds:',
value: `${bot.dispatchedGuildIds.size}`,
inline: true,
},
{
name: 'Total Members\n(may be artificially higher if 1 user is in multiple guilds the bot is in):',
value: `${totalCount}`,
inline: true,
},
{
name: 'Average members per guild:',
value: `${(totalCount / bot.guilds.size).toFixed(2)}`,
inline: true,
},
],
timestamp: new Date().getTime(),
fields: embedFields.slice(0, 25),
}],
file: {
'blob': b.size > 8388290 ? tooBig : b,
'name': 'auditDetails.txt',
},
},
},
).catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@dbSize', interaction, e));
})
.catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@guilds', interaction, e));
break;
}
case auditCustomActivities:
case auditGuildName:
default:
somethingWentWrong(bot, interaction, `auditNameNotHandled@${auditName}`);
break;