-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* start of response feature, very dirty * don't mention user * add refresh and command handling * add ability to parseEmojis * add crude list option * final changes
- Loading branch information
Showing
8 changed files
with
330 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { SlashCommandBuilder, PermissionFlagsBits, type Guild, type ChatInputCommandInteraction, Collection } from 'discord.js'; | ||
import { Command } from '../../typings/index.js'; | ||
import { checkEmoji, embedEntries } from '../../helpers.js'; | ||
import { randomUUID } from 'node:crypto'; | ||
import { Responses } from '../../typings/database.js'; | ||
|
||
const responsesCommand: Command = { | ||
data: new SlashCommandBuilder() | ||
.addSubcommand(subcommand => | ||
subcommand | ||
.setName('add') | ||
.setDescription('Add a response to the guild.') | ||
.addStringOption(option => | ||
option | ||
.setName('type') | ||
.setDescription('What type of response is this?') | ||
.addChoices( | ||
{ name: 'word', value: 'word' }, | ||
{ name: 'phrase', value: 'phrase' } | ||
) | ||
.setRequired(true) | ||
) | ||
.addStringOption(option => | ||
option | ||
.setName('response_trigger') | ||
.setDescription('What should the response trigger be?') | ||
.setRequired(true) | ||
) | ||
.addStringOption(option => | ||
option | ||
.setName('response_type') | ||
.setDescription('What should the reply be?') | ||
.addChoices( | ||
{ name: 'reaction', value: 'reaction' }, | ||
{ name: 'message', value: 'message' } | ||
) | ||
.setRequired(true) | ||
) | ||
.addStringOption(option => | ||
option | ||
.setName('response_value') | ||
.setDescription('What should the response be?') | ||
.setRequired(true) | ||
) | ||
) | ||
.addSubcommand(option => | ||
option | ||
.setName('list') | ||
.setDescription('List configured responses in the guild.') | ||
) | ||
.addSubcommand(subcommand => | ||
subcommand | ||
.setName('remove') | ||
.setDescription('Remove a response from the guild.') | ||
.addStringOption(option => | ||
option | ||
.setName('id') | ||
.setDescription('ID of the response to remove from the guild.') | ||
.setRequired(true) | ||
) | ||
) | ||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels) | ||
.setDMPermission(false) | ||
.setName('response') | ||
.setDescription('Configuration for the response feature!'), | ||
async execute(interaction) { | ||
if (!interaction.channel || !interaction.channel.isTextBased() || !interaction.inCachedGuild()) return; | ||
|
||
// This should never run but we will do this anyway, command is blocked from dms | ||
if (!interaction.guild) return interaction.reply({ content: `This command must be ran in a guild!`, ephemeral: true }) | ||
.catch(console.error); | ||
|
||
await interaction.deferReply({ ephemeral: true }) | ||
.catch(console.error); | ||
|
||
const subCommand = interaction.options.getSubcommand(); | ||
switch (subCommand) { | ||
case 'add': | ||
await addResponse(interaction.guild, interaction); | ||
break; | ||
case 'list': | ||
await listResponses(interaction.guild, interaction); | ||
break; | ||
case 'remove': | ||
await removeResponse(interaction.guild, interaction); | ||
break; | ||
} | ||
|
||
if (!interaction.replied) { | ||
interaction.editReply(`Function has completed but no reply was given, please contact a bot administrator.`) | ||
.catch(console.error); | ||
} | ||
|
||
return; | ||
} | ||
}; | ||
|
||
async function addResponse(guild: Guild, interaction: ChatInputCommandInteraction) { | ||
const responseType = interaction.options.getString('response_type', true); | ||
const responseValue = interaction.options.getString('response_value', true); | ||
if (responseType === 'reaction') { | ||
const validEmoji = checkEmoji(responseValue); | ||
if (!validEmoji) { | ||
return interaction.editReply(`Please supply a valid emoji for the reaction.`); | ||
} | ||
} | ||
|
||
await interaction.client.db | ||
.insertInto('responses') | ||
.values({ | ||
id: randomUUID(), | ||
guild_id: guild.id, | ||
type: interaction.options.getString('type', true), | ||
response_type: responseType, | ||
trigger: interaction.options.getString('response_trigger', true), | ||
value: responseValue, | ||
}) | ||
.execute(); | ||
|
||
// Tell the utility to refresh the cache | ||
await interaction.client.util.get('autoResponse') | ||
?.refreshCache?.(); | ||
|
||
return interaction.editReply(`Response added to guild!`); | ||
} | ||
|
||
async function listResponses(guild: Guild, interaction: ChatInputCommandInteraction) { | ||
const guildResponses: Collection<string, Responses> = interaction.client.util.get('autoResponse') | ||
?.cache | ||
?.responses[guild.id]; | ||
if (!guildResponses) return interaction.editReply('No configured responses in this guild.'); | ||
|
||
const embeds = embedEntries(guildResponses.toJSON(), { | ||
title: `Responses for ${guild.name}` | ||
}, (embed, response) => { | ||
// We only get 25 fields each embed, value is not human readable thanks to mobile | ||
embed.addFields({ | ||
name: `${response.id}`, | ||
value: `**🏷️Type**: ${response.type}\n**🪤Trigger**: ${response.trigger}\n**🗣️Response Type**: ${response.response_type}\n**📋Value**: ${response.value}`, | ||
inline: true | ||
}); | ||
}); | ||
if (!embeds) return interaction.editReply(`There are no embeds in response, unable to send data.`); | ||
|
||
return interaction.editReply({ | ||
embeds: embeds | ||
}); | ||
} | ||
|
||
async function removeResponse(guild: Guild, interaction: ChatInputCommandInteraction) { | ||
const responseId = interaction.options.get('id', true).value as string; | ||
if (!responseId) return interaction.editReply(`Required values have not been supplied`); | ||
|
||
const removedResponse = await interaction.client.db | ||
.deleteFrom('responses') | ||
.where('id', '=', responseId) | ||
.where('guild_id', '=', guild.id) | ||
.executeTakeFirst() | ||
.catch(() => {}); | ||
if (!removedResponse || removedResponse.numDeletedRows <= 0) return interaction.editReply(`A response by the supplied ID was not found, skipping.`); | ||
|
||
// Tell the utility to refresh the cache | ||
await interaction.client.util.get('autoResponse') | ||
?.refreshCache?.(); | ||
|
||
return interaction.editReply(`Removed response successfully.`); | ||
} | ||
|
||
export default responsesCommand; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/database/migrations/2024-06-15T025030_add_responses_table.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Kysely, sql } from 'kysely'; | ||
|
||
export async function up(db: Kysely<any>): Promise<void> { | ||
await db.schema | ||
.createTable('responses') | ||
.addColumn('id', 'uuid', col => col.primaryKey()) | ||
.addColumn('guild_id', 'varchar(255)', col => col.notNull()) | ||
.addColumn('type', 'varchar(255)') | ||
.addColumn('response_type', 'varchar(255)') | ||
.addColumn('trigger', 'varchar(255)') | ||
.addColumn('value', 'varchar(255)') | ||
.addColumn('created_at', 'timestamp', (col) => | ||
col.defaultTo(sql`now()`).notNull() | ||
) | ||
.execute(); | ||
} | ||
|
||
export async function down(db: Kysely<any>): Promise<void> { | ||
await db.schema | ||
.dropTable('responses') | ||
.execute(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.