Skip to content
Open
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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: CI
# Lints, builds, tests, and checks versions of the project when a pull request is opened or merged
# Lints, type-checks, tests, and checks versions of the project when a pull request is opened or merged

on:
pull_request:
Expand Down Expand Up @@ -41,8 +41,8 @@ jobs:
- name: Lint
run: npm run lint

build:
name: Build
type-check:
name: Type check

runs-on: ubuntu-latest

Expand All @@ -68,8 +68,8 @@ jobs:
- name: Generate version
run: npm run export-version

- name: Build
run: npm run build
- name: Type check
run: npm run type-check

test:
name: Test
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ logs
# Generated files
node_modules
db
dist
coverage
src/constants/version.ts
tmp
Expand Down
5 changes: 2 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"js/ts.tsdk.path": "node_modules/typescript/lib",
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"git.branchProtection": ["main"],
"todo-tree.filtering.excludeGlobs": ["**/dist/**", "**/node_modules/**", "coverage/**"],
"todo-tree.filtering.excludeGlobs": ["**/node_modules/**", "coverage/**"],
"files.eol": "auto",
"terminal.integrated.defaultProfile.windows": "Git Bash",
"editor.formatOnSave": true,
"eslint.useFlatConfig": true,
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": "**/workflows/*.yml"
}
Expand Down
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update dependencies
- Modernize TypeScript and ESLint configurations
- Refactor GitHub workflows
- **Breaking:** set minimum Node version to 22.18.0
- **Breaking:** set minimum Node version to `^22.18.0 || >=24.0.0`
- **Breaking:** upgrade to Node 24
- Replace `nodemon` with `node --watch` for the `npm run dev` script
- Use `clientReady` event instead of `ready` to fix deprecation warning
- Use `Events` enum for event handler names
- Replace `tsx` with native Node for `release.ts` script
- Replace `jiti` with native Node for loading `eslint.config.ts`
- Replace `nodemon` with `node --watch` for the `npm run dev` script
- Replace `tsx` with native Node.js for `release.ts` script
- Replace `jiti` with native Node.js for loading `eslint.config.ts`
- Replace build step with native Node.js for running the bot

### Removed

Expand Down
20 changes: 9 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
FROM node:24-slim as builder

RUN apt-get update -y
RUN apt-get install -y openssl
FROM node:24-slim AS builder

WORKDIR /app

COPY . .

RUN npm ci
RUN npm run export-version
RUN npm run build --omit=dev

FROM node:24-slim as runner
FROM node:24-slim AS runner

ENV NODE_ENV=production

RUN apt-get update -y
Copy link
Copy Markdown
Contributor

@AverageHelper AverageHelper May 13, 2026

Choose a reason for hiding this comment

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

Not sure about removing this line. Shouldn't we update the package lists before trying to install packages?

RUN apt-get install -y openssl

WORKDIR /app

COPY --from=builder /app/dist/ ./dist/
COPY --from=builder /app/res/ ./res/
COPY --exclude=**/*.test.* --exclude=**/__mocks__/** src ./src/
COPY --from=builder /app/src/constants/version.ts ./src/constants/
COPY res ./res/
COPY package*.json ./
COPY prisma prisma/
COPY scripts/launch_in_docker.sh .
COPY scripts/launch_in_docker.sh ./scripts/

RUN npm ci --omit=dev

# Path internal to container; use `volumes` config to specify real system path of `/db/`
ENV DATABASE_URL="file:/db/db.sqlite"

# Using bash here to get consistent behavior and configurations
CMD ["bash", "/app/launch_in_docker.sh"]
CMD ["bash", "/app/scripts/launch_in_docker.sh"]
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ If you've read this far, and don't plan to run or develop on the bot yourself, o

### Prerequisites

This project requires [NodeJS](https://nodejs.org/) (version 22.12.0 or later) and [NPM](https://npmjs.org/). To make sure you have them available on your machine, try running the following command:
This project requires [NodeJS](https://nodejs.org/) and [NPM](https://npmjs.org/). To make sure you have them available on your machine, try running the following command:

```sh
$ node -v && npm -v
Expand Down Expand Up @@ -215,7 +215,7 @@ The docker script will take care of every part of the docker development process

### Build the bot

Be sure to install dependencies, run a quick lint to generate needed files, compile the source, and deploy commands. Here's a handy command to do all of that:
Be sure to install dependencies, generate necessary files, initialize the database, and deploy commands. Here's a handy command to do all of that:

```sh
$ npm run setup
Expand Down Expand Up @@ -270,8 +270,6 @@ If you have added new code, you should write new unit tests to cover all the cod
For development purposes:

```sh
$ node --env-file=.env .
# or
$ npm run dev
```

Expand Down
16 changes: 15 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import unicorn from 'eslint-plugin-unicorn';

export default defineConfig(
{ files: ['**/*.{m,c,}{js,ts}{x,}'] },
globalIgnores(['dist', 'coverage', 'src/constants/version.ts']),
globalIgnores(['coverage', 'src/constants/version.ts']),
{
linterOptions: {
reportUnusedDisableDirectives: 'error',
Expand Down Expand Up @@ -53,6 +53,20 @@ export default defineConfig(
},
},

// FIXME Test files cannot be type-checked (see tsconfig.json)
{
files: ['**/*.test.ts'],
linterOptions: {
reportUnusedDisableDirectives: 'off',
},
languageOptions: {
parserOptions: {
projectService: false,
},
},
extends: [typescriptConfigs.disableTypeChecked],
},

stylistic.configs['disable-legacy'],
stylistic.configs.customize({
braceStyle: '1tbs',
Expand Down
3 changes: 0 additions & 3 deletions nodemon.json

This file was deleted.

2 changes: 1 addition & 1 deletion package-lock.json

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

20 changes: 7 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
"license": "0BSD",
"author": "BYU CS",
"type": "module",
"main": "./dist/main.js",
"main": "src/main.ts",
"scripts": {
"build": "npm run db:generate && rm -rf dist && ./node_modules/.bin/tsc -p tsconfig.build.json",
"commands:deploy": "node --env-file=.env . --deploy # TODO: Replace these with automatic command deployment",
"commands:revoke": "node --env-file=.env . --revoke",
"db:generate": "./node_modules/.bin/prisma generate --no-hints --schema ./prisma/schema.prisma",
Expand All @@ -30,7 +29,7 @@
"db:introspect": "./node_modules/.bin/prisma db pull",
"db:migrate": "./node_modules/.bin/prisma migrate deploy",
"db:migrations:generate": "./node_modules/.bin/prisma migrate dev --create-only",
"dev": "NODE_ENV=development node --watch --env-file=.env ./dist/main.js",
"dev": "NODE_ENV=development node --watch --env-file=.env .",
"docker": "npm run docker:image && npm run docker:cleanup && npm run docker:container",
"docker:cleanup": "docker system prune --force --filter='label=cs-bot'",
"docker:container": "docker run --tty --interactive --rm --mount type=bind,source=$PWD,destination=/cs-bot cs-bot-image",
Expand All @@ -39,9 +38,10 @@
"lint": "./node_modules/.bin/eslint --flag unstable_native_nodejs_ts_config",
"lint:fix": "npm run lint -- --fix",
"release": "node ./scripts/release.ts",
"setup": "npm ci && npm run export-version && npm run db:migrate && npm run build --omit=dev && npm run commands:deploy",
"start": "node --env-file=.env ./dist/main.js",
"test": "./node_modules/.bin/vitest"
"setup": "npm ci && npm run export-version && npm run db:migrate && npm run db:generate && npm run commands:deploy",
"start": "node --env-file=.env .",
"test": "./node_modules/.bin/vitest",
"type-check": "./node_modules/.bin/tsc --noEmit"
},
"dependencies": {
"@discordjs/voice": "0.19.2",
Expand Down Expand Up @@ -80,12 +80,6 @@
"vitest-mock-extended": "4.0.0"
},
"engines": {
"node": ">=22.18.0"
},
"devEngines": {
"runtime": {
"name": "node",
"version": ">=22.18.0"
}
"node": "^22.18.0 || >=24.0.0"
}
}
2 changes: 1 addition & 1 deletion src/__mocks__/logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { vi } from 'vitest';

import type * as logger from '../logger.js';
import type * as logger from '../logger.ts';

/**
* The mock function serving as the `debug` method of the logger.
Expand Down
2 changes: 1 addition & 1 deletion src/buttons/hangmanLessButton.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, beforeEach, describe, expect, test, vi } from 'vitest';

import { hangmanLessButton } from './hangmanLessButton.js';
import { hangmanLessButton } from './hangmanLessButton.ts';

describe('hangmanLessButton', () => {
const mockUpdate = vi.fn<ButtonContext['interaction']['update']>();
Expand Down
2 changes: 1 addition & 1 deletion src/buttons/hangmanLessButton.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ButtonBuilder } from '@discordjs/builders';
import { ButtonStyle } from 'discord.js';

import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.js';
import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.ts';

const customId = 'hangmanLessButton';
export const hangmanLessButton: Button = {
Expand Down
4 changes: 2 additions & 2 deletions src/buttons/hangmanLetterButton.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';

import { letterButtons } from '../evilHangman/hangmanLetterButtons.js';
import { UserMessageError } from '../helpers/UserMessageError.js';
import { letterButtons } from '../evilHangman/hangmanLetterButtons.ts';
import { UserMessageError } from '../helpers/UserMessageError.ts';

describe('hangmanMoreButton', () => {
const mockUpdate = vi.fn<ButtonContext['interaction']['update']>();
Expand Down
6 changes: 3 additions & 3 deletions src/buttons/hangmanLetterButton.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ButtonBuilder, ButtonStyle } from 'discord.js';

import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.js';
import { UserMessageError } from '../helpers/UserMessageError.js';
import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.ts';
import { UserMessageError } from '../helpers/UserMessageError.ts';

export type Letter =
| 'a'
Expand Down Expand Up @@ -36,7 +36,7 @@ export function hangmanLetterButton(letter: Letter): Button {
return {
customId,
async execute({ interaction, message }): Promise<void> {
const { buildEvilHangmanMessage } = await import('../evilHangman/evilHangmanMessage.js');
const { buildEvilHangmanMessage } = await import('../evilHangman/evilHangmanMessage.ts');
const game = parseEvilHangmanMessage(message);

const guessErrorMessage = game.checkGuess(letter);
Expand Down
2 changes: 1 addition & 1 deletion src/buttons/hangmanMoreButton.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, beforeEach, describe, expect, test, vi } from 'vitest';

import { hangmanMoreButton } from './hangmanMoreButton.js';
import { hangmanMoreButton } from './hangmanMoreButton.ts';

describe('hangmanMoreButton', () => {
const mockUpdate = vi.fn<ButtonContext['interaction']['update']>();
Expand Down
4 changes: 2 additions & 2 deletions src/buttons/hangmanMoreButton.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ButtonBuilder } from '@discordjs/builders';
import { ButtonStyle } from 'discord.js';

import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.js';
import { parseEvilHangmanMessage } from '../evilHangman/parseEvilHangmanMessage.ts';

const customId = 'hangmanMoreButton';
export const hangmanMoreButton: Button = {
customId,
async execute({ message, interaction }): Promise<void> {
const { buildEvilHangmanMessage } = await import('../evilHangman/evilHangmanMessage.js');
const { buildEvilHangmanMessage } = await import('../evilHangman/evilHangmanMessage.ts');
const game = parseEvilHangmanMessage(message);

const displayInfo = game.getDisplayInfo();
Expand Down
2 changes: 1 addition & 1 deletion src/buttons/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest';

import { allButtons, _add } from './index.js';
import { allButtons, _add } from './index.ts';

describe('allButtons', () => {
test('index is not empty', () => {
Expand Down
6 changes: 3 additions & 3 deletions src/buttons/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { letterButtons } from '../evilHangman/hangmanLetterButtons.js';
import { hangmanLessButton } from './hangmanLessButton.js';
import { hangmanMoreButton } from './hangmanMoreButton.js';
import { letterButtons } from '../evilHangman/hangmanLetterButtons.ts';
import { hangmanLessButton } from './hangmanLessButton.ts';
import { hangmanMoreButton } from './hangmanMoreButton.ts';

/**
* The private list of all buttons. You can use this to edit the list within this file.
Expand Down
10 changes: 5 additions & 5 deletions src/commandContext/followUp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';

import type { Message, RepliableInteraction } from 'discord.js';

vi.mock('../helpers/actions/messages/replyToMessage.js');
import { sendMessageInChannel } from '../helpers/actions/messages/replyToMessage.js';
vi.mock('../helpers/actions/messages/replyToMessage.ts');
import { sendMessageInChannel } from '../helpers/actions/messages/replyToMessage.ts';
const mockSendMessageInChannel = sendMessageInChannel as Mock<typeof sendMessageInChannel>;

// Mock the logger to track output
vi.mock('../logger.js');
import { error as mockLoggerError } from '../logger.js';
vi.mock('../logger.ts');
import { error as mockLoggerError } from '../logger.ts';

import { followUpFactory as factory } from './followUp.js';
import { followUpFactory as factory } from './followUp.ts';

describe('follow-up message', () => {
const testMessage = { id: 'test-message' } as Message;
Expand Down
4 changes: 2 additions & 2 deletions src/commandContext/followUp.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { InteractionReplyOptions, RepliableInteraction } from 'discord.js';

import { sendMessageInChannel } from '../helpers/actions/messages/replyToMessage.js';
import { error } from '../logger.js';
import { sendMessageInChannel } from '../helpers/actions/messages/replyToMessage.ts';
import { error } from '../logger.ts';

export function followUpFactory(interaction: RepliableInteraction): CommandContext['followUp'] {
return async function followUp(options) {
Expand Down
6 changes: 3 additions & 3 deletions src/commandContext/prepareForLongRunningTasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { describe, expect, test, vi } from 'vitest';
import type { RepliableInteraction } from 'discord.js';

// Mock the logger to track output
vi.mock('../logger.js');
import { error as mockLoggerError } from '../logger.js';
vi.mock('../logger.ts');
import { error as mockLoggerError } from '../logger.ts';

import { prepareForLongRunningTasksFactory as factory } from './prepareForLongRunningTasks.js';
import { prepareForLongRunningTasksFactory as factory } from './prepareForLongRunningTasks.ts';

describe('prepareForLongRunningTasks', () => {
const mockInteractionDeferReply = vi.fn<RepliableInteraction['deferReply']>();
Expand Down
2 changes: 1 addition & 1 deletion src/commandContext/prepareForLongRunningTasks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RepliableInteraction } from 'discord.js';

import { error } from '../logger.js';
import { error } from '../logger.ts';

export function prepareForLongRunningTasksFactory(
interaction: RepliableInteraction
Expand Down
6 changes: 3 additions & 3 deletions src/commandContext/reply.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { describe, expect, test, vi } from 'vitest';
import type { RepliableInteraction } from 'discord.js';

// Mock the logger to track output
vi.mock('../logger.js');
import { error as mockLoggerError } from '../logger.js';
vi.mock('../logger.ts');
import { error as mockLoggerError } from '../logger.ts';

import { replyFactory as factory } from './reply.js';
import { replyFactory as factory } from './reply.ts';

describe('public reply', () => {
const mockInteractionReply = vi.fn<RepliableInteraction['reply']>();
Expand Down
Loading