{"id":50792835,"url":"https://github.com/cryptojones/1812","last_synced_at":"2026-06-12T12:02:24.102Z","repository":{"id":357976484,"uuid":"1239341330","full_name":"CryptoJones/1812","owner":"CryptoJones","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-15T03:52:41.000Z","size":22,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-15T05:45:04.927Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CryptoJones.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-15T02:09:22.000Z","updated_at":"2026-05-15T03:52:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/CryptoJones/1812","commit_stats":null,"previous_names":["cryptojones/1812"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/CryptoJones/1812","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2F1812","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2F1812/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2F1812/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2F1812/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CryptoJones","download_url":"https://codeload.github.com/CryptoJones/1812/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2F1812/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34243053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-12T12:01:45.532Z","updated_at":"2026-06-12T12:02:23.887Z","avatar_url":"https://github.com/CryptoJones.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1812\n\n**A self-hosted Discord chatbot with configurable LLM backends, persistent memory, and a FastAPI dashboard.**\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg?logo=apache)](https://opensource.org/licenses/Apache-2.0)\n[![GitHub](https://img.shields.io/badge/GitHub-CryptoJones%2F1812-181717?logo=github\u0026logoColor=white)](https://github.com/CryptoJones/1812)\n[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python\u0026logoColor=white)](https://www.python.org/)\n[![Discord](https://img.shields.io/badge/discord.py-2.3%2B-5865F2?logo=discord\u0026logoColor=white)](https://discordpy.readthedocs.io/)\n[![Version](https://img.shields.io/badge/version-v0.2.0--dev-orange)]()\n\n\u003e *\"Cannons firing. Bells ringing. The overture begins.\"*\n\u003e — Tchaikovsky, 1880\n\n---\n\n## Overview\n\n1812 is a self-hosted, open-source Discord chatbot backed by your choice of LLM. It maintains\nper-channel conversation history in SQLite, streams responses token-by-token, supports image input,\nand ships with a FastAPI health/dashboard sidecar.\n\nNamed after Tchaikovsky's 1812 Overture — a piece that knows exactly when to be quiet\nand exactly when to be deafening.\n\nA **CryptoJones** project.\n\n---\n\n## Features\n\n| Feature | Details |\n|---|---|\n| **Multi-backend LLM** | OpenAI, Anthropic Claude, Ollama (local), OpenRouter |\n| **Streaming responses** | Token-by-token output, edited live in Discord |\n| **SQLite persistence** | Per-channel history and per-server personas survive restarts |\n| **Per-user memory** | Remember facts about individual users across sessions |\n| **Per-server personas** | Different personality per Discord server |\n| **Slash commands** | `/clear`, `/persona`, `/model`, `/remember`, `/forget` |\n| **Image input** | Attach images — vision-capable models process them inline |\n| **Rate limiting** | Per-user sliding window (configurable requests/window) |\n| **Auto-summarisation** | History is summarised by the LLM when it gets too long |\n| **FastAPI sidecar** | `/health`, `/api/channels`, `/api/history/:id`, and a live dashboard |\n| **JSON structured logging** | Machine-readable logs with level, timestamp, and context |\n| **Docker** | Single-container deploy via `docker-compose up` |\n| **Graceful shutdown** | SIGTERM/SIGINT handled cleanly |\n\n---\n\n## Architecture\n\n| Component | Technology |\n|---|---|\n| Bot framework | [discord.py](https://discordpy.readthedocs.io/) 2.3+ |\n| LLM backends | OpenAI / Anthropic / Ollama / OpenRouter |\n| History storage | SQLite via `aiosqlite` |\n| Config | Pydantic Settings + `.env` |\n| Web sidecar | FastAPI + uvicorn (background thread) |\n| Container | Docker + Docker Compose |\n| Tests | pytest + pytest-asyncio |\n\n---\n\n## Quick Start\n\n**Docker (recommended)**\n```bash\ncp .env.example .env\n# Edit .env with your tokens\ndocker compose up -d\n```\n\n**Manual**\n```bash\nchmod +x setup.sh \u0026\u0026 ./setup.sh\ncp .env.example .env\n# Edit .env with your tokens\nsource .venv/bin/activate\npython bot.py\n```\n\n---\n\n## Configuration\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `DISCORD_TOKEN` | Yes | — | Your Discord bot token |\n| `LLM_PROVIDER` | No | `openai` | `openai` \\| `anthropic` \\| `ollama` \\| `openrouter` |\n| `LLM_MODEL` | No | `gpt-4o-mini` | Model name for the selected provider |\n| `OPENAI_API_KEY` | If OpenAI | — | OpenAI API key |\n| `ANTHROPIC_API_KEY` | If Anthropic | — | Anthropic API key |\n| `OPENROUTER_API_KEY` | If OpenRouter | — | OpenRouter API key |\n| `OLLAMA_BASE_URL` | No | `http://localhost:11434/v1` | Ollama endpoint |\n| `SYSTEM_PROMPT` | No | `You are 1812...` | Default bot personality |\n| `MAX_HISTORY_MESSAGES` | No | `20` | Messages to keep per channel before summarising |\n| `SUMMARISE_AT` | No | `40` | History length that triggers summarisation |\n| `RATE_LIMIT_REQUESTS` | No | `10` | Max requests per user per window |\n| `RATE_LIMIT_WINDOW_SECONDS` | No | `60` | Rate limit sliding window |\n| `DB_PATH` | No | `1812.db` | Path to SQLite database file |\n| `WEB_HOST` | No | `0.0.0.0` | FastAPI sidecar host |\n| `WEB_PORT` | No | `8080` | FastAPI sidecar port |\n| `SHUTDOWN_AFTER_MINUTES` | No | unset (= unbounded) | Auto-shutdown timer in minutes. CLI `--shutdown-after N` overrides this. |\n\n---\n\n## CLI Flags\n\n| Flag | Description |\n|---|---|\n| `--shutdown-after N` | Self-shutdown after N minutes of runtime. Overrides `SHUTDOWN_AFTER_MINUTES`. `0` is an explicit \"unbounded\" sentinel that clears any env value. Useful for time-boxed runs, cron jobs, and CI canaries. |\n\n```bash\n# Run for exactly two hours, then graceful shutdown.\npython bot.py --shutdown-after 120\n\n# Wipe an env-configured timer for this invocation only.\nSHUTDOWN_AFTER_MINUTES=30 python bot.py --shutdown-after 0\n```\n\n---\n\n## Slash Commands\n\n| Command | Who | Description |\n|---|---|---|\n| `/clear` | Anyone | Wipe conversation history for the current channel |\n| `/persona \u003cname\u003e` | Anyone | Switch persona: `default`, `concise`, `creative`, `technical` |\n| `/model \u003cname\u003e` | Admin only | Change the LLM model at runtime |\n| `/remember \u003ctext\u003e` | Anyone | Save a personal note about yourself for the bot to use |\n| `/forget` | Anyone | Clear your personal memory |\n\n---\n\n## Discord Setup\n\n1. Go to [discord.com/developers/applications](https://discord.com/developers/applications)\n2. Create or select your application\n3. Navigate to **Bot** → enable **Message Content Intent** and **Server Members Intent**\n4. Copy your bot token into `.env`\n5. Invite via **OAuth2 → URL Generator** — scopes: `bot`, `applications.commands` — permissions: `Send Messages`, `Read Message History`, `Attach Files`\n\n---\n\n## Web Dashboard\n\nThe FastAPI sidecar runs on port 8080 by default:\n\n| Endpoint | Description |\n|---|---|\n| `GET /health` | Liveness check |\n| `GET /api/channels` | List all channels with history |\n| `GET /api/history/{channel_id}` | Full message history for a channel |\n| `GET /` | Live HTML dashboard |\n\n---\n\n## Project Structure\n\n```\n1812/\n├── bot.py              # Discord bot, slash commands, streaming\n├── config.py           # Pydantic Settings\n├── db.py               # SQLite helpers (aiosqlite)\n├── llm.py              # LLM backend abstraction (OpenAI / Anthropic / Ollama / OpenRouter)\n├── rate_limiter.py     # Per-user sliding window rate limiter\n├── web.py              # FastAPI sidecar\n├── tests/              # pytest test suite\n├── Dockerfile\n├── docker-compose.yml\n├── requirements.txt\n├── setup.sh\n└── .env.example\n```\n\n---\n\n## Running Tests\n\n```bash\npython3 -m pytest tests/ -v\n```\n\n31 tests covering rate limiting, database operations, LLM backend selection, and bot helpers.\n\n---\n\n## Contributing\n\nPull requests welcome. Keep it simple. Keep it clean. Write tests first.\n\n---\n\n## License\n\n**Apache License 2.0** — Copyright 2026 Aaron K. Clark. See [LICENSE](LICENSE).\n\n---\n\n## Acknowledgments\n\nTo **Dr. John Crichton** of the Farscape Project — astronaut, wormhole theorist, and the most\nunlikely hero to ever fall through the wrong end of the universe. Wherever he is.\n\n---\n\nProudly Made in Nebraska. 🌽\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptojones%2F1812","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcryptojones%2F1812","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptojones%2F1812/lists"}