diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..6ad2f34 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,102 @@ +# Thoughtful Discord Bot - AI Coding Instructions + +## Project Overview +A Go Discord bot that integrates with the Thoughtful API for managing ideas through Discord commands. Built with `discordgo` library, uses JSON for user config persistence. + +## Architecture + +### Component Structure +- **bot.go**: Main entry point. Handles Discord session, registers handlers, manages bot lifecycle +- **commands/**: Modular command system where each command is a separate file + - `commands.go`: Command registry and interaction router + - `storage.go`: JSON-based user config persistence (users.json) + - Individual command files: `setup.go`, `list.go`, `delete.go`, `logout.go` + +### Key Design Patterns + +**Command Registration Pattern**: Each command implements the `Cmd` struct: +```go +type Cmd struct { + Command *discordgo.ApplicationCommand + Handler func(s *discordgo.Session, ic *discordgo.InteractionCreate) + AutocompleteHandler func(s *discordgo.Session, ic *discordgo.InteractionCreate) // optional +} +``` +All commands are registered via `getAllCommands()` in [commands/commands.go](../commands/commands.go). + +**Dual Command Interface**: The bot supports both: +- Slash commands (e.g., `/setup`, `/list`) via Discord's ApplicationCommand API +- Legacy text commands (e.g., `.thought Title; Description`) for backward compatibility + +**User State Management**: Per-user configs (Thoughtful instance URL + API key) stored in `users.json`. Thread-safe access via mutex in [commands/storage.go](../commands/storage.go). + +**Modal-Based Setup**: `/setup` uses Discord modals (not option prompts) to collect sensitive credentials privately. + +### External Dependencies +- **Thoughtful API**: External idea management service. Requires: + - Base URL (user-provided, e.g., `https://instance.com`) + - API key authentication via `API-Authentication` header + - Endpoints: `/api/ideas/create`, `/api/ideas/list`, `/api/ideas/delete` +- **Discord API**: via `github.com/bwmarrin/discordgo` library + +## Developer Workflows + +### Running Locally +```sh +# Set environment variable +$env:DISCORD_BOT_TOKEN="your_token_here" + +# Run directly +go run . +``` + +### Docker Deployment +The bot is designed for Docker Compose deployment. See [docker-compose.yml](../docker-compose.yml): +- Uses `golang:1.25` image (note: uncommon version - likely should be `1.21` or `1.22`) +- Auto-pulls git changes on container start +- Requires `.env` file with `DISCORD_BOT_TOKEN` + +### Adding New Commands +1. Create new file in `commands/` (e.g., `mycommand.go`) +2. Implement `newMyCommand()` returning `*Cmd` +3. Add to `getAllCommands()` in [commands/commands.go](../commands/commands.go) +4. Command automatically registers on bot startup + +## Project-Specific Conventions + +### Error Handling +- Use `respondError()` helper for interaction failures (defined in command files) +- Silent failures acceptable for autocomplete handlers +- Channel messages for `.thought` command errors + +### User Identification +Discord interactions may have `User` OR `Member.User` depending on context: +```go +user := ic.User +if user == nil { + user = ic.Member.User +} +``` +Always check both when accessing user data. + +### API Communication +- Always trim trailing slashes from instance URLs: `url = strings.TrimSuffix(url, "/")` +- Use `API-Authentication` header (not `Authorization`) +- 401 status codes mean invalid/expired API keys + +### Storage Layer +- **Never** edit `users.json` directly - always use `storage.go` functions +- Operations are mutex-protected but not transactional +- Corrupted JSON automatically resets to empty map + +## Critical Gotchas + +1. **Intents Required**: Bot needs `IntentsGuildMessages` (set in [bot.go](../bot.go#L40)). Message Content Intent must be enabled in Discord Developer Portal. + +2. **Slash Command Updates**: Slash commands persist in Discord. To update, uncomment command cleanup code in [commands/commands.go](../commands/commands.go#L30-L33). + +3. **Modal Response Type**: Setup uses `InteractionResponseModal`, not `InteractionResponseChannelMessageWithSource`. + +4. **Autocomplete Filtering**: Delete command autocomplete should filter by user input but currently returns all ideas - see [commands/delete.go](../commands/delete.go#L86-L100). + +5. **No Database**: All state in `users.json`. Loss of this file = all users must re-run `/setup`.