Skip to content
Closed
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
7 changes: 4 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ jobs:
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
echo "DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }}" > /home/${{ secrets.VPS_USER }}/discord-bot/.env
echo "CLIENT_ID=${{ secrets.CLIENT_ID }}" >> /home/${{ secrets.VPS_USER }}/discord-bot/.env
echo "DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }}" > /home/${{ secrets.VPS_USER }}/discord-bot/.env.local
echo "CLIENT_ID=${{ secrets.CLIENT_ID }}" >> /home/${{ secrets.VPS_USER }}/discord-bot/.env.local
echo "NODE_ENV=production" >> /home/${{ secrets.VPS_USER }}/discord-bot/.env.local

- name: Extract and restart bot on VPS
uses: appleboy/[email protected]
Expand All @@ -71,4 +72,4 @@ jobs:
script: |
cd /home/${{ secrets.VPS_USER }}/discord-bot/
tar xzf bot-build.tar.gz
pm2 restart bot || pm2 start ./dist/index.js --name bot
pm2 restart bot || pm2 start ./dist/index.js --name bot --node-args="--env-file=.env.local"
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"description": "Web Dev & Web Design discord bot",
"type": "module",
"scripts": {
"build:ci": "npm run build:ts && npm run build:copy",
"build:dev": "pnpm run build:ts && pnpm run build:copy",
"build:ci": "NODE_ENV=production npm run build:ts && npm run build:copy",
"build:dev": "NODE_ENV=production pnpm run build:ts && pnpm run build:copy",
"build:ts": "tsup",
"build:copy": "node scripts/copy-assets.js",
"start": "node dist/index.js",
"start": "NODE_ENV=production node dist/index.js",
"dev": "tsx watch src/index.ts",
"deploy": "tsx src/util/deploy.ts",
"lint": "biome lint .",
Expand All @@ -29,6 +29,7 @@
"dependencies": {
"@discordjs/core": "^2.2.2",
"discord.js": "^14.22.1",
"typescript": "^5.9.2",
"web-features": "^3.3.0"
},
"devDependencies": {
Expand All @@ -37,8 +38,7 @@
"husky": "^9.1.7",
"lint-staged": "^16.2.1",
"tsup": "^8.5.0",
"tsx": "^4.20.6",
"typescript": "^5.9.2"
"tsx": "^4.20.6"
},
"lint-staged": {
"*.{ts,js}": [
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

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

9 changes: 6 additions & 3 deletions src/commands/guides/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { ApplicationCommandOptionType, ApplicationCommandType, MessageFlags } from 'discord.js';
import { logToChannel } from '../../util/channel-logging.js';
import { createCommand } from '../../util/commands.js';
import { loadMarkdownOptions } from '../../util/markdown.js';

const subjectsDir = new URL('./subjects/', import.meta.url);
import { loadMarkdownOptions, resolveAssetPath } from '../../util/markdown.js';

const subjectChoices = new Map<string, string>();

const loadChoices = async (): Promise<void> => {
const subjectsDir = resolveAssetPath(
'./subjects/',
'./commands/guides/subjects/',
import.meta.url
);
const choices = await loadMarkdownOptions<{ name: string }>(subjectsDir);

for (const { frontmatter, content } of choices) {
Expand Down
5 changes: 2 additions & 3 deletions src/commands/tips/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { ApplicationCommandOptionType, ApplicationCommandType, MessageFlags } from 'discord.js';
import { logToChannel } from '../../util/channel-logging.js';
import { createCommand } from '../../util/commands.js';
import { loadMarkdownOptions } from '../../util/markdown.js';

const subjectsDir = new URL('./subjects/', import.meta.url);
import { loadMarkdownOptions, resolveAssetPath } from '../../util/markdown.js';

const subjectChoices = new Map<string, string>();

const loadChoices = async (): Promise<void> => {
const subjectsDir = resolveAssetPath('./subjects/', './commands/tips/subjects/', import.meta.url);
const choices = await loadMarkdownOptions<{ name: string }>(subjectsDir);

for (const { frontmatter, content } of choices) {
Expand Down
1 change: 1 addition & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const config = {
token: requireEnv('DISCORD_TOKEN'),
clientId: requireEnv('CLIENT_ID'),
},
ENV: process.env.NODE_ENV,
// Add more config sections as needed:
// database: {
// url: requireEnv('DATABASE_URL'),
Expand Down
10 changes: 6 additions & 4 deletions src/events/just-ask.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Events } from 'discord.js';
import { MINUTE } from '../constants/time.js';
import { createEvent } from '../util/events.js';
import { loadMarkdownOptions } from '../util/markdown.js';
import { loadMarkdownOptions, resolveAssetPath } from '../util/markdown.js';
import { rateLimit } from '../util/rate-limit.js';

// Subject patterns (who)
Expand All @@ -26,10 +26,12 @@ const askToAskPattern = new RegExp(

const isAskingToAsk = (text: string) => askToAskPattern.test(text);

const [response] = await loadMarkdownOptions<{ name: string }>(
new URL('../commands/tips/subjects/', import.meta.url),
'justask.md'
const justAskDir = resolveAssetPath(
'../commands/tips/subjects/',
'./commands/tips/subjects/',
import.meta.url
);
const [response] = await loadMarkdownOptions<{ name: string }>(justAskDir, 'justask.md');

const { canRun, reset } = rateLimit(10 * MINUTE);

Expand Down
15 changes: 15 additions & 0 deletions src/util/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import type { PathLike } from 'node:fs';
import { readdir, readFile } from 'node:fs/promises';
import { config } from '../env.js';

/**
* Resolves a path that works in both dev (src/) and prod (dist/) environments
*
* @param devPath - Path relative to the calling file in dev (e.g., './subjects/')
* @param prodPath - Path relative to dist/index.js in prod (e.g., './commands/tips/subjects/')
* @param baseUrl - import.meta.url from the calling file
* @returns URL that works in both environments
*/
export const resolveAssetPath = (devPath: string, prodPath: string, baseUrl: string): URL => {
const isProduction = config.ENV === 'production';
const path = isProduction ? prodPath : devPath;
return new URL(path, baseUrl);
};

/**
* A simple markdown parser that extracts frontmatter and content
Expand Down
15 changes: 12 additions & 3 deletions tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { defineConfig } from 'tsup';

export default defineConfig({
bundle: false,
bundle: true,
clean: true,
dts: false,
outDir: 'dist',
format: ['esm'],
target: 'esnext',
entry: ['src/**/*.ts', 'src/*.ts'],
minify: true,
entry: ['src/index.ts'],
minify: false,
external: [
// Don't bundle discord.js - it has dynamic requires that don't work in ESM bundles
'discord.js',
'@discordjs/core',
// Don't bundle typescript - it has dynamic requires that don't work in ESM bundles
'typescript',
],
treeshake: true,
shims: true,
});