Skip to content

Commit

Permalink
refactor folder structure (#236)
Browse files Browse the repository at this point in the history
* refactor: add types to build referral list

* refactor: move commands into slash and context menu command folders

* refactor: rename date utils to utils/date

* docs: update README about commands section
  • Loading branch information
samhwang committed Apr 13, 2024
1 parent ace03af commit 3520ae7
Show file tree
Hide file tree
Showing 91 changed files with 159 additions and 153 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,27 @@ pnpm run prisma:gen
### DB GUI

```bash
pnpm prisma:studio
pnpm run prisma:studio
```

### Deploying your commands to a test Discord Server

- Please make sure you have filled out your `GUILD_ID`, `TOKEN` and `CLIENT_ID`
in the `.env` file.
- Add your commands into the `src/command/index.ts` file like so.
- Add your commands into the `src/slash-commands/index.ts` & `src/context-menu-commands/index.ts` file like so.

```ts
import yourCommand from './yourCommand';
// File: src/slash-commands/index.ts
import yourCommand from './your-command';

export const commandList: Command[] = [yourCommand];
export const commandList: SlashCommand[] = [yourCommand];
```

```ts
// File: src/context-menu-commands/index.ts
import yourCommand from './your-command';

export const commandList: ContextMenuCommand[] = [yourCommand]
```

- Run the `deploy:command` command.
Expand Down Expand Up @@ -182,6 +190,7 @@ pnpm run deploy:command
### Running lints and tests

```bash
pnpm run lint
pnpm run format
pnpm run test
```
Expand Down
2 changes: 1 addition & 1 deletion bin/autobump.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ThreadChannel } from 'discord.js';
import { Result } from 'oxide.ts';
import { getDiscordClient } from '../src/clients';
import { listAllThreads } from '../src/commands/autobump-threads/utils';
import { listAllThreads } from '../src/slash-commands/autobump-threads/utils';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';

Expand Down
4 changes: 2 additions & 2 deletions bin/broadcast-reminder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ChannelType } from 'discord.js';
import { Result } from 'oxide.ts';
import { getDiscordClient } from '../src/clients';
import { formatReminderMessage, getReminderByTime, removeReminders } from '../src/commands/reminder/utils';
import { getReminderChannel } from '../src/commands/serverSettings/utils';
import { formatReminderMessage, getReminderByTime, removeReminders } from '../src/slash-commands/reminder/utils';
import { getReminderChannel } from '../src/slash-commands/server-settings/utils';
import { getCurrentUnixTime } from '../src/utils/date';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';
Expand Down
2 changes: 1 addition & 1 deletion bin/cleanup-expired-referrals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Result } from 'oxide.ts';
import { cleanupExpiredCode } from '../src/commands/referral/cleanupExpiredCode';
import { cleanupExpiredCode } from '../src/slash-commands/referral/utils';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';

Expand Down
12 changes: 7 additions & 5 deletions bin/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { InteractionType } from 'discord-api-types/v10';
import { Result } from 'oxide.ts';
import { getDiscordClient } from '../src/clients';
import { commandList, contextMenuCommandList } from '../src/commands';
import { deployGlobalCommands } from '../src/commands/deploy-command';
import { getConfigs } from '../src/config';
import { commands as contextMenuCommandList } from '../src/context-menu-commands';
import { deployGlobalCommands } from '../src/deploy-command';
import { commands as slashCommandList } from '../src/slash-commands';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';
import { processMessage } from '../src/utils/message-processor';
Expand All @@ -21,8 +22,9 @@ const main = async () => {
// This should only be run once during the bot startup in production.
// For development usage, please use `pnpm deploy:command`
logger.info('[main]: Deploying global commands');
const commands = [...slashCommandList, ...contextMenuCommandList];
const op = await Result.safe(
deployGlobalCommands([...commandList, ...contextMenuCommandList], {
deployGlobalCommands(commands, {
token,
clientId: client.user.id,
})
Expand All @@ -45,7 +47,7 @@ const main = async () => {
if (isCommand) {
const { commandName } = interaction;
logger.info(`[main]: RECEIVED COMMAND. COMMAND: ${commandName}`);
const command = commandList.find((cmd) => cmd.data.name === commandName);
const command = slashCommandList.find((cmd) => cmd.data.name === commandName);
return await command?.execute(interaction);
}

Expand All @@ -61,7 +63,7 @@ const main = async () => {
if (isAutocomplete) {
const { commandName } = interaction;
logger.info(`[main]: RECEIVED AUTOCOMPLETE. COMMAND: ${commandName}`);
const command = commandList.find((cmd) => cmd.data.name === commandName);
const command = slashCommandList.find((cmd) => cmd.data.name === commandName);
return await command?.autocomplete?.(interaction);
}
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 160,
"ignore": ["node_modules/**", "./src/commands/referral/generated"]
"ignore": ["node_modules/**", "./src/slash-commands/referral/generated"]
},
"javascript": {
"formatter": {
Expand Down
5 changes: 3 additions & 2 deletions scripts/build-referral-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import wretch from 'wretch';
import { logger } from '../src/utils/logger';

const ozbargainApi = wretch('https://www.ozbargain.com.au/wiki/list_of_referral_links');
const OUTPUT_DIR = path.join(__dirname, '..', 'src', 'commands', 'referral', 'generated');
const referralModuleDir = path.join(__dirname, '..', 'src', 'slash-commands', 'referral');
const OUTPUT_DIR = path.join(referralModuleDir, 'generated');

const getOzbReferralNodes = async () => {
const getOzbReferralNodes = async (): Promise<HTMLElement[]> => {
logger.info('[get-ozbargain-referral-nodes]: Fetching Ozbargain referral list');
const rawHtml = await ozbargainApi.get().text();
const htmlTree = parseHtml(rawHtml);
Expand Down
2 changes: 1 addition & 1 deletion scripts/delete-global-commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Result } from 'oxide.ts';
import { deployGlobalCommands } from '../src/commands/deploy-command';
import { deployGlobalCommands } from '../src/deploy-command';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';

Expand Down
2 changes: 1 addition & 1 deletion scripts/delete-guild-commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Result } from 'oxide.ts';
import { deployGuildCommands } from '../src/commands/deploy-command';
import { deployGuildCommands } from '../src/deploy-command';
import { getCurrentUnixTime } from '../src/utils/date';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';
Expand Down
8 changes: 5 additions & 3 deletions scripts/deploy-guild-commands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Result } from 'oxide.ts';
import { commandList, contextMenuCommandList } from '../src/commands';
import { deployGuildCommands } from '../src/commands/deploy-command';
import { commands as slashCommandList } from '../src/slash-commands';
import { commands as contextMenuCommandList } from '../src/context-menu-commands';
import { deployGuildCommands } from '../src/deploy-command';
import { getCurrentUnixTime } from '../src/utils/date';
import { loadEnv } from '../src/utils/load-env';
import { logger } from '../src/utils/logger';
Expand All @@ -16,8 +17,9 @@ const deploy = async () => {
}

logger.info('[deploy-guild-commands]: Deploying guild commands');
const commands = [...slashCommandList, ...contextMenuCommandList];
const op = await Result.safe(
deployGuildCommands([...commandList, ...contextMenuCommandList,], {
deployGuildCommands(commands, {
token,
clientId,
guildId,
Expand Down
13 changes: 0 additions & 13 deletions src/commands/referral/cleanupExpiredCode.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { thankUserInMessage } from './commands';
import { thankUserInMessage } from './slash-commands/reputation/give-reputation';
import type { CommandConfig } from './utils/message-processor';

export const getConfigs = (): CommandConfig => {
Expand Down
8 changes: 8 additions & 0 deletions src/context-menu-commands/builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ContextMenuCommandBuilder, ContextMenuCommandInteraction } from 'discord.js';

export type ContextMenuCommandInteractionHandler = (interaction: ContextMenuCommandInteraction) => Promise<void>;

export interface ContextMenuCommand {
data: ContextMenuCommandBuilder;
execute: ContextMenuCommandInteractionHandler;
}
4 changes: 4 additions & 0 deletions src/context-menu-commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { ContextMenuCommand } from './builder';
import pinMessageCommand from './pin-message';

export const commands: ContextMenuCommand[] = [pinMessageCommand];
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApplicationCommandType, ContextMenuCommandBuilder, type ContextMenuCommandInteraction } from 'discord.js';
import { logger } from '../../../utils/logger';
import type { ContextMenuCommand } from '../../builder';
import { logger } from '../../utils/logger';
import type { ContextMenuCommand } from '../builder';

export const data = new ContextMenuCommandBuilder().setName('Pin').setType(ApplicationCommandType.Message);

Expand Down
7 changes: 4 additions & 3 deletions src/commands/deploy-command.ts → src/deploy-command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { REST, type RequestData, type RouteLike } from '@discordjs/rest';
import { Routes } from 'discord-api-types/v10';
import type { Command, ContextMenuCommand } from './builder';
import type { ContextMenuCommand } from './context-menu-commands/builder';
import type { SlashCommand } from './slash-commands/builder';

interface DiscordRequestConfig {
token: string;
Expand All @@ -19,7 +20,7 @@ const registerCommands = async ({ request, token, body }: DiscordRequestPayload)
return rest.put(request, { body });
};

export const deployGuildCommands = async (commandList: Array<Command | ContextMenuCommand>, config: DiscordRequestConfig) => {
export const deployGuildCommands = async (commandList: Array<SlashCommand | ContextMenuCommand>, config: DiscordRequestConfig) => {
const { token, clientId, guildId } = config;

const commands = commandList.map((cmd) => cmd.data.toJSON());
Expand All @@ -28,7 +29,7 @@ export const deployGuildCommands = async (commandList: Array<Command | ContextMe
return registerCommands({ request, token, body: commands });
};

export const deployGlobalCommands = async (commandList: Array<Command | ContextMenuCommand>, config: Omit<DiscordRequestConfig, 'guildId'>) => {
export const deployGlobalCommands = async (commandList: Array<SlashCommand | ContextMenuCommand>, config: Omit<DiscordRequestConfig, 'guildId'>) => {
const { token, clientId } = config;

const commands = commandList.map((cmd) => cmd.data.toJSON());
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import { logger } from '../../utils/logger';
import { getRandomIntInclusive } from '../../utils/random';
import type { Command } from '../builder';
import type { SlashCommand } from '../builder';

const data = new SlashCommandBuilder()
.setName('8ball')
Expand Down Expand Up @@ -32,7 +32,7 @@ export const ask8Ball = async (interaction: ChatInputCommandInteraction) => {
await interaction.reply(`Q: ${question}\nA: ${reply}`);
};

const command: Command = {
const command: SlashCommand = {
data,
execute: ask8Ball,
};
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Result } from 'oxide.ts';
import { isBlank } from '../../utils/is-blank';
import { logger } from '../../utils/logger';
import { fetchLastMessageBeforeId } from '../../utils/message-fetcher';
import type { Command } from '../builder';
import type { SlashCommand } from '../builder';

const data = new SlashCommandBuilder()
.setName('allcap')
Expand Down Expand Up @@ -44,7 +44,7 @@ export const allCapExpandText = async (interaction: ChatInputCommandInteraction)
await interaction.reply(reply);
};

const command: Command = {
const command: SlashCommand = {
data,
execute: allCapExpandText,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { ChannelType, SlashCommandSubcommandBuilder } from 'discord.js';
import { Result } from 'oxide.ts';
import { logger } from '../../utils/logger';
import type { CommandHandler, Subcommand } from '../builder';
import type { SlashCommandHandler, Subcommand } from '../builder';
import { addAutobumpThread } from './utils';

const data = new SlashCommandSubcommandBuilder()
.setName('add')
.setDescription('Add thread to autobump list')
.addChannelOption((option) => option.setName('thread').setDescription('thread to be auto-bumped').setRequired(true));

export const addAutobumpThreadCommand: CommandHandler = async (interaction) => {
export const addAutobumpThreadCommand: SlashCommandHandler = async (interaction) => {
const guildId = interaction.guildId!;
const thread = interaction.options.getChannel('thread', true);
logger.info(`[add-autobump-thread]: Adding thread ${thread.id} to autobump list for guild ${guildId}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { type GuildMember, SlashCommandBuilder } from 'discord.js';
import { isAdmin, isModerator } from '../../utils/permission';
import addThread from '../autobump-threads/add-thread';
import listThreads from '../autobump-threads/list-threads';
import removeThread from '../autobump-threads/remove-thread';
import type { Command, CommandHandler } from '../builder';
import type { SlashCommand, SlashCommandHandler } from '../builder';
import addThread from './add-thread';
import listThreads from './list-threads';
import removeThread from './remove-thread';

const data = new SlashCommandBuilder()
.setName('autobump-threads')
Expand All @@ -14,7 +14,7 @@ const data = new SlashCommandBuilder()

const subcommands = [listThreads, addThread, removeThread];

const execute: CommandHandler = async (interaction) => {
const execute: SlashCommandHandler = async (interaction) => {
const member = interaction.member as GuildMember;
if (!isAdmin(member) && !isModerator(member)) {
await interaction.reply("You don't have enough permission to run this command.");
Expand All @@ -26,7 +26,7 @@ const execute: CommandHandler = async (interaction) => {
return subcommand?.execute(interaction);
};

const command: Command = {
const command: SlashCommand = {
data,
execute,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ServerChannelsSettings } from '@prisma/client';
import { SlashCommandSubcommandBuilder } from 'discord.js';
import { Result } from 'oxide.ts';
import { logger } from '../../utils/logger';
import type { CommandHandler, Subcommand } from '../builder';
import type { SlashCommandHandler, Subcommand } from '../builder';
import { listThreadsByGuild } from './utils';

const data = new SlashCommandSubcommandBuilder().setName('list').setDescription('Show list of autobump threads');
Expand All @@ -14,7 +14,7 @@ const buildThreadList = (threadIds: ServerChannelsSettings['autobumpThreads']) =
}, startText);
};

export const listAutobumpThreadsCommand: CommandHandler = async (interaction) => {
export const listAutobumpThreadsCommand: SlashCommandHandler = async (interaction) => {
const guildId = interaction.guildId!;
const threads = await Result.safe(listThreadsByGuild(guildId));
logger.info(`[list-autobump-threads]: Listing autobump threads for guild ${guildId}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { SlashCommandSubcommandBuilder } from 'discord.js';
import { Result } from 'oxide.ts';
import { logger } from '../../utils/logger';
import type { CommandHandler, Subcommand } from '../builder';
import type { SlashCommandHandler, Subcommand } from '../builder';
import { removeAutobumpThread } from './utils';

const data = new SlashCommandSubcommandBuilder()
.setName('remove')
.setDescription('Remove thread from autobump list')
.addChannelOption((option) => option.setName('thread').setDescription('thread not to be auto-bumped').setRequired(true));

export const removeAutobumpThreadCommand: CommandHandler = async (interaction) => {
export const removeAutobumpThreadCommand: SlashCommandHandler = async (interaction) => {
const guildId = interaction.guildId!;
const thread = interaction.options.getChannel('thread', true);
logger.info(`[remove-autobump-thread]: Removing thread ${thread.id} from autobump list for guild ${guildId}`);
Expand Down
File renamed without changes.
16 changes: 4 additions & 12 deletions src/commands/builder.ts → src/slash-commands/builder.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import type {
AutocompleteInteraction,
ChatInputCommandInteraction,
ContextMenuCommandBuilder,
ContextMenuCommandInteraction,
SlashCommandBuilder,
SlashCommandSubcommandBuilder,
SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js';

export type CommandHandler = (interaction: ChatInputCommandInteraction) => Promise<void>;
export type SlashCommandHandler = (interaction: ChatInputCommandInteraction) => Promise<void>;
export type AutocompleteHandler = (autocomplete: AutocompleteInteraction) => Promise<void>;
export type ContextMenuCommandInteractionHandler = (interaction: ContextMenuCommandInteraction) => Promise<void>;

export interface Command {
export interface SlashCommand {
data: Omit<SlashCommandBuilder, 'addSubcommandGroup' | 'addSubcommand'> | SlashCommandSubcommandsOnlyBuilder;
execute: CommandHandler;
execute: SlashCommandHandler;
autocomplete?: AutocompleteHandler;
}

export interface Subcommand {
data: SlashCommandSubcommandBuilder | ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder);
execute: CommandHandler;
}

export interface ContextMenuCommand {
data: ContextMenuCommandBuilder;
execute: ContextMenuCommandInteractionHandler;
execute: SlashCommandHandler;
}
File renamed without changes.
Loading

0 comments on commit 3520ae7

Please sign in to comment.