Compare commits

..

No commits in common. "1336ee7d0dfedc2395f6fd356f1b2de4ed23ad99" and "c61a6fd28219d57add9ae6d51f30c22964218c64" have entirely different histories.

10 changed files with 91 additions and 275 deletions

View File

@ -4,7 +4,7 @@ import {
SlashCommandBuilder, SlashCommandBuilder,
ChatInputCommandInteraction ChatInputCommandInteraction
} from 'discord.js'; } from 'discord.js';
import { Guilds, VoiceChannels } from '../../database.js'; import { Guilds, VoiceChannels } from '../../../database.js';
export const data = new SlashCommandBuilder() export const data = new SlashCommandBuilder()
.setName('custom_vc') .setName('custom_vc')

View File

@ -1,5 +1,5 @@
import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js'; import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction } from 'discord.js';
import { Roles, Guilds } from '../../database.js'; import { Roles, Guilds } from '../../../database.js';
/** /**
* @param {Guilds} guild * @param {Guilds} guild

View File

@ -115,68 +115,35 @@ async function addResponse(interaction) {
await interaction.showModal(modal); await interaction.showModal(modal);
} }
/** @param {ChatInputCommandInteraction} interaction */
async function removeKeyword(interaction) {
const { options } = interaction;
// Get command options
const keyword = options.getString('name');
// Try deleting keyword from database
await Keywords.destroy({
where: {
guild: interaction.guildId,
name: keyword
}
});
// Reply with success
await interaction.reply({
content: `Keyword '${keyword}' was successfully deleted!`,
ephemeral: true
});
}
/** @param {ChatInputCommandInteraction} interaction */ /** @param {ChatInputCommandInteraction} interaction */
async function removeResponse(interaction) { async function removeResponse(interaction) {
const { options } = interaction; const { options } = interaction;
// Get command options // Get command options
const keyword = options.getString('keyword'); /** @type {'keyword'|'response'} */
const type = options.getString('type');
const name = options.getString('name'); const name = options.getString('name');
// Find keyword in database switch (type) {
/** @type {import('../../models/keywords.js').Keyword|null} */ case 'keyword':
const found = await Keywords.findOne({ // Try removing keyword
await Keywords.destroy({
where: { where: {
guild: interaction.guildId, guild: interaction.guildId,
name: keyword
}
});
// Abort if keyword not found
if (found === null) {
await interaction.reply({
content: 'Unknown keyword was specified!',
ephemeral: true
});
return;
}
// Try deleting response from database
await Responses.destroy({
where: {
keyword: found.id,
name name
} }
}); });
break;
// Reply with success case 'response':
await interaction.reply({ // Try removing response
content: `Response with name '${name}' was successfully deleted!`, await Responses.destroy({
ephemeral: true where: {
guild: interaction.guildId,
name
}
}); });
} }
}
/** @param {ChatInputCommandInteraction} interaction */ /** @param {ChatInputCommandInteraction} interaction */
async function listResponse(interaction) { async function listResponse(interaction) {
@ -208,62 +175,11 @@ async function listResponse(interaction) {
} }
/** @param {ChatInputCommandInteraction} interaction */ /** @param {ChatInputCommandInteraction} interaction */
async function responseInfos(interaction) { async function infoResponse(interaction) {
const { options } = interaction; const { options } = interaction;
// Get command options // Get command options
const keyword = options.getString('keyword'); const keyword = options.getString('keyword');
const name = options.getString('name');
// Find keyword in database
/** @type {import('../../models/keywords.js').Keyword|null} */
const found = await Keywords.findOne({
where: {
guild: interaction.guildId,
name: keyword
}
});
// Abort if keyword not found
if (found === null) {
await interaction.reply({
content: 'Unknown keyword was specified!',
ephemeral: true
});
return;
}
// Find response in database
/** @type {import('../../models/responses.js').Response|null} */
const response = await Responses.findOne({
where: {
keyword: found.id,
name
}
});
// Abort if response not found
if (response === null) {
await interaction.reply({
content: 'Unknown response was specified!',
ephemeral: true
});
return;
}
// Reply with success
await interaction.reply({
content: `Response with name '${name}' has data of \`${response.response}\`!`,
ephemeral: true
});
}
/** @param {ChatInputCommandInteraction} interaction */
async function keywordInfos(interaction) {
const { options } = interaction;
// Get command options
const keyword = options.getString('name');
// Find keyword in database // Find keyword in database
/** @type {import('../../models/keywords.js').Keyword|null} */ /** @type {import('../../models/keywords.js').Keyword|null} */
@ -287,21 +203,13 @@ async function keywordInfos(interaction) {
/** @type {import('../../models/responses.js').Response[]} */ /** @type {import('../../models/responses.js').Response[]} */
const responses = await Responses.findAll({ const responses = await Responses.findAll({
where: { where: {
keyword: found.id guild: interaction.guildId,
name: found.id
} }
}); });
// Abort if no responses registered
if (responses.length === 0) {
await interaction.reply({
content: 'No responses have been registered yet!',
ephemeral: true
});
return;
}
// Join list of responses // Join list of responses
const joined = responses.map((response) => response.name).join('\n- '); const joined = responses.map((response) => response.response).join('\n- ');
// Reply with list of responses // Reply with list of responses
await interaction.reply({ await interaction.reply({
@ -310,13 +218,11 @@ async function keywordInfos(interaction) {
}); });
} }
/** @param {AutocompleteInteraction} interaction */ /**
async function keywordAutocomplete(interaction, focused) { * @param {string} guildId
const { options, guildId } = interaction; * @param {string} focused
*/
// Get command options async function keywordAutocomplete(guildId, focused) {
if (!focused) focused = options.getFocused();
// Get list of keywords from database // Get list of keywords from database
/** @type {import('../../models/keywords.js').Keyword[]} */ /** @type {import('../../models/keywords.js').Keyword[]} */
const keywords = await Keywords.findAll({ const keywords = await Keywords.findAll({
@ -333,15 +239,11 @@ async function keywordAutocomplete(interaction, focused) {
} }
/** /**
* @param {AutocompleteInteraction} interaction * @param {string} guildId
* @param {string} focused * @param {string} focused
* @param {string} keyword
*/ */
async function completeResponses(interaction, focused) { async function responseAutocomplete(guildId, focused, keyword) {
const { options, guildId } = interaction;
// Get command options
const keyword = options.getString('keyword');
// Get keyword // Get keyword
/** @type {import('../../models/keywords.js').Keyword} */ /** @type {import('../../models/keywords.js').Keyword} */
const found = await Keywords.findOne({ const found = await Keywords.findOne({
@ -358,6 +260,7 @@ async function completeResponses(interaction, focused) {
? [] ? []
: await Responses.findAll({ : await Responses.findAll({
where: { where: {
guild: guildId,
keyword: found.id keyword: found.id
} }
}); });
@ -366,22 +269,27 @@ async function completeResponses(interaction, focused) {
const filtered = responses.filter((choice) => choice.name.startsWith(focused)); const filtered = responses.filter((choice) => choice.name.startsWith(focused));
// Respond with possible suggestions // Respond with possible suggestions
await interaction.respond(filtered.map((choice) => ({ name: choice.name, value: choice.name }))); await interaction.respond(
filtered.map((choice) => ({ name: choice.name, value: choice.response }))
);
} }
/** @param {AutocompleteInteraction} interaction */ /** @param {AutocompleteInteraction} interaction */
async function responseAutocomplete(interaction) { async function removeAutocomplete(interaction) {
const { options } = interaction; const { options } = interaction;
// Get command options const type = options.getString('type');
const focused = options.getFocused(true);
const { name, value } = focused; switch (type) {
switch (name) {
case 'keyword': case 'keyword':
keywordAutocomplete(interaction, value); await keywordAutocomplete(interaction.guildId, options.getFocused());
break; break;
case 'name': case 'response':
completeResponses(interaction, value); await responseAutocomplete(
interaction.guildId,
options.getFocused(),
options.getString('keyword')
);
break; break;
} }
} }
@ -414,33 +322,20 @@ export const data = new SlashCommandBuilder()
.setRequired(true) .setRequired(true)
) )
) )
.addSubcommandGroup((group) => .addSubcommand((subcommand) =>
group subcommand
.setName('remove') .setName('remove')
.setDescription('Unregisters a response or a keyword.') .setDescription('Unregisters a response to a keyword.')
.addSubcommand((subcommand) =>
subcommand
.setName('keyword')
.setDescription('Deletes a keyword completely.')
.addStringOption((option) => .addStringOption((option) =>
option option
.setName('name') .setName('type')
.setDescription('The keyword to be deleted.') .setDescription('The type of data to be removed.')
.setAutocomplete(true)
.setRequired(true) .setRequired(true)
.addChoices(
{ name: 'Keyword', value: 'keyword' },
{ name: 'Response', value: 'response' }
) )
) )
.addSubcommand((subcommand) =>
subcommand
.setName('response')
.setDescription('Unregisters a response of a keyword.')
.addStringOption((option) =>
option
.setName('keyword')
.setDescription('The keyword that would trigger the response.')
.setAutocomplete(true)
.setRequired(true)
)
.addStringOption((option) => .addStringOption((option) =>
option option
.setName('name') .setName('name')
@ -449,54 +344,29 @@ export const data = new SlashCommandBuilder()
.setRequired(true) .setRequired(true)
) )
) )
)
.addSubcommand((subcommand) => .addSubcommand((subcommand) =>
subcommand.setName('list').setDescription('Lists all registered keywords.') subcommand.setName('list').setDescription('Lists all registered keywords.')
) )
.addSubcommandGroup((group) => .addSubcommand((subcommand) =>
group subcommand
.setName('info') .setName('info')
.setDescription('Lists information about a response or a keyword.') .setDescription('Shows responses of a registered keyword.')
.addSubcommand((subcommand) =>
subcommand
.setName('keyword')
.setDescription('Lists registered responses of a keyword.')
.addStringOption((option) =>
option
.setName('name')
.setDescription('The keyword to be shown the details of.')
.setAutocomplete(true)
.setRequired(true)
)
)
.addSubcommand((subcommand) =>
subcommand
.setName('response')
.setDescription('Lists the data being sent by a response.')
.addStringOption((option) => .addStringOption((option) =>
option option
.setName('keyword') .setName('keyword')
.setDescription('The keyword that would trigger the response.') .setDescription('The keyword to show the details of.')
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(true) .setRequired(true)
) )
.addStringOption((option) =>
option
.setName('name')
.setDescription('The name of the data to be listed.')
.setAutocomplete(true)
.setRequired(true)
)
)
); );
/** @param {ModalSubmitInteraction} interaction */ /** @param {ModalSubmitInteraction} interaction */
export async function modalSubmit(interaction) { export async function modalSubmit(interaction) {
const { fields } = interaction; const { fields } = interaction;
// Get text inputs from modal // Get text inputs from modal
const name = fields.getTextInputValue('name');
const keyword = fields.getTextInputValue('keyword'); const keyword = fields.getTextInputValue('keyword');
const response = fields.getTextInputValue('response'); const response = fields.getTextInputValue('response');
const name = fields.getTextInputValue('name').toLowerCase();
// Get id of keyword // Get id of keyword
/** @type {import('../../models/keywords.js').Keyword} */ /** @type {import('../../models/keywords.js').Keyword} */
@ -506,50 +376,20 @@ export async function modalSubmit(interaction) {
} }
}); });
// Abort if response exists
if (
(await Responses.findOne({
where: {
keyword: found.id,
name
}
})) !== null
) {
await interaction.reply({
content: `Response with name '${name}' already exists!`,
ephemeral: true
});
return;
}
// Create new response data with keyword attached // Create new response data with keyword attached
await Responses.create({ keyword: found.id, name, response }); await Responses.create({ keyword: found.id, name, response });
// Reply with success
await interaction.reply({
content: `Successfully registered '${name}' as response to '${keyword}'!`,
ephemeral: true
});
} }
/** @param {AutocompleteInteraction} interaction */ /** @param {AutocompleteInteraction} interaction */
export async function autocomplete(interaction) { export async function autocomplete(interaction) {
const { options } = interaction; const { options } = interaction;
const command = options.getSubcommand(); switch (options.getSubcommand()) {
const group = options.getSubcommandGroup(); case 'remove':
const joined = group === null ? command : `${group} ${command}`; removeAutocomplete(interaction);
switch (joined) {
case 'info keyword':
case 'remove keyword':
await keywordAutocomplete(interaction);
break;
case 'info response':
case 'remove response':
await responseAutocomplete(interaction);
break; break;
case 'add': case 'add':
keywordAutocomplete(interaction); case 'info':
keywordAutocomplete(interaction.guildId, interaction.options.getFocused());
break; break;
} }
} }
@ -557,31 +397,21 @@ export async function autocomplete(interaction) {
export async function execute(interaction) { export async function execute(interaction) {
const { options } = interaction; const { options } = interaction;
const command = options.getSubcommand(); switch (options.getSubcommand()) {
const group = options.getSubcommandGroup();
const joined = group === null ? command : `${group} ${command}`;
switch (joined) {
case 'create': case 'create':
createResponse(interaction); createResponse(interaction);
break; break;
case 'add': case 'add':
addResponse(interaction); addResponse(interaction);
break; break;
case 'remove keyword': case 'remove':
removeKeyword(interaction);
break;
case 'remove response':
removeResponse(interaction); removeResponse(interaction);
break; break;
case 'list': case 'list':
listResponse(interaction); listResponse(interaction);
break; break;
case 'info keyword': case 'info':
keywordInfos(interaction); infoResponse(interaction);
break;
case 'info response':
responseInfos(interaction);
break; break;
} }
} }

View File

@ -9,7 +9,7 @@ import {
ContextMenuCommandBuilder, ContextMenuCommandBuilder,
ContextMenuCommandInteraction ContextMenuCommandInteraction
} from 'discord.js'; } from 'discord.js';
import { addSelfRoles } from '../../../shared.js'; import { addSelfRoles } from '../../../../shared.js';
export const data = new ContextMenuCommandBuilder() export const data = new ContextMenuCommandBuilder()
.setDMPermission(false) .setDMPermission(false)

View File

@ -4,7 +4,7 @@ import {
PermissionFlagsBits, PermissionFlagsBits,
ContextMenuCommandInteraction ContextMenuCommandInteraction
} from 'discord.js'; } from 'discord.js';
import { Messages } from '../../../database.js'; import { Messages } from '../../../../database.js';
export const data = new ContextMenuCommandBuilder() export const data = new ContextMenuCommandBuilder()
.setDMPermission(false) .setDMPermission(false)

View File

@ -4,7 +4,7 @@ import {
PermissionFlagsBits, PermissionFlagsBits,
ContextMenuCommandInteraction ContextMenuCommandInteraction
} from 'discord.js'; } from 'discord.js';
import { removeSelfRoles } from '../../../shared.js'; import { removeSelfRoles } from '../../../../shared.js';
export const data = new ContextMenuCommandBuilder() export const data = new ContextMenuCommandBuilder()
.setDMPermission(false) .setDMPermission(false)

View File

@ -1,6 +1,6 @@
import { PermissionFlagsBits, SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; import { PermissionFlagsBits, SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js';
import { addSelfRoles, removeSelfRoles } from '../../shared.js'; import { addSelfRoles, removeSelfRoles } from '../../../shared.js';
import { Guilds, Messages } from '../../database.js'; import { Guilds, Messages } from '../../../database.js';
/** /**
* Sends a `Message` in the current channel and registers for self roles. * Sends a `Message` in the current channel and registers for self roles.

View File

@ -9,7 +9,7 @@ import Module from 'module';
const executeCommand = async (interaction, command) => { const executeCommand = async (interaction, command) => {
// Try executing command // Try executing command
try { try {
console.info(`[INFO] Command '${interaction.commandName}' was executed.`); console.info(`[INFO] Command ${interaction.commandName} was executed.`);
await command.execute(interaction); await command.execute(interaction);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -38,7 +38,7 @@ const executeCommand = async (interaction, command) => {
const genericExecute = async (interaction, command, name, description, cmdName) => { const genericExecute = async (interaction, command, name, description, cmdName) => {
try { try {
console.info( console.info(
`[INFO] Command '${cmdName ?? interaction.commandName ?? 'anonymous'}' ${ `[INFO] Command ${cmdName ?? interaction.commandName ?? 'anonymous'} ${
description ?? `used "${name}"` description ?? `used "${name}"`
}.` }.`
); );

View File

@ -1,13 +0,0 @@
import { Keywords, Responses, sequelize } from '../../database.js';
import { Events, Message } from 'discord.js';
import { Op } from 'sequelize';
export const name = Events.MessageCreate;
/** @param {Message} message */
export async function execute(message) {
// Ignore direct messages
if (!message.inGuild()) return;
// Split message content into words
const words = message.content.split(/\s+/);
}

View File

@ -19,7 +19,6 @@ const runClient = (commands, events) => {
GatewayIntentBits.Guilds, GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessageReactions GatewayIntentBits.GuildMessageReactions
], ],