diff --git a/.env.example b/.env.example index eb3d937..366674d 100644 --- a/.env.example +++ b/.env.example @@ -1,57 +1,39 @@ -# Matrix -MATRIX_HOMESERVER_URL=https://matrix.example.com -MATRIX_ACCESS_TOKEN=syt_your_token_here -MATRIX_BOT_USER_ID=@freebee:example.com -MATRIX_BOT_PASSWORD= +# ---- Matrix Connection ---- +HOMESERVER_URL=https://matrix.example.com +BOT_USER_ID=@gogobee:example.com +BOT_PASSWORD=your_password_here +BOT_DISPLAY_NAME=GogoBee -# Which rooms the bot actively posts scheduled content to (comma-separated) -BOT_ROOMS=!roomid:example.com +# Which rooms the bot posts scheduled content to (comma-separated room IDs) +BROADCAST_ROOMS=!roomid:example.com -# Admins who can run admin-only commands -BOT_ADMIN_USERS=@yourmxid:example.com +# Admins who can run admin-only commands (comma-separated Matrix user IDs) +ADMIN_USERS=@yourmxid:example.com -# Runtime -BOT_PREFIX=! +# ---- Runtime ---- DATA_DIR=./data LOG_LEVEL=info -# External APIs +# ---- External API Keys (all optional) ---- RAWG_API_KEY= WORDNIK_API_KEY= CALENDARIFIC_API_KEY= -ASIAN_HOLIDAY_COUNTRIES=JP,CN,KR,IN,TH,VN,TW,PH -HEBCAL_API_KEY= +HOLIDAY_COUNTRIES=US,GB,CA,AU,PT,IN,JP,DE,FR,BR,MX,IT,ES,KR,NL,SE,NO,IE,NZ,ZA,PH OPENWEATHER_API_KEY= FINNHUB_API_KEY= BANDSINTOWN_API_KEY= TMDB_API_KEY= + +# ---- Self-Hosted Services (optional) ---- LIBRETRANSLATE_URL= - -# Scheduler times (24h UTC) -SCHEDULE_HOLIDAYS_HOUR=7 -SCHEDULE_HOLIDAYS_MINUTE=0 -SCHEDULE_RELEASES_HOUR=19 -SCHEDULE_RELEASES_MINUTE=0 - -# Feature flags -FEATURE_SHADE=false -FEATURE_URL_PREVIEW=true - -# Crypto reset (set to true to wipe and re-establish E2EE keys on next startup) -CRYPTO_RESET=false - -# LLM passive classifier (requires Ollama — leave blank to disable) OLLAMA_HOST= OLLAMA_MODEL= -LLM_SAMPLE_RATE=0.15 +LLM_SAMPLE_RATE=0.15 # 0.0-1.0, fraction of non-keyword messages to classify (1.0 = all) -# Rate limits (calls per user per day, 0 = unlimited) -RATELIMIT_WEATHER=5 +# ---- Feature Flags ---- +FEATURE_URL_PREVIEW= +FEATURE_SHADE= +FEATURE_TRIVIA=true # set to "false" to disable trivia + +# ---- Rate Limits (per user per day) ---- RATELIMIT_TRANSLATE=20 -RATELIMIT_CONCERTS=10 - -# Bot default city for concert digest and location-based features -BOT_DEFAULT_CITY= - -# Trivia -TRIVIA_TIMEOUT_SECONDS=20 diff --git a/.gitignore b/.gitignore index 4f0bb48..036cb99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ -dist/ data/ .env .env.* !.env.example -node_modules/ -merge_env.py -Twinbee Build Plan.md +gogobee diff --git a/Dockerfile b/Dockerfile index 32b9a15..adc3c8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,18 @@ -FROM node:20-alpine +FROM golang:1.24-alpine AS builder WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download -COPY package.json package-lock.json* ./ -RUN npm ci --omit=dev +COPY . . +RUN CGO_ENABLED=0 go build -tags goolm -o gogobee . -COPY tsconfig.json ./ -COPY src/ ./src/ -RUN npx tsc +FROM alpine:3.21 +RUN apk add --no-cache ca-certificates tzdata +WORKDIR /app +COPY --from=builder /app/gogobee . -CMD ["node", "dist/index.js"] +VOLUME /app/data +ENV DATA_DIR=/app/data + +CMD ["./gogobee"] diff --git a/README.md b/README.md index 114901e..755dc63 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Freebee +# GogoBee -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. +Matrix community bot with E2EE, 35+ plugins, passive tracking, scheduled posts, and optional LLM features. -Runs on Node.js with TypeScript, SQLite, and `matrix-js-sdk` with automatic Rust E2EE. +Written in Go using [mautrix-go](https://github.com/mautrix/go) for encryption and [modernc.org/sqlite](https://modernc.org/sqlite) for storage. Successor to the TypeScript "Freebee" bot. --- @@ -18,58 +18,33 @@ Runs on Node.js with TypeScript, SQLite, and `matrix-js-sdk` with automatic Rust - [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 35+ independent plugins -- **Silent passive tracking** — 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 and Twinbee/Parodius-themed level-up announcements -- **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 -- **`!howami` roast command** — LLM generates a personalized roast based on your actual stats, achievements, and behavior patterns -- **`!vibe` room energy check** — LLM reads recent messages and describes the current chat mood -- **`!tldr` conversation summary** — catch up on what you missed with an LLM-generated summary of recent messages -- **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 -- **Weather system** — current conditions via OpenWeatherMap with intelligent geocoding: city names, zip/postal codes (with optional country suffix), `@user` timezone-based lookup, US-preferred disambiguation for ambiguous cities, dual °C/°F display, feels-like temperature, humidity, and wind speed -- **Reaction polls**, dice rolling, world clock, 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 +- **E2EE that actually works** - mautrix-go with goolm (pure Go). Crypto state lives in SQLite so device keys survive restarts. Cross-signing bootstraps on first run. Verify once, done. +- **No CGo, no system deps** - builds to a single static binary. Cross-compile to whatever you want. +- **35+ plugins** with dependency injection and ordered registration +- **Passive tracking** - XP, stats, streaks, achievements, markov corpus, keyword alerts, all running silently +- **Scheduled posts** via [robfig/cron](https://github.com/robfig/cron) - WOTD, holidays, game releases, birthdays, anime/movie releases, concert digests +- **LLM integration** (optional) - Ollama-powered sentiment analysis, roast profiles, room vibes, conversation summaries +- **SQLite everything** - one file, no external database needed --- ## Requirements -- **Node.js 22+** -- **npm** (comes with Node.js) -- A Matrix account for the bot with an access token or password +- Go 1.22+ +- A Matrix homeserver account for the bot + +Optional: +- [Ollama](https://ollama.ai) for LLM features +- API keys for various services (see [Configuration](#configuration)) --- @@ -78,19 +53,23 @@ Runs on Node.js with TypeScript, SQLite, and `matrix-js-sdk` with automatic Rust ### From Source ```bash -git clone https://github.com/your-org/freebee.git -cd freebee -npm install -npm run build +git clone https://github.com/prosolis/gogobee.git +cd gogobee +cp .env.example .env # edit with your settings +go build -tags goolm -o gogobee . +./gogobee ``` -### With Docker +### Docker + +```bash +docker build -t gogobee . +docker run --env-file .env -v ./data:/app/data gogobee +``` + +### Docker Compose ```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 ``` @@ -98,930 +77,438 @@ docker compose up -d ## Configuration -Copy `.env.example` to `.env` and fill in the values: +Everything is configured through environment variables or a `.env` file. -```bash -cp .env.example .env -``` - -### Required Variables +### Required | 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) | +|----------|-------------| +| `HOMESERVER_URL` | Matrix homeserver URL, e.g. `https://matrix.org` | +| `BOT_USER_ID` | Bot's Matrix user ID, e.g. `@gogobee:matrix.org` | +| `BOT_PASSWORD` | Bot's Matrix password | -### 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 +### Core (optional) | 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 | +|----------|---------|-------------| +| `DATA_DIR` | `./data` | Where the database and device files live | +| `BOT_DISPLAY_NAME` | `GogoBee` | Display name | +| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` | +| `ADMIN_USERS` | | Comma-separated admin user IDs | +| `BROADCAST_ROOMS` | | Comma-separated room IDs for scheduled posts | -### API Keys +### API Keys (optional) -| 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) | +| Variable | Service | Used By | +|----------|---------|---------| +| `RAWG_API_KEY` | [RAWG](https://rawg.io/apidocs) | `!game`, `!retro`, `!releases` | +| `WORDNIK_API_KEY` | [Wordnik](https://developer.wordnik.com) | Word of the Day | +| `CALENDARIFIC_API_KEY` | [Calendarific](https://calendarific.com) | Holiday posts | +| `OPENWEATHER_API_KEY` | [OpenWeather](https://openweathermap.org/api) | `!weather` | +| `FINNHUB_API_KEY` | [Finnhub](https://finnhub.io) | `!stock` | +| `BANDSINTOWN_API_KEY` | [Bandsintown](https://artists.bandsintown.com) | `!concerts` | +| `TMDB_API_KEY` | [TMDB](https://www.themoviedb.org/documentation/api) | `!movie`, `!tv`, `!upcoming` | -HebCal (Jewish calendar), Aladhan (Islamic calendar), Jikan/MAL (anime), OpenTDB (trivia), Wikipedia, and Free Dictionary are free and require no API keys. +### Services (optional) -### Optional: Self-hosted Services +| Variable | Description | +|----------|-------------| +| `OLLAMA_HOST` | Ollama server URL, e.g. `http://localhost:11434` | +| `OLLAMA_MODEL` | Model name, e.g. `llama3.2` | +| `LIBRETRANSLATE_URL` | LibreTranslate instance for `!translate` | -| 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 | +### Feature Flags -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. +| Variable | Description | +|----------|-------------| +| `FEATURE_URL_PREVIEW` | Set to anything to enable automatic URL previews | +| `FEATURE_SHADE` | Set to anything to enable the shade plugin (stub) | ### 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. +|----------|---------|-------------| +| `RATELIMIT_TRANSLATE` | `20` | Daily translation limit per user | --- ## Running the Bot -### Development - ```bash -npm run dev +# dev +go run -tags goolm . + +# prod +go build -tags goolm -o gogobee . +./gogobee ``` -### Production +The `-tags goolm` flag selects the pure-Go crypto implementation. No C compiler or libolm needed. -```bash -npm run build -npm start -``` +### First Run -### 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. +1. Start the bot. It logs in, creates a device, and sets up cross-signing automatically. +2. Verify the bot's device from your main Matrix account (Element, etc). +3. That's it. E2EE works across restarts from here on out. --- ## 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. When a user levels up, the bot announces it with a random Twinbee/Parodius-themed power-up message (bell combos, laser modes, shield activations, etc.). +|---------|-------------| +| `!rank` | Your level, XP, and progress | +| `!leaderboard` | Top 10 by XP | ### Reputation - | Command | Description | -|---|---| -| `!rep [@user]` | Show reputation score | -| `!repboard` | Top 10 by reputation | +|---------|-------------| +| `!rep [@user]` | Reputation count | +| `!repboard` | Top 10 by rep | -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 +Rep is earned when someone thanks you. The bot detects this automatically. +### Stats & Personality | 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` +|---------|-------------| +| `!stats [@user]` | Message statistics | +| `!rankings [category]` | Rankings by words, links, questions, or emojis | +| `!personality` | Your community archetype | ### Streaks - | Command | Description | -|---|---| -| `!streak [@user]` | Current and record streak (in days) | -| `!firstboard` | Early bird leaderboard (who posts first each day) | - -### Reminders - -| Command | Description | -|---|---| -| `!remindme