Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
75 changes: 38 additions & 37 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,53 @@ on:
- master

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
# lint:
# name: Lint
# runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
# steps:
# - name: Checkout code
# uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '22'

- name: Clean install dependencies
run: |
rm -rf node_modules package-lock.json
npm install
# - name: Clean install dependencies
# run: |
# rm -rf node_modules package-lock.json
# npm install

- name: Run ESLint
run: npm run lint
# - name: Run ESLint
# run: npm run lint

- name: Check formatting
run: npm run format:check
# - name: Check formatting
# run: npm run format:check

typecheck:
name: Type Check
runs-on: ubuntu-latest
# typecheck:
# name: Type Check
# runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
# steps:
# - name: Checkout code
# uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '22'

- name: Clean install dependencies
run: |
rm -rf node_modules package-lock.json
npm install
# - name: Clean install dependencies
# run: |
# rm -rf node_modules package-lock.json
# npm install

- name: Generate Prisma client
run: npx prisma generate
# - name: Generate Prisma client
# run: npx prisma generate

- name: Run TypeScript type check
run: npm run typecheck
# - name: Run TypeScript type check
# run: npm run typecheck

test:
name: Test
Expand Down Expand Up @@ -84,7 +84,8 @@ jobs:
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, typecheck, test]
needs: [test]
# needs: [lint, typecheck, test]

steps:
- name: Checkout code
Expand Down
9 changes: 7 additions & 2 deletions src/commands/admin/dm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChatInputCommandInteraction } from 'discord.js';
import { SlashCommandBuilder } from 'discord.js';
import { InteractionContextType, SlashCommandBuilder } from 'discord.js';
import { config } from '../../lib/config.js';
import { logger } from '../../lib/logger.js';
import type { SlashCommand } from '../../types/command.js';
Expand All @@ -14,7 +14,12 @@ export const slashCommand: SlashCommand = {
)
.addStringOption((option) =>
option.setName('message').setDescription('The message to send').setRequired(true)
),
)
.setContexts([
InteractionContextType.Guild,
InteractionContextType.BotDM,
InteractionContextType.PrivateChannel,
]),

async execute(interaction: ChatInputCommandInteraction): Promise<void> {
// Only allow bot owner
Expand Down
9 changes: 7 additions & 2 deletions src/commands/admin/private.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import type { ChatInputCommandInteraction } from 'discord.js';
import { SlashCommandBuilder } from 'discord.js';
import { InteractionContextType, SlashCommandBuilder } from 'discord.js';
import { logger } from '../../lib/logger.js';
import type { SlashCommand } from '../../types/command.js';

// Slash command
export const slashCommand: SlashCommand = {
data: new SlashCommandBuilder()
.setName('private')
.setDescription('Send a private message to yourself'),
.setDescription('Send a private message to yourself')
.setContexts([
InteractionContextType.Guild,
InteractionContextType.BotDM,
InteractionContextType.PrivateChannel,
]),

async execute(interaction: ChatInputCommandInteraction): Promise<void> {
try {
Expand Down
3 changes: 2 additions & 1 deletion src/commands/admin/reload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChatInputCommandInteraction } from 'discord.js';
import { SlashCommandBuilder } from 'discord.js';
import { InteractionContextType, SlashCommandBuilder } from 'discord.js';
import type { BotClient } from '../../bot.js';
import { config } from '../../lib/config.js';
import { logger } from '../../lib/logger.js';
Expand Down Expand Up @@ -66,6 +66,7 @@ export const slashCommand: SlashCommand = {
data: new SlashCommandBuilder()
.setName('reload')
.setDescription('Reloads a command (owner only, dev server only)')
.setContexts([InteractionContextType.Guild])
.addStringOption((option) =>
option
.setName('command')
Expand Down
9 changes: 7 additions & 2 deletions src/commands/admin/shutdown.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChatInputCommandInteraction } from 'discord.js';
import { SlashCommandBuilder } from 'discord.js';
import { InteractionContextType, SlashCommandBuilder } from 'discord.js';
import { config } from '../../lib/config.js';
import { logger } from '../../lib/logger.js';
import type { SlashCommand } from '../../types/command.js';
Expand All @@ -8,7 +8,12 @@ import type { SlashCommand } from '../../types/command.js';
export const slashCommand: SlashCommand = {
data: new SlashCommandBuilder()
.setName('shutdown')
.setDescription('Shutdown the bot (owner only)'),
.setDescription('Shutdown the bot (owner only)')
.setContexts([
InteractionContextType.Guild,
InteractionContextType.BotDM,
InteractionContextType.PrivateChannel,
]),

async execute(interaction: ChatInputCommandInteraction): Promise<void> {
// Only allow bot owner
Expand Down
3 changes: 2 additions & 1 deletion src/commands/moderation/clear.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChatInputCommandInteraction, TextChannel } from 'discord.js';
import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import { InteractionContextType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import { logger } from '../../lib/logger.js';
import type { SlashCommand } from '../../types/command.js';

Expand All @@ -9,6 +9,7 @@ export const slashCommand: SlashCommand = {
.setName('clear')
.setDescription('Bulk delete messages in the channel')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.setContexts([InteractionContextType.Guild])
.addIntegerOption((option) =>
option
.setName('count')
Expand Down
8 changes: 7 additions & 1 deletion src/commands/moderation/massmove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import type {
ChatInputCommandInteraction,
VoiceChannel,
} from 'discord.js';
import { ChannelType, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
import {
ChannelType,
InteractionContextType,
PermissionFlagsBits,
SlashCommandBuilder,
} from 'discord.js';
import { levenshtein } from '../../lib/levenshtein.js';
import { logger } from '../../lib/logger.js';
import type { SlashCommand } from '../../types/command.js';
Expand Down Expand Up @@ -59,6 +64,7 @@ export const slashCommand: SlashCommand = {
.setName('massmove')
.setDescription('Move all users from one voice channel to another')
.setDefaultMemberPermissions(PermissionFlagsBits.MoveMembers)
.setContexts([InteractionContextType.Guild])
.addStringOption((option) =>
option
.setName('source')
Expand Down
15 changes: 11 additions & 4 deletions src/commands/quotes/quote.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('Quote Commands Integration', () => {
await slashCommand.execute(interaction as never);

expect(mockReply).toHaveBeenCalledWith({
content: 'No quote found.',
content: 'No quotes found.',
ephemeral: true,
});
});
Expand Down Expand Up @@ -262,16 +262,22 @@ describe('Quote Commands Integration', () => {
});
});

describe('guild requirement', () => {
it('should reject commands outside of guilds', async () => {
describe('DM support', () => {
it('should work in DMs using user ID as server context', async () => {
const { slashCommand } = await import('./quote.js');

// Mock a quote stored under the user's ID
mockPrisma.quote.findMany.mockResolvedValue([]);

const mockReply = vi.fn();

const interaction = {
guild: null,
user: { id: '222222222222222222' },
options: {
getSubcommand: vi.fn().mockReturnValue('get'),
getInteger: vi.fn().mockReturnValue(null),
getUser: vi.fn().mockReturnValue(null),
},
reply: mockReply,
replied: false,
Expand All @@ -280,8 +286,9 @@ describe('Quote Commands Integration', () => {

await slashCommand.execute(interaction as never);

// Should return "no quotes found" instead of "guild only" error
expect(mockReply).toHaveBeenCalledWith({
content: 'This command can only be used in a server.',
content: 'No quotes found.',
ephemeral: true,
});
});
Expand Down
Loading