chore: add pnpm workspace configuration with only built dependencies
This commit is contained in:
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
@@ -73,28 +73,28 @@ attachments (SQLite):
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
bun install
|
||||
pnpm install
|
||||
|
||||
# Development (auto-restart on file changes)
|
||||
bun run dev
|
||||
pnpm run dev
|
||||
|
||||
# Production
|
||||
bun run start
|
||||
pnpm run start
|
||||
|
||||
# Type checking
|
||||
bun run typecheck
|
||||
pnpm run typecheck
|
||||
|
||||
# Linting (Biome)
|
||||
bun run lint
|
||||
pnpm run lint
|
||||
|
||||
# Format code (Biome)
|
||||
bun run format
|
||||
pnpm run format
|
||||
|
||||
# Run tests
|
||||
bun run test
|
||||
pnpm run test
|
||||
|
||||
# Build TypeScript
|
||||
bun run build
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -136,7 +136,7 @@ All config via `.env` (see `.env.example`). Key variables:
|
||||
|
||||
## 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:**
|
||||
- `tests/moderation/messageStore.test.ts` — Message store CRUD operations
|
||||
@@ -290,7 +290,7 @@ Handles SIGINT/SIGTERM/uncaughtException/unhandledRejection:
|
||||
## Notes
|
||||
|
||||
- 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)
|
||||
- WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord
|
||||
- Graceful shutdown ensures clean disconnection and resource cleanup
|
||||
@@ -327,7 +327,7 @@ Handles SIGINT/SIGTERM/uncaughtException/unhandledRejection:
|
||||
|
||||
- **Recorder subsystem** (`src/recorder/`):
|
||||
- `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)
|
||||
- `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
|
||||
# Install dependencies
|
||||
bun install
|
||||
pnpm install
|
||||
|
||||
# Development (auto-restart on file changes)
|
||||
bun run dev
|
||||
pnpm run dev
|
||||
|
||||
# Production
|
||||
bun run start
|
||||
pnpm run start
|
||||
|
||||
# Type checking
|
||||
bun run typecheck
|
||||
pnpm run typecheck
|
||||
|
||||
# Linting (Biome)
|
||||
bun run lint
|
||||
pnpm run lint
|
||||
|
||||
# Format code (Biome)
|
||||
bun run format
|
||||
pnpm run format
|
||||
|
||||
# Run tests
|
||||
bun run test
|
||||
pnpm run test
|
||||
|
||||
# Build TypeScript
|
||||
bun run build
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -404,9 +404,9 @@ All config via `.env` (see `.env.example`). Key variables:
|
||||
|
||||
## 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
|
||||
|
||||
@@ -507,7 +507,7 @@ These will likely require:
|
||||
## Notes
|
||||
|
||||
- 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)
|
||||
- WebSocket broadcasts PCM in real-time; browser can transmit audio back to Discord
|
||||
- Graceful shutdown ensures clean disconnection and resource cleanup
|
||||
|
||||
+5
-3
@@ -3,9 +3,10 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Discord bot that joins a voice channel and records audio",
|
||||
"main": "src/index.ts",
|
||||
"packageManager": "pnpm@10.25.0",
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts",
|
||||
"start": "bun src/index.ts",
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"start": "tsx src/index.ts",
|
||||
"build": "tsc --outDir dist",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "biome check --diagnostic-level=error .",
|
||||
@@ -39,9 +40,10 @@
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/fluent-ffmpeg": "^2.1.28",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"bun-types": "latest",
|
||||
"pino-pretty": "^10.3.1",
|
||||
"tsx": "^4.20.6",
|
||||
"vitest": "latest"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3626
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
onlyBuiltDependencies:
|
||||
- '@discordjs/opus'
|
||||
- better-sqlite3
|
||||
- esbuild
|
||||
+11
-1
@@ -15,10 +15,15 @@ import { getDatabase } from "./muxer-queue";
|
||||
const logger = createChildLogger("bot");
|
||||
|
||||
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 voiceController = new VoiceController(client);
|
||||
|
||||
logger.info("Opening database");
|
||||
const db = getDatabase();
|
||||
logger.info("Database ready");
|
||||
|
||||
let isShuttingDown = false;
|
||||
|
||||
@@ -84,5 +89,10 @@ process.on("unhandledRejection", (reason, promise) => {
|
||||
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");
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,11 @@ const isDev = process.env.NODE_ENV !== "production";
|
||||
|
||||
export const logger = pino({
|
||||
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
|
||||
? {
|
||||
target: "pino-pretty",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import path from "node:path";
|
||||
import { Database } from "bun:sqlite";
|
||||
import Database from "better-sqlite3";
|
||||
import { createChildLogger } from "./logger";
|
||||
|
||||
const logger = createChildLogger("muxer-queue");
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
StreamType,
|
||||
VoiceConnection,
|
||||
} from "@discordjs/voice";
|
||||
import prism from "prism-media";
|
||||
import { Readable } from "stream";
|
||||
|
||||
export class DiscordPlayer {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createRequire } from "node:module";
|
||||
import prism from "prism-media";
|
||||
import * as prism from "prism-media";
|
||||
import { config } from "../config";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import prism from "prism-media";
|
||||
import * as prism from "prism-media";
|
||||
import type { SegmentState } from "../types";
|
||||
|
||||
export function buildSegmentPaths(
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import helmet from "helmet";
|
||||
import http from "http";
|
||||
import path from "path";
|
||||
import pinoHttp from "pino-http";
|
||||
import prism from "prism-media";
|
||||
import * as prism from "prism-media";
|
||||
import { WebSocketServer } from "ws";
|
||||
import { AppError } from "./errors";
|
||||
import { createChildLogger, logger } from "./logger";
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["bun-types"],
|
||||
"types": ["node"],
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"experimentalDecorators": true,
|
||||
|
||||
Reference in New Issue
Block a user