# Freebee A full-featured Matrix community bot with end-to-end encryption support, a plugin architecture, silent passive tracking, daily scheduled posts, and a suite of community engagement features. Built for the TwinBee/Parodius community. Runs on Node.js with TypeScript, SQLite, and `matrix-js-sdk` with automatic Rust E2EE. --- ## Table of Contents - [Features](#features) - [Requirements](#requirements) - [Installation](#installation) - [Configuration](#configuration) - [Running the Bot](#running-the-bot) - [Commands](#commands) - [Passive Features](#passive-features) - [Scheduled Posts](#scheduled-posts) - [Achievements](#achievements) - [Personality Archetypes](#personality-archetypes) - [Supported Timezones](#supported-timezones) - [External APIs](#external-apis) - [Architecture](#architecture) - [Database](#database) - [Troubleshooting](#troubleshooting) - [Backup & Recovery](#backup--recovery) --- ## Features - **End-to-end encryption** via `matrix-js-sdk` with Rust crypto — automatic key management, session rotation, and key gossiping out of the box - **Automatic token renewal** — set `MATRIX_BOT_PASSWORD` and the bot refreshes expired access tokens on its own - **Crypto reset flow** — set `CRYPTO_RESET=true` to wipe and re-establish E2EE keys in one restart, with automatic server-side device cleanup - **Plugin architecture** — modular design with 32+ independent plugins - **Silent passive tracking** — XP, streaks, stats, achievements, keyword alerts, Markov corpus, and presence tracked without announcements - **Scheduled daily posts** — Word of the Day, holidays, game releases, birthdays, anime airings, movie releases, and weekly concert digests - **Multi-calendar holiday support** — Gregorian, Hebrew, Hijri, and Asian calendars (JP, CN, KR, IN, TH, VN, TW, PH) with non-Western holiday featuring - **XP & leveling system** with cooldown-based anti-farming - **Reputation system** with natural language thanks detection (LLM-powered sarcasm-aware mode when Ollama is active) - **32 unlockable achievements** evaluated silently on every message - **Trivia system** with OpenTDB integration, time-weighted scoring, and threaded conversations - **Game backlog manager**, now-playing status, and HowLongToBeat integration - **Stock quotes** via Finnhub with watchlists - **Anime tracking** via MyAnimeList/Jikan with watchlists and airing alerts - **Movie & TV tracking** via TMDB with watchlists and release alerts - **Concert tracking** via Bandsintown with artist watchlists and weekly digests - **LLM-powered passive classifier** (optional, via Ollama) — multilingual profanity, insult, sentiment, gratitude, and WOTD correctness detection with emoji reactions and robust JSON repair - **URL preview enrichment** — automatic og:tag extraction for shared links - **Markov chain text generation** — per-user trigram models for chaos - **Lookup tools** — Wikipedia, dictionary, and translation (LibreTranslate) - **Presence system** — away/afk/back status with auto-clear and `!whois` profile cards - **Community countdowns** — public and private event countdowns - **Welcome system** — greets new users with bonus XP and provides `!help` via DM - **Reminder system** with natural language time parsing - **Reaction polls**, dice rolling, world clock, weather (with geocoding for ambiguous cities and zip codes), and more - **Inline calculator** — math expressions, percentages, unit conversions via mathjs - **QR code generator** — generates and posts QR code images to the room - **Reaction leaderboard** — silently tracks emoji reactions, revealed with `!emojiboard` - **Per-command rate limiting** for paid API endpoints --- ## Requirements - **Node.js 22+** - **npm** (comes with Node.js) - A Matrix account for the bot with an access token or password --- ## Installation ### From Source ```bash git clone https://github.com/your-org/freebee.git cd freebee npm install npm run build ``` ### With Docker ```bash git clone https://github.com/your-org/freebee.git cd freebee cp .env.example .env # Edit .env with your configuration docker compose up -d ``` --- ## Configuration Copy `.env.example` to `.env` and fill in the values: ```bash cp .env.example .env ``` ### Required Variables | Variable | Description | |---|---| | `MATRIX_HOMESERVER_URL` | Your Matrix homeserver (e.g., `https://matrix.example.com`) | | `MATRIX_BOT_USER_ID` | Bot's full user ID (e.g., `@freebee:example.com`) | | `MATRIX_ACCESS_TOKEN` | Bot's access token (or set `MATRIX_BOT_PASSWORD` instead) | ### Authentication You can authenticate the bot in two ways: 1. **Access token only** — set `MATRIX_ACCESS_TOKEN`. If it expires, the bot exits. 2. **Password (recommended)** — set `MATRIX_BOT_PASSWORD`. The bot validates the token on startup and automatically obtains a new one via password login if it's expired or missing. The new token is persisted to `.env`. Both can be set simultaneously for maximum resilience. ### Recommended Variables | Variable | Default | Description | |---|---|---| | `MATRIX_BOT_PASSWORD` | *(empty)* | Bot account password for automatic token renewal | | `BOT_ROOMS` | *(empty)* | Comma-separated room IDs for scheduled posts | | `BOT_ADMIN_USERS` | *(empty)* | Comma-separated admin user IDs | | `BOT_PREFIX` | `!` | Command prefix | | `DATA_DIR` | `./data` | Directory for database, crypto store, and logs | | `LOG_LEVEL` | `info` | Logging level (`debug`, `info`, `warn`, `error`) | | `BOT_DEFAULT_CITY` | *(empty)* | Default city for concert digests and location features | ### API Keys | Variable | Required For | How to Get | |---|---|---| | `RAWG_API_KEY` | `!releases`, daily game releases post | [rawg.io/apidocs](https://rawg.io/apidocs) (free) | | `WORDNIK_API_KEY` | Word of the Day post | [developer.wordnik.com](https://developer.wordnik.com/) (free) | | `CALENDARIFIC_API_KEY` | Holiday data (Western + Asian holidays) | [calendarific.com](https://calendarific.com/api-documentation) (free tier) | | `OPENWEATHER_API_KEY` | `!weather` command | [openweathermap.org](https://openweathermap.org/api) (free tier) | | `FINNHUB_API_KEY` | `!stock` command | [finnhub.io](https://finnhub.io/) (free, 60 calls/min) | | `BANDSINTOWN_API_KEY` | `!concerts` command | [bandsintown.com](https://artists.bandsintown.com/) (free) | | `TMDB_API_KEY` | `!movie`, `!tv` commands | [themoviedb.org](https://www.themoviedb.org/documentation/api) (free) | HebCal (Jewish calendar), Aladhan (Islamic calendar), Jikan/MAL (anime), OpenTDB (trivia), Wikipedia, and Free Dictionary are free and require no API keys. ### Optional: Self-hosted Services | Variable | Default | Description | |---|---|---| | `LIBRETRANSLATE_URL` | *(empty)* | URL for self-hosted LibreTranslate instance (e.g., `http://localhost:5000`) | | `OLLAMA_HOST` | *(empty)* | Ollama endpoint for LLM classifier (e.g., `http://localhost:11434`) | | `OLLAMA_MODEL` | *(empty)* | Ollama model name (e.g., `qwen2.5:1.5b`) | | `LLM_SAMPLE_RATE` | `0.15` | Fraction of non-keyword messages randomly sampled for LLM classification (0.0–1.0) | | `ASIAN_HOLIDAY_COUNTRIES` | `JP,CN,KR,IN,TH,VN,TW,PH` | Comma-separated country codes for Asian holiday data | LLM features (profanity/insult/sentiment/gratitude detection, smart WOTD validation) are **only active when both `OLLAMA_HOST` and `OLLAMA_MODEL` are set**. Leave them blank to disable. The classifier uses a 16k context window, a 30-second request timeout, and exponential backoff (5s–5min) when Ollama is unreachable. When active, the LLM replaces keyword-based thanks detection with sarcasm-aware gratitude detection for the reputation system. ### Rate Limits | Variable | Default | Description | |---|---|---| | `RATELIMIT_WEATHER` | `5` | Max `!weather` calls per user per day | | `RATELIMIT_TRANSLATE` | `20` | Max `!translate` calls per user per day | | `RATELIMIT_CONCERTS` | `10` | Max `!concerts` calls per user per day | Admins (`BOT_ADMIN_USERS`) bypass all rate limits. Set to `0` for unlimited. ### Feature Flags | Variable | Default | Description | |---|---|---| | `FEATURE_URL_PREVIEW` | `true` | Enable automatic URL og:tag previews | | `FEATURE_SHADE` | `false` | Enable shade plugin (stub) | | `CRYPTO_RESET` | `false` | Set to `true` to wipe and re-establish E2EE keys on next startup (auto-deletes old device from server) | ### Scheduler Times All times are in **24-hour UTC format**. | Variable | Default | Description | |---|---|---| | `SCHEDULE_HOLIDAYS_HOUR` | `7` | Hour for holiday post | | `SCHEDULE_HOLIDAYS_MINUTE` | `0` | Minute for holiday post | | `SCHEDULE_RELEASES_HOUR` | `19` | Hour for game releases post | | `SCHEDULE_RELEASES_MINUTE` | `0` | Minute for game releases post | Additional scheduled jobs with DB-configured defaults: | Job | Default Time | Description | |---|---|---| | `wotd` | 08:00 | Word of the Day | | `birthday_check` | 07:05 | Birthday announcements | | `anime_releases` | 19:30 | Anime airing alerts | | `movie_releases` | 20:00 | Movie/TV release alerts | | `concert_digest` | 10:00 | Weekly concert digest (Sundays only) | All schedule times can be adjusted at runtime with `!schedule `. ### Getting a Matrix Access Token 1. Log into your bot's Matrix account via Element or another client 2. Go to **Settings > Help & About > Advanced** and copy the access token 3. Alternatively, use the Matrix client-server API: ```bash curl -X POST "https://your-homeserver/_matrix/client/v3/login" \ -H "Content-Type: application/json" \ -d '{"type":"m.login.password","user":"freebee","password":"your-password"}' ``` 4. Copy the `access_token` from the response Or just set `MATRIX_BOT_PASSWORD` and let the bot handle token management automatically. --- ## Running the Bot ### Development ```bash npm run dev ``` ### Production ```bash npm run build npm start ``` ### Docker ```bash docker compose up -d # View logs docker compose logs -f freebee # Restart docker compose restart ``` The bot will auto-join rooms when invited. --- ## Commands All commands use the configured prefix (default: `!`). Use `!help` to get a full command list sent to your DMs. ### XP & Leveling | Command | Description | |---|---| | `!rank [@user]` | Show XP, level, and progress bar | | `!leaderboard [n]` | Top N users by XP (default 10, max 25) | XP is earned passively: **10 XP per message** with a 30-second cooldown to prevent farming. The level curve is linear: level N requires N x 100 total XP. ### Reputation | Command | Description | |---|---| | `!rep [@user]` | Show reputation score | | `!repboard` | Top 10 by reputation | Reputation is granted automatically when the bot detects natural "thanks" messages directed at another user (e.g., "thanks @user:server.com", "ty @user:server.com"). There's a 24-hour cooldown per giver/receiver pair. The bot reacts with a checkmark to acknowledge. When the LLM classifier is active, keyword-based detection is replaced with sarcasm-aware gratitude detection — sarcastic thanks like "thanks for nothing" won't grant rep. Receiving reputation also grants **5 bonus XP**. ### Statistics | Command | Description | |---|---| | `!stats [@user]` | Full message metrics (words, links, images, questions, etc.) | | `!rankings [category]` | Leaderboard by category | | `!personality [@user]` | Your community archetype | Valid ranking categories: `messages`, `words`, `links`, `images`, `questions`, `emojis`, `streak` ### Streaks | Command | Description | |---|---| | `!streak [@user]` | Current and record streak (in days) | | `!firstboard` | Early bird leaderboard (who posts first each day) | ### Reminders | Command | Description | |---|---| | `!remindme