Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ae0091c
chore: install and configure Biome
BillChirico Feb 11, 2026
cdce895
style: format codebase with Biome
BillChirico Feb 11, 2026
394e2b8
ci: add GitHub Actions workflows
BillChirico Feb 11, 2026
da14dc0
docs: add comprehensive documentation and editor config
BillChirico Feb 11, 2026
40e63ac
test: set up Vitest with smoke tests
BillChirico Feb 11, 2026
8075750
chore: align env vars and add command deploy script
BillChirico Feb 11, 2026
7d40516
chore: use claude-opus-4-6 for PR review action
BillChirico Feb 11, 2026
90299cc
docs: replace CLAUDE.md content with link to AGENTS.md
BillChirico Feb 11, 2026
c584c22
docs: add documentation maintenance rules to AGENTS.md
BillChirico Feb 11, 2026
5edd06c
docs: enforce no console.* logging rule in AGENTS.md
BillChirico Feb 11, 2026
de877f0
refactor: replace all console.* calls with Winston logger
BillChirico Feb 11, 2026
86c82aa
chore: regenerate lockfile with biome and vitest dependencies
BillChirico Feb 11, 2026
1458dbc
chore: update dotenv 17.2.3→17.2.4, pnpm 10.28.2→10.29.2
BillChirico Feb 11, 2026
0219421
test: add 80% coverage threshold with @vitest/coverage-v8
BillChirico Feb 11, 2026
bb0a611
chore: remove leftover verification scripts from repo root
BillChirico Feb 11, 2026
368a8c9
ci: enforce 80% test coverage threshold in CI pipeline
BillChirico Feb 11, 2026
8043038
chore: remove leftover test-log-levels.js script
BillChirico Feb 11, 2026
bcb3e0e
📝 CodeRabbit Chat: Add generated unit tests
coderabbitai[bot] Feb 11, 2026
8af60a7
📝 CodeRabbit Chat: Add unit tests for PR changes
coderabbitai[bot] Feb 11, 2026
99f0bf1
chore: remove coverage output and add to gitignore
BillChirico Feb 11, 2026
ead65e4
test: comprehensive unit tests achieving 80%+ coverage
BillChirico Feb 11, 2026
a218e85
ci: add id-token write permission for Claude Code review action
BillChirico Feb 11, 2026
35eca61
fix: address PR #16 review comments (round 1 partial)
BillChirico Feb 11, 2026
519d3f4
chore: remove temporary fix tracking files
BillChirico Feb 11, 2026
c661207
fix: address PR #16 review comments (round 1 continued)
BillChirico Feb 11, 2026
be98128
fix: implement test quality improvements from review feedback
BillChirico Feb 11, 2026
6597d1b
ci: pin workflow actions to immutable commit SHAs
BillChirico Feb 11, 2026
089a35d
docs: align Node 22 references in AGENTS and README
BillChirico Feb 11, 2026
4651d70
fix(config): sanitize production error output and escape reset section
BillChirico Feb 11, 2026
0b854fc
fix(deploy): remove unused __filename and log stack on failure
BillChirico Feb 11, 2026
b208df9
fix(events): guard process unhandledRejection handler from duplicate …
BillChirico Feb 11, 2026
4fc995a
test(config): clarify not-deferred error-path intent in comment
BillChirico Feb 11, 2026
8a1ecbd
fix(index): use Events.ClientReady constant in startup handler
BillChirico Feb 11, 2026
1e9d469
test(ai): ensure OPENCLAW_API_KEY cleanup via finally
BillChirico Feb 11, 2026
030f7b6
test(errors): use distinct empty-message unknown error case
BillChirico Feb 11, 2026
ec42599
refactor: share command loader across startup and deploy
BillChirico Feb 11, 2026
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
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# EditorConfig — https://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
38 changes: 27 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
# Discord bot token
# Discord bot token (required)
DISCORD_TOKEN=your_discord_bot_token

# Discord application client ID (for slash command registration)
CLIENT_ID=your_discord_client_id
# Discord application/client ID for slash command deployment (required)
# Preferred name:
DISCORD_CLIENT_ID=your_discord_client_id
# Backward-compatible alias (optional):
# CLIENT_ID=your_discord_client_id

# Discord guild/server ID (optional - for faster command deployment during development)
# If not set, commands deploy globally (takes up to 1 hour to propagate)
# Discord guild/server ID (optional)
# If set, commands deploy to one guild instantly (great for development).
# If omitted, commands deploy globally (can take up to 1 hour).
GUILD_ID=your_discord_guild_id

# OpenClaw API (routes through your Claude subscription)
# Local: http://localhost:18789/v1/chat/completions
# Remote (Railway/etc): https://your-tailscale-hostname.ts.net/v1/chat/completions
OPENCLAW_URL=http://localhost:18789/v1/chat/completions
OPENCLAW_TOKEN=your_openclaw_gateway_token
# OpenClaw chat completions endpoint (required)
# Local: http://localhost:18789/v1/chat/completions
# Remote: https://your-tailscale-hostname.ts.net/v1/chat/completions
OPENCLAW_API_URL=http://localhost:18789/v1/chat/completions
# Backward-compatible alias (optional):
# OPENCLAW_URL=http://localhost:18789/v1/chat/completions

# Logging level (options: debug, info, warn, error)
# OpenClaw API key / gateway token (required)
OPENCLAW_API_KEY=your_openclaw_gateway_token
# Backward-compatible alias (optional):
# OPENCLAW_TOKEN=your_openclaw_gateway_token

# PostgreSQL connection string (required for persistent config/state)
DATABASE_URL=postgresql://user:password@host:5432/database

# Optional: force SSL for DB connections if needed by your host
# DATABASE_SSL=true

# Logging level (optional: debug, info, warn, error)
LOG_LEVEL=info
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint-and-test:
name: Lint & Test
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup pnpm
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint (Biome)
run: pnpm lint

- name: Test with coverage (Vitest)
run: pnpm test:coverage
30 changes: 30 additions & 0 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Claude Code Review

on:
pull_request:
types: [opened, reopened, synchronize]
issue_comment:
types: [created]

jobs:
claude-review:
if: |
(github.event_name == 'pull_request') ||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude'))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
id-token: write

steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Claude Code Review
uses: anthropics/claude-code-action@23ed4cb53d6eacddbc22ec16652c98bcc54e0476 # v1.0.48
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
model: claude-opus-4-6-20250616
timeout_minutes: 10
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ node_modules/
.env
*.log
logs/
coverage/

# Auto Claude data directory and files
.auto-claude/
Expand Down
147 changes: 147 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# AGENTS.md — AI Coding Agent Guide

> This file provides context for AI coding agents (Claude Code, Copilot, Cursor, etc.) working on bills-bot.

## Project Overview

**Bill Bot** is a Discord bot for the Volvox developer community. It provides AI chat (via OpenClaw/Claude), dynamic welcome messages, spam detection, and runtime configuration management backed by PostgreSQL.

## Stack

- **Runtime:** Node.js 22 (ESM modules, `"type": "module"`)
- **Framework:** discord.js v14
- **Database:** PostgreSQL (via `pg` — raw SQL, no ORM)
- **Logging:** Winston with daily file rotation
- **AI:** Claude via OpenClaw chat completions API
- **Linting:** Biome
- **Testing:** Vitest
- **Hosting:** Railway

## Key Files

| File | Purpose |
|------|---------|
| `src/index.js` | Entry point — client setup, command loading, startup sequence |
| `src/db.js` | PostgreSQL pool management (init, query, close) |
| `src/logger.js` | Winston logger setup with file + console transports |
| `src/commands/*.js` | Slash commands (auto-loaded) |
| `src/modules/ai.js` | AI chat handler — conversation history, OpenClaw API calls |
| `src/modules/chimeIn.js` | Organic conversation joining logic |
| `src/modules/welcome.js` | Dynamic welcome message generation |
| `src/modules/spam.js` | Spam/scam pattern detection |
| `src/modules/config.js` | Config loading/saving (DB + file), runtime updates |
| `src/modules/events.js` | Event handler registration (wires modules to Discord events) |
| `src/utils/errors.js` | Error classes and handling utilities |
| `src/utils/health.js` | Health monitoring singleton |
| `src/utils/permissions.js` | Permission checking for commands |
| `src/utils/retry.js` | Retry utility for flaky operations |
| `src/utils/registerCommands.js` | Discord REST API command registration |
| `src/utils/splitMessage.js` | Message splitting for Discord's 2000-char limit |
| `config.json` | Default configuration (seeded to DB on first run) |
| `.env.example` | Environment variable template |

## Code Conventions

### General

- **ESM only** — use `import`/`export`, never `require()`
- **No TypeScript** — plain JavaScript with JSDoc comments for documentation
- **Node.js builtins** — always use `node:` protocol (e.g. `import { readFileSync } from 'node:fs'`)
- **Semicolons** — always use them
- **Single quotes** — enforced by Biome
- **2-space indentation** — enforced by Biome

### Logging

- **Always use Winston** — `import { info, warn, error } from '../logger.js'`
- **NEVER use `console.log`, `console.warn`, `console.error`, or any `console.*` method** in src/ files — no exceptions
- If you see `console.*` in existing code, replace it with the Winston equivalent
- Pass structured metadata: `info('Message processed', { userId, channelId })`

### Error Handling

- Use custom error classes from `src/utils/errors.js`
- Always log errors with context before re-throwing
- Graceful shutdown is handled in `src/index.js`

### Config

- Config is loaded from PostgreSQL (falls back to `config.json`)
- Use `getConfig()` from `src/modules/config.js` to read config
- Use `setConfigValue(key, value)` to update at runtime
- Config is a live object reference — mutations propagate automatically

## How to Add a Slash Command

1. Create `src/commands/yourcommand.js`:

```js
import { SlashCommandBuilder } from 'discord.js';

export const data = new SlashCommandBuilder()
.setName('yourcommand')
.setDescription('What it does');

export async function execute(interaction) {
await interaction.reply('Hello!');
}
```

2. Commands are auto-discovered from `src/commands/` on startup
3. Run `pnpm run deploy` to register with Discord (or restart the bot)
4. Add permission in `config.json` under `permissions.allowedCommands`

## How to Add a Module

1. Create `src/modules/yourmodule.js` with handler functions
2. Register handlers in `src/modules/events.js`:

```js
import { yourHandler } from './yourmodule.js';
// In registerEventHandlers():
client.on('eventName', (args) => yourHandler(args, config));
```

3. Config for your module goes in `config.json` under a new key
4. Check `config.yourModule.enabled` before processing

## Testing

- **Framework:** Vitest (`pnpm test`)
- **Test directory:** `tests/`
- **Coverage:** `pnpm test:coverage` — **mandatory 80% threshold** on statements, branches, functions, and lines
- Coverage provider: `@vitest/coverage-v8`
- Tests are smoke/unit tests — the bot requires Discord credentials so we don't test live connections
- Test config structure, command exports, utility functions
- Run `pnpm test` before every commit
- **Any new code must include tests** — PRs that drop coverage below 80% will fail CI

## Documentation

**Keep docs up to date — this is non-negotiable.**

After every code change, check whether these files need updating:

- **`README.md`** — setup instructions, architecture overview, config reference, env vars
- **`AGENTS.md`** (this file) — key files table, code conventions, "how to add" guides, common pitfalls
- **`CONTRIBUTING.md`** — workflow, branching, commit conventions
- **`.env.example`** — if you add/remove/rename an environment variable, update this immediately
- **`config.json`** — if you add a new config section or key, document it in README.md's config reference

**When to update:**
- Added a new command → update Key Files table, add to README command list
- Added a new module → update Key Files table, document config section
- Changed env vars → update `.env.example` and README's environment section
- Changed architecture (new dependency, new pattern) → update Stack section and relevant guides
- Found a new pitfall → add to Common Pitfalls below

**Rule of thumb:** If a new contributor (human or AI) would be confused without the update, write it.

## Common Pitfalls

1. **Missing `node:` prefix** — Biome will catch this, but remember it for new imports
2. **Config is async** — `loadConfig()` returns a Promise; it must be awaited at startup
3. **Discord intents** — the bot needs MessageContent, GuildMembers, and GuildVoiceStates intents enabled
4. **DATABASE_URL optional** — the bot works without a database (uses config.json only), but config persistence requires PostgreSQL
5. **Undici override** — `pnpm.overrides` pins undici; this was originally added for Node 18 compatibility and may no longer be needed on Node 22. Verify before removing
6. **2000-char limit** — Discord messages can't exceed 2000 characters; use `splitMessage()` utility
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLAUDE.md

See [AGENTS.md](./AGENTS.md) for full project context, architecture, and coding guidelines.
77 changes: 77 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Contributing to Bill Bot

Thanks for your interest in contributing! Bill Bot is part of the [Volvox](https://volvox.dev) open-source community.

## Getting Started

1. Fork the repository
2. Follow the [setup instructions](README.md#-setup) in the README
3. Create a feature branch from `main`

## Development Workflow

### Branch Naming

Use descriptive branch names with prefixes:

- `feat/add-music-command` — new features
- `fix/welcome-message-crash` — bug fixes
- `chore/update-dependencies` — maintenance
- `docs/update-readme` — documentation
- `refactor/simplify-config` — code improvements

### Commit Messages

We use [Conventional Commits](https://www.conventionalcommits.org/):

```text
feat: add music playback command
fix: prevent crash on empty welcome channel
chore: update discord.js to v14.16
docs: add API reference to README
refactor: simplify config loading logic
style: format with Biome
test: add config validation tests
ci: update Node.js version in CI
```

### Before Submitting

1. **Lint:** `pnpm lint` — must pass with no errors
2. **Format:** `pnpm format` — auto-format your code
3. **Test:** `pnpm test` — all tests must pass
4. **Commit:** use conventional commit messages

### Pull Requests

1. Open a PR against `main`
2. Fill in the PR description with what changed and why
3. PRs are automatically reviewed by Claude Code
4. CI must pass (lint + tests)
5. Wait for a maintainer review

## Code Style

Code style is enforced by [Biome](https://biomejs.dev/):

- Single quotes
- Semicolons always
- 2-space indentation
- Trailing commas
- 100-character line width

Run `pnpm format` to auto-format. The CI will reject PRs with formatting issues.

## Project Structure

See [AGENTS.md](AGENTS.md) for a detailed guide to the codebase, including:

- Key files and their purposes
- How to add commands and modules
- Code conventions
- Common pitfalls

## Questions?

- Open an issue on GitHub
- Ask in the Volvox Discord server at [volvox.dev](https://volvox.dev)
Loading
Loading