Add Guild and CustomActivity auditing
This commit is contained in:
		
							parent
							
								
									f1767c915c
								
							
						
					
					
						commit
						bc5f7a0473
					
				|  | @ -1,5 +1,5 @@ | ||||||
| import config from '../../config.ts'; | 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 { infoColor2, isLFGChannel, somethingWentWrong } from '../commandUtils.ts'; | ||||||
| import { dbClient } from '../db/client.ts'; | import { dbClient } from '../db/client.ts'; | ||||||
| import { queries } from '../db/common.ts'; | import { queries } from '../db/common.ts'; | ||||||
|  | @ -7,8 +7,20 @@ import { CommandDetails } from '../types/commandTypes.ts'; | ||||||
| import utils from '../utils.ts'; | import utils from '../utils.ts'; | ||||||
| import { auditSlashName } from './slashCommandNames.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 auditDbName = 'database'; | ||||||
| const auditCustomActivities = 'custom-activities'; | const auditCustomActivitiesName = 'custom-activities'; | ||||||
| const auditGuildName = 'guilds'; | const auditGuildName = 'guilds'; | ||||||
| 
 | 
 | ||||||
| const details: CommandDetails = { | const details: CommandDetails = { | ||||||
|  | @ -23,7 +35,7 @@ const details: CommandDetails = { | ||||||
| 			description: `Developer Command: Checks ${config.name}'s DB size.`, | 			description: `Developer Command: Checks ${config.name}'s DB size.`, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: auditCustomActivities, | 			name: auditCustomActivitiesName, | ||||||
| 			type: ApplicationCommandOptionTypes.SubCommand, | 			type: ApplicationCommandOptionTypes.SubCommand, | ||||||
| 			description: 'Developer Command: Checks for duplicate custom activities.', | 			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) { | 	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)); | 		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; | 		const auditName = interaction.data.options[0].name; | ||||||
| 		switch (auditName) { | 		switch (auditName) { | ||||||
| 			case auditDbName: { | 			case auditDbName: { | ||||||
| 				// Get DB statistics
 | 				// 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
 | 				// 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> = []; | 				const embedFields: Array<DiscordEmbedField> = []; | ||||||
| 				auditQuery.forEach((row: any) => { | 				auditQuery.forEach((row) => { | ||||||
| 					embedFields.push({ | 					embedFields.push({ | ||||||
| 						name: `${row.table}`, | 						name: `${row.table}`, | ||||||
| 						value: `**Size:** ${row.size} MB
 | 						value: `**Size:** ${row.size} MB
 | ||||||
|  | @ -54,27 +66,129 @@ const execute = async (bot: Bot, interaction: Interaction) => { | ||||||
| 						inline: true, | 						inline: true, | ||||||
| 					}); | 					}); | ||||||
| 				}); | 				}); | ||||||
| 				bot.helpers.sendInteractionResponse( | 				bot.helpers | ||||||
| 					interaction.id, | 					.sendInteractionResponse(interaction.id, interaction.token, { | ||||||
| 					interaction.token, |  | ||||||
| 					{ |  | ||||||
| 						type: InteractionResponseTypes.ChannelMessageWithSource, | 						type: InteractionResponseTypes.ChannelMessageWithSource, | ||||||
| 						data: { | 						data: { | ||||||
| 							flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n), | 							flags: isLFGChannel(interaction.guildId || 0n, interaction.channelId || 0n), | ||||||
| 							embeds: [{ | 							embeds: [ | ||||||
|  | 								{ | ||||||
| 									color: infoColor2, | 									color: infoColor2, | ||||||
| 									title: 'Database Audit', | 									title: 'Database Audit', | ||||||
| 									description: 'Lists all tables with their current size and row count.', | 									description: 'Lists all tables with their current size and row count.', | ||||||
| 									timestamp: new Date().getTime(), | 									timestamp: new Date().getTime(), | ||||||
| 									fields: embedFields.slice(0, 25), | 									fields: embedFields.slice(0, 25), | ||||||
| 							}], |  | ||||||
| 								}, | 								}, | ||||||
|  | 							], | ||||||
| 						}, | 						}, | ||||||
| 				).catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@dbSize', interaction, e)); | 					}) | ||||||
|  | 					.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: '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(), | ||||||
|  | 							}], | ||||||
|  | 							file: { | ||||||
|  | 								'blob': b.size > 8388290 ? tooBig : b, | ||||||
|  | 								'name': 'auditDetails.txt', | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}) | ||||||
|  | 					.catch((e: Error) => utils.commonLoggers.interactionSendError('audit.ts@guilds', interaction, e)); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 			case auditCustomActivities: |  | ||||||
| 			case auditGuildName: |  | ||||||
| 			default: | 			default: | ||||||
| 				somethingWentWrong(bot, interaction, `auditNameNotHandled@${auditName}`); | 				somethingWentWrong(bot, interaction, `auditNameNotHandled@${auditName}`); | ||||||
| 				break; | 				break; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue