Project
Necobot
Discord bot in TypeScript with a points economy, minigames and in-character replies generated by a locally hosted language model.
- SQLite
- Ollama
- Docker
- Drizzle
- Sapphire
- TypeScript
- Discord API
- GitHub Actions
Context
A bot for a Discord server with friends: a points system, minigames and a character that replies to mentions. The interesting part was giving the replies personality without paying for an external API.
Architecture
TypeScript on the Sapphire framework, which organises commands, listeners and preconditions. Persistence in SQLite with Drizzle and versioned migrations. The character's replies are generated by a local model through Ollama, with scheduled tasks for periodic events.
Continuous integration with GitHub Actions and deployment as a Docker container on my own server.
Decisions
SQLite instead of PostgreSQL: a bot with a handful of users doesn't justify a database server, and a single file makes backups trivial. The local model adds latency compared with a cloud API, but it removes the per-message cost and keeps the conversations at home.
What I'd do differently
I'd research the models before building around one. I switched models six times in two months: from Qwen 2.5 to Qwen 3, to Salamandra 2B and 7B, back to 2B because the 7B ate the server, and back to Qwen after properly testing the options. Every switch shifted the tone, length and cost of the replies. Measuring quality and resource usage on my own hardware first would have settled the choice in an afternoon.