chore: add pnpm workspace configuration with only built dependencies

This commit is contained in:
MythEclipse
2026-05-14 01:07:50 +07:00
parent 0bdab3b446
commit a02d1fb7c0
13 changed files with 3679 additions and 33 deletions
+23 -23
View File
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
**Discord Moderation Watcher Bot** — A comprehensive monitoring bot that captures voice, text messages, and images from Discord servers. Records audio from voice channels, captures all text messages (new/edited/deleted) from channels and threads, and uploads attachments to external storage. All data stored in SQLite with real-time dashboard. **Discord Moderation Watcher Bot** — A comprehensive monitoring bot that captures voice, text messages, and images from Discord servers. Records audio from voice channels, captures all text messages (new/edited/deleted) from channels and threads, and uploads attachments to external storage. All data stored in SQLite with real-time dashboard.
Built with **Bun** + **discord.js-selfbot-v13** + **@discordjs/voice** + **Express** + **WebSocket**. Built with **Node.js/pnpm** + **discord.js-selfbot-v13** + **@discordjs/voice** + **Express** + **WebSocket**.
## Architecture ## Architecture
@@ -73,28 +73,28 @@ attachments (SQLite):
```bash ```bash
# Install dependencies # Install dependencies
bun install pnpm install
# Development (auto-restart on file changes) # Development (auto-restart on file changes)
bun run dev pnpm run dev
# Production # Production
bun run start pnpm run start
# Type checking # Type checking
bun run typecheck pnpm run typecheck
# Linting (Biome) # Linting (Biome)
bun run lint pnpm run lint
# Format code (Biome) # Format code (Biome)
bun run format pnpm run format
# Run tests # Run tests
bun run test pnpm run test
# Build TypeScript # Build TypeScript
bun run build pnpm run build
``` ```
## Configuration ## Configuration
@@ -136,7 +136,7 @@ All config via `.env` (see `.env.example`). Key variables:
## Testing ## Testing
Tests use **Vitest** in `tests/` directory. Run with `bun run test`. Tests use **Vitest** in `tests/` directory. Run with `pnpm run test`.
**Test Coverage:** **Test Coverage:**
- `tests/moderation/messageStore.test.ts` — Message store CRUD operations - `tests/moderation/messageStore.test.ts` — Message store CRUD operations
@@ -290,7 +290,7 @@ Handles SIGINT/SIGTERM/uncaughtException/unhandledRejection:
## Notes ## Notes
- Bot uses selfbot variant (user account) rather than standard bot token — check Discord ToS - Bot uses selfbot variant (user account) rather than standard bot token — check Discord ToS
- Opus decoding disabled on Bun without native opus to avoid crashes - Opus decoding requires native `@discordjs/opus` under Node.js
- OGG segments include metadata JSON for each segment (user info, timestamps, duration) - OGG segments include metadata JSON for each segment (user info, timestamps, duration)
- WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord - WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord
- Graceful shutdown ensures clean disconnection and resource cleanup - Graceful shutdown ensures clean disconnection and resource cleanup
@@ -327,7 +327,7 @@ Handles SIGINT/SIGTERM/uncaughtException/unhandledRejection:
- **Recorder subsystem** (`src/recorder/`): - **Recorder subsystem** (`src/recorder/`):
- `audioStream.ts` — Subscribes to Discord audio receiver, emits Opus packets - `audioStream.ts` — Subscribes to Discord audio receiver, emits Opus packets
- `decoder.ts` — Opus decoder with runtime checks (Bun vs Node), cooldown/rotation logic for web PCM broadcast - `decoder.ts` — Opus decoder with runtime checks, cooldown/rotation logic for web PCM broadcast
- `segment.ts` — Manages OGG file rotation (5s default segments per user) - `segment.ts` — Manages OGG file rotation (5s default segments per user)
- `metadata.ts` — Collects user/role info, creates segment metadata JSON - `metadata.ts` — Collects user/role info, creates segment metadata JSON
@@ -363,28 +363,28 @@ Each segment is 5s (configurable). Metadata JSON includes user info, roles, time
```bash ```bash
# Install dependencies # Install dependencies
bun install pnpm install
# Development (auto-restart on file changes) # Development (auto-restart on file changes)
bun run dev pnpm run dev
# Production # Production
bun run start pnpm run start
# Type checking # Type checking
bun run typecheck pnpm run typecheck
# Linting (Biome) # Linting (Biome)
bun run lint pnpm run lint
# Format code (Biome) # Format code (Biome)
bun run format pnpm run format
# Run tests # Run tests
bun run test pnpm run test
# Build TypeScript # Build TypeScript
bun run build pnpm run build
``` ```
## Configuration ## Configuration
@@ -404,9 +404,9 @@ All config via `.env` (see `.env.example`). Key variables:
## Testing ## Testing
Tests use **Vitest** in `tests/` directory. Run with `bun run test`. Tests use **Vitest** in `tests/` directory. Run with `pnpm run test`.
Example: `tests/decoder.test.ts` tests Opus decoder runtime detection (Bun vs Node, native opus availability). Example: `tests/decoder.test.ts` tests Opus decoder runtime detection and native opus availability.
## Code Style ## Code Style
@@ -507,7 +507,7 @@ These will likely require:
## Notes ## Notes
- Bot uses selfbot variant (user account) rather than standard bot token — check Discord ToS - Bot uses selfbot variant (user account) rather than standard bot token — check Discord ToS
- Opus decoding disabled on Bun without native opus to avoid crashes - Opus decoding requires native `@discordjs/opus` under Node.js
- OGG segments include metadata JSON for each segment (user info, timestamps, duration) - OGG segments include metadata JSON for each segment (user info, timestamps, duration)
- WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord - WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord
- Graceful shutdown ensures clean disconnection and resource cleanup - Graceful shutdown ensures clean disconnection and resource cleanup
BIN
View File
Binary file not shown.
+5 -3
View File
@@ -3,9 +3,10 @@
"version": "1.0.0", "version": "1.0.0",
"description": "Discord bot that joins a voice channel and records audio", "description": "Discord bot that joins a voice channel and records audio",
"main": "src/index.ts", "main": "src/index.ts",
"packageManager": "pnpm@10.25.0",
"scripts": { "scripts": {
"dev": "bun --watch src/index.ts", "dev": "tsx watch src/index.ts",
"start": "bun src/index.ts", "start": "tsx src/index.ts",
"build": "tsc --outDir dist", "build": "tsc --outDir dist",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"lint": "biome check --diagnostic-level=error .", "lint": "biome check --diagnostic-level=error .",
@@ -39,9 +40,10 @@
"@types/better-sqlite3": "^7.6.13", "@types/better-sqlite3": "^7.6.13",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/fluent-ffmpeg": "^2.1.28", "@types/fluent-ffmpeg": "^2.1.28",
"@types/node": "^24.10.1",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"bun-types": "latest",
"pino-pretty": "^10.3.1", "pino-pretty": "^10.3.1",
"tsx": "^4.20.6",
"vitest": "latest" "vitest": "latest"
} }
} }
+3626
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
onlyBuiltDependencies:
- '@discordjs/opus'
- better-sqlite3
- esbuild
+11 -1
View File
@@ -15,10 +15,15 @@ import { getDatabase } from "./muxer-queue";
const logger = createChildLogger("bot"); const logger = createChildLogger("bot");
const token = config.DISCORD_TOKEN; const token = config.DISCORD_TOKEN;
logger.info({ hasToken: token.length > 0, tokenLength: token.length }, "Config loaded");
logger.info("Creating Discord client");
const client = new Client(); const client = new Client();
const voiceController = new VoiceController(client); const voiceController = new VoiceController(client);
logger.info("Opening database");
const db = getDatabase(); const db = getDatabase();
logger.info("Database ready");
let isShuttingDown = false; let isShuttingDown = false;
@@ -84,5 +89,10 @@ process.on("unhandledRejection", (reason, promise) => {
gracefulShutdown("unhandledRejection"); gracefulShutdown("unhandledRejection");
}); });
client.login(token); logger.info("Calling Discord client.login");
client.login(token).then(() => {
logger.info("Discord client.login resolved");
}).catch((error) => {
logger.error({ error }, "Discord client.login failed");
});
+5
View File
@@ -4,6 +4,11 @@ const isDev = process.env.NODE_ENV !== "production";
export const logger = pino({ export const logger = pino({
level: process.env.LOG_LEVEL || (isDev ? "debug" : "info"), level: process.env.LOG_LEVEL || (isDev ? "debug" : "info"),
serializers: {
error: pino.stdSerializers.err,
err: pino.stdSerializers.err,
reason: (value) => value instanceof Error ? pino.stdSerializers.err(value) : value,
},
transport: isDev transport: isDev
? { ? {
target: "pino-pretty", target: "pino-pretty",
+1 -1
View File
@@ -1,5 +1,5 @@
import path from "node:path"; import path from "node:path";
import { Database } from "bun:sqlite"; import Database from "better-sqlite3";
import { createChildLogger } from "./logger"; import { createChildLogger } from "./logger";
const logger = createChildLogger("muxer-queue"); const logger = createChildLogger("muxer-queue");
-1
View File
@@ -6,7 +6,6 @@ import {
StreamType, StreamType,
VoiceConnection, VoiceConnection,
} from "@discordjs/voice"; } from "@discordjs/voice";
import prism from "prism-media";
import { Readable } from "stream"; import { Readable } from "stream";
export class DiscordPlayer { export class DiscordPlayer {
+1 -1
View File
@@ -1,5 +1,5 @@
import { createRequire } from "node:module"; import { createRequire } from "node:module";
import prism from "prism-media"; import * as prism from "prism-media";
import { config } from "../config"; import { config } from "../config";
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
+1 -1
View File
@@ -1,6 +1,6 @@
import fs from "node:fs"; import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import prism from "prism-media"; import * as prism from "prism-media";
import type { SegmentState } from "../types"; import type { SegmentState } from "../types";
export function buildSegmentPaths( export function buildSegmentPaths(
+1 -1
View File
@@ -4,7 +4,7 @@ import helmet from "helmet";
import http from "http"; import http from "http";
import path from "path"; import path from "path";
import pinoHttp from "pino-http"; import pinoHttp from "pino-http";
import prism from "prism-media"; import * as prism from "prism-media";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
import { AppError } from "./errors"; import { AppError } from "./errors";
import { createChildLogger, logger } from "./logger"; import { createChildLogger, logger } from "./logger";
+1 -1
View File
@@ -6,7 +6,7 @@
"strict": true, "strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"types": ["bun-types"], "types": ["node"],
"outDir": "dist", "outDir": "dist",
"rootDir": "src", "rootDir": "src",
"experimentalDecorators": true, "experimentalDecorators": true,