Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Prefix Command Management #85

Open
wants to merge 51 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
22f4d96
Initial prefix commands management
pdellaert Jan 10, 2024
763c6a0
Splitting into multiple commands
pdellaert Jan 11, 2024
7a21eff
Migrating Schema and adapting Content management
pdellaert Mar 3, 2024
d4d401f
Updating permissions for new schema and fix issues
pdellaert Mar 3, 2024
b9905bc
Adding AutoComplete and Content Modal
pdellaert May 27, 2024
96c1c3a
Adding Default Channel Versions and add autocomplete to everything
pdellaert Sep 2, 2024
4a76dfa
Introducing cache mechanism to keep local cache, manage it and initia…
pdellaert Sep 5, 2024
03163ee
Making cache and command checks case-insensitive
pdellaert Sep 5, 2024
fc35d7c
Adding a scheduled task to refresh the cache automatically
pdellaert Sep 5, 2024
4546bfe
Adding Categories and Default Channel Versions to cache
pdellaert Sep 7, 2024
73ca86e
Managing Category cache and Default Version Cache
pdellaert Sep 7, 2024
3dd97ce
Adding default channel version parsing
pdellaert Sep 7, 2024
52a7351
Refactor cache management to be more intuitive and introduce version …
pdellaert Sep 8, 2024
114ed1f
Fixing staging config json
pdellaert Sep 8, 2024
ce78316
Refactor Permission mechanism
pdellaert Sep 8, 2024
7dd6207
Finalizing permission work
pdellaert Sep 8, 2024
3caa8fa
Adding description to command and fixing autoComplete for Prefix comm…
pdellaert Oct 14, 2024
32b6c53
Made changes to Prefix Command Handler and Cache Manager:
pdellaert Oct 14, 2024
8224fab
Introducing prefix help to show list of prefix commands per category
pdellaert Oct 14, 2024
68c8de9
Use appropriate language for blocklist
pdellaert Oct 14, 2024
de2356e
Making the max length more standardized
pdellaert Oct 14, 2024
9fbbf33
Show the emoji of a category
pdellaert Oct 14, 2024
9238f42
Show the buttons for generic in case the version specific content doe…
pdellaert Oct 14, 2024
152d834
Fixing bug when there is no CDV set for a channel
pdellaert Oct 14, 2024
fb5db69
Adding initial documentation for prefix-commands
pdellaert Oct 14, 2024
5b867cf
Refactor cache management for Channel Default Versions
pdellaert Oct 15, 2024
a4351c7
Refactoring cache
pdellaert Oct 15, 2024
984b1db
Updating docs on cachi auto refresh
pdellaert Oct 15, 2024
2df024c
Adding CHANGELOG entry
pdellaert Oct 15, 2024
23ce2f6
Disabling autocreate for unnecessary collections
pdellaert Oct 16, 2024
72fa602
Fixing bug where initial permissions were not properly set
pdellaert Oct 16, 2024
035b415
Fixing issues:
pdellaert Oct 16, 2024
26973ba
Fixing maximum number of callback items
pdellaert Oct 16, 2024
d3e0165
Fix if there's no content at all
pdellaert Oct 16, 2024
98eabc8
Fix boolean handling
pdellaert Oct 16, 2024
8076f0b
Fix: Cache update with additional caches and proper mod output
pdellaert Oct 17, 2024
dc11d6e
Fix: Improvements based on review feedback
pdellaert Oct 17, 2024
f00ed5c
Fix: review issues and bug on missing max number of choices
pdellaert Oct 17, 2024
573862d
Change order of cache update to be in line with command
pdellaert Oct 17, 2024
5bfc280
Fix: Fixing a potential race condition to set the clicked immediately
pdellaert Oct 17, 2024
1b771fb
Fix: Actually fixes the race condition properly by checking for the r…
pdellaert Oct 17, 2024
539eae2
Disabling buttons if it is a fallback to GENERIC version
pdellaert Oct 17, 2024
0b2119b
Fix bugs - crash for unexisting version in setContext and missing db …
pdellaert Oct 20, 2024
f26c5ae
Fix bugs:
pdellaert Oct 20, 2024
ffd3888
Fix bug: Add name and aliases uniqueness
pdellaert Oct 20, 2024
551b7eb
Fix bugs in Content management
pdellaert Oct 20, 2024
556caee
Fix: Checking for empty content
pdellaert Oct 20, 2024
1ddc66e
Fix verifying generic does not get created/used for versions or comma…
pdellaert Oct 20, 2024
d07d7be
Fix bug where if no content for enabled versions was found it could c…
pdellaert Oct 20, 2024
b650b38
Clarifying button
pdellaert Oct 20, 2024
f3893d0
Fix modchannel checks
pdellaert Oct 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ HEARTBEAT_INTERVAL=300

# Set the interval in seconds for the birthday handler to be run, 0 to disable
BIRTHDAY_INTERVAL=1800

# Set the interval in seconds for the in memory cache to be refreshed, 1800 is the default
CACHE_REFRESH_INTERVAL=1800
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Update <small>_ October 2024</small>

- feat: Prefix Command Management (23/10/2024)
- fix: role assignment typo for server announcements (22/10/2024)

Update <small>_ August 2024</small>
Expand Down
2 changes: 2 additions & 0 deletions config/production.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"1163130801131634868",
"1021464464928809022"
],
"prefixCommandPrefix": ".",
"prefixCommandPermissionDelay": 10000,
"roleAssignmentIds": [
{
"group": "interestedIn",
Expand Down
2 changes: 2 additions & 0 deletions config/staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"1163130801131634868",
"1021464464928809022"
],
"prefixCommandPrefix": ".",
"prefixCommandPermissionDelay": 10000,
"roleAssignmentIds": [
{
"group": "interestedIn",
Expand Down
521 changes: 521 additions & 0 deletions docs/prefix-commands.md

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@hokify/agenda": "^6.0.0",
"@octokit/request": "^8.1.1",
"bad-words": "^3.0.4",
"cache-manager": "^5.7.6",
"config": "^3.3.9",
"discord.js": "^14.11.0",
"jsdom": "^23.2.0",
Expand Down
8 changes: 8 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ import commandTable from './moderation/commandTable';
import listRoleUsers from './moderation/listRoleUsers';
import clearMessages from './moderation/clearMessages';
import locate from './utils/locate/locate';
import prefixCommands from './moderation/prefixCommands/prefixCommands';
import prefixCommandPermissions from './moderation/prefixCommands/prefixCommandPermissions';
import prefixCommandCacheUpdate from './moderation/prefixCommands/prefixCommandCacheUpdate';
import prefixHelp from './utils/prefixHelp';

const commandArray: SlashCommand[] = [
ping,
Expand Down Expand Up @@ -65,6 +69,10 @@ const commandArray: SlashCommand[] = [
listRoleUsers,
clearMessages,
locate,
prefixCommands,
prefixCommandPermissions,
prefixCommandCacheUpdate,
prefixHelp,
];

export default commandArray;
97 changes: 97 additions & 0 deletions src/commands/moderation/prefixCommands/functions/addCategory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { ChatInputCommandInteraction, Colors, TextChannel, User } from 'discord.js';
import { constantsConfig, getConn, PrefixCommandCategory, Logger, makeEmbed, loadSinglePrefixCommandCategoryToCache } from '../../../../lib';

const noConnEmbed = makeEmbed({
title: 'Prefix Commands - Add Category - No Connection',
description: 'Could not connect to the database. Unable to add the prefix command category.',
color: Colors.Red,
});

const failedEmbed = (category: string) => makeEmbed({
title: 'Prefix Commands - Add Category - Failed',
description: `Failed to add the prefix command category ${category}.`,
color: Colors.Red,
});

const alreadyExistsEmbed = (category: string) => makeEmbed({
title: 'Prefix Commands - Add Category - Already exists',
description: `The prefix command category ${category} already exists. Not adding again.`,
color: Colors.Red,
});

const successEmbed = (category: string) => makeEmbed({
title: `Prefix command category ${category} was added successfully.`,
color: Colors.Green,
});

const modLogEmbed = (moderator: User, category: string, emoji: string, categoryId: string) => makeEmbed({
title: 'Prefix command category added',
fields: [
{
name: 'Category',
value: category,
},
{
name: 'Moderator',
value: `${moderator}`,
},
{
name: 'Emoji',
value: emoji,
},
],
footer: { text: `Category ID: ${categoryId}` },
color: Colors.Green,
});

const noModLogs = makeEmbed({
title: 'Prefix Commands - Add Category - No Mod Log',
description: 'I can\'t find the mod logs channel. Please check the channel still exists.',
color: Colors.Red,
});

export async function handleAddPrefixCommandCategory(interaction: ChatInputCommandInteraction<'cached'>) {
await interaction.deferReply({ ephemeral: true });

const conn = getConn();
if (!conn) {
await interaction.followUp({ embeds: [noConnEmbed], ephemeral: true });
return;
}

const name = interaction.options.getString('name')!;
const emoji = interaction.options.getString('emoji') || '';
const moderator = interaction.user;

//Check if the mod logs channel exists
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
Comment on lines +67 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
let modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS);
if (!modLogsChannel || !modLogsChannel.isTextBased()) {
modLogsChannel = null;

Might be safer to actually check if the channel is a text channel? Especially if we want to offer the codebase to other projects. Then, if it's not a text channel set it to null to prevent accidental attempts to send into a non-text channel.

await interaction.followUp({ embeds: [noModLogs], ephemeral: true });
}

const existingCategory = await PrefixCommandCategory.findOne({ name });

if (!existingCategory) {
const prefixCommandCategory = new PrefixCommandCategory({
name,
emoji,
});
try {
await prefixCommandCategory.save();
await loadSinglePrefixCommandCategoryToCache(prefixCommandCategory);
await interaction.followUp({ embeds: [successEmbed(name)], ephemeral: true });
if (modLogsChannel) {
try {
await modLogsChannel.send({ embeds: [modLogEmbed(moderator, name, emoji, prefixCommandCategory.id)] });
} catch (error) {
Logger.error(`Failed to post a message to the mod logs channel: ${error}`);
}
}
} catch (error) {
Logger.error(`Failed to add a prefix command category ${name}: ${error}`);
await interaction.followUp({ embeds: [failedEmbed(name)], ephemeral: true });
}
} else {
await interaction.followUp({ embeds: [alreadyExistsEmbed(name)], ephemeral: true });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { ChatInputCommandInteraction, Colors, TextChannel, User } from 'discord.js';
import { constantsConfig, getConn, PrefixCommand, Logger, makeEmbed, refreshSinglePrefixCommandCache } from '../../../../lib';

const noConnEmbed = makeEmbed({
title: 'Prefix Commands - Add Channel - No Connection',
description: 'Could not connect to the database. Unable to add the prefix command channel.',
color: Colors.Red,
});

const noCommandEmbed = (command: string) => makeEmbed({
title: 'Prefix Commands - Add Channel - No Command',
description: `Failed to add the prefix command channel for command ${command} as the command does not exist or there are more than one matching.`,
color: Colors.Red,
});

const failedEmbed = (command: string, channel: string) => makeEmbed({
title: 'Prefix Commands - Add Channel - Failed',
description: `Failed to add the prefix command channel <#${channel}> for command ${command}.`,
color: Colors.Red,
});

const alreadyExistsEmbed = (command: string, channel: string) => makeEmbed({
title: 'Prefix Commands - Add Channel - Already exists',
description: `A prefix command channel <#${channel}> for command ${command} already exists. Not adding again.`,
color: Colors.Red,
});

const successEmbed = (command: string, channel: string) => makeEmbed({
title: `Prefix command channel <#${channel}> added for command ${command}.`,
color: Colors.Green,
});

const modLogEmbed = (moderator: User, command: string, channel: string) => makeEmbed({
title: 'Add prefix command channel permission',
fields: [
{
name: 'Command',
value: command,
},
{
name: 'Channel',
value: `<#${channel}>`,
},
{
name: 'Moderator',
value: `${moderator}`,
},
],
color: Colors.Green,
});

const noModLogs = makeEmbed({
title: 'Prefix Commands - Add Channel - No Mod Log',
description: 'I can\'t find the mod logs channel. Please check the channel still exists.',
color: Colors.Red,
});

export async function handleAddPrefixCommandChannelPermission(interaction: ChatInputCommandInteraction<'cached'>) {
await interaction.deferReply({ ephemeral: true });

const conn = getConn();
if (!conn) {
await interaction.followUp({ embeds: [noConnEmbed], ephemeral: true });
return;
}

const command = interaction.options.getString('command')!;
const channel = interaction.options.getChannel('channel')!;
const moderator = interaction.user;

//Check if the mod logs channel exists
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
Comment on lines +72 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS) as TextChannel;
if (!modLogsChannel) {
let modLogsChannel = interaction.guild.channels.resolve(constantsConfig.channels.MOD_LOGS);
if (!modLogsChannel || !modLogsChannel.isTextBased()) {
modLogsChannel = null;

Might be safer to actually check if the channel is a text channel? Especially if we want to offer the codebase to other projects. Then, if it's not a text channel set it to null to prevent accidental attempts to send into a non-text channel.

await interaction.followUp({ embeds: [noModLogs], ephemeral: true });
}

let foundCommands = await PrefixCommand.find({ name: command });
if (!foundCommands || foundCommands.length > 1) {
foundCommands = await PrefixCommand.find({ aliases: { $in: [command] } });
}
if (!foundCommands || foundCommands.length > 1) {
await interaction.followUp({ embeds: [noCommandEmbed(command)], ephemeral: true });
return;
}
const [foundCommand] = foundCommands;
const { id: channelId } = channel;

const existingChannelPermission = foundCommand.permissions.channels?.includes(channelId);
if (!existingChannelPermission) {
if (!foundCommand.permissions.channels) {
foundCommand.permissions.channels = [];
}
foundCommand.permissions.channels.push(channelId);
try {
await foundCommand.save();
await refreshSinglePrefixCommandCache(foundCommand, foundCommand);
await interaction.followUp({ embeds: [successEmbed(command, channelId)], ephemeral: true });
if (modLogsChannel) {
try {
await modLogsChannel.send({ embeds: [modLogEmbed(moderator, command, channelId)] });
} catch (error) {
Logger.error(`Failed to post a message to the mod logs channel: ${error}`);
}
}
} catch (error) {
Logger.error(`Failed to add prefix command channel <#${channel}> for command ${command}: ${error}`);
await interaction.followUp({ embeds: [failedEmbed(command, channelId)], ephemeral: true });
}
} else {
await interaction.followUp({ embeds: [alreadyExistsEmbed(command, channelId)], ephemeral: true });
}
}
Loading
Loading