Files
Melora/README.md
Claude aaacd0b9bf Implement Melora: *arr media arrival webhook announcer for Matrix
Replace Bellhop scaffolding with full Melora implementation:
- FastAPI webhook endpoints for Radarr, Sonarr, and Lidarr
- matrix-nio integration with threaded room posting
- SQLite for thread root persistence and event deduplication
- Message formatting with plain text and HTML for each media type
- Shared secret authentication via X-Arr-Webhook-Secret header
- Updated dependencies, configuration, and documentation

https://claude.ai/code/session_01DuzWyMMXvLMB4VxEwJyV4X
2026-02-28 07:38:07 +00:00

4.0 KiB

Melora

A webhook receiver that listens for media import events from Radarr, Sonarr, and Lidarr and announces new arrivals to a Matrix room via a bot. Each media type posts into its own persistent thread, keeping the room tidy.

Architecture

Radarr ─┐
Sonarr ──┼─→ POST webhook → Melora (FastAPI) → Matrix room (threaded)
Lidarr ─┘

No polling. All three *arr instances push events to Melora via their built-in webhook/Connect system.

Requirements

  • Python 3.12+
  • A Matrix homeserver with an unencrypted room and a bot account
  • Radarr, Sonarr, and/or Lidarr instances configured to send webhooks

Quick Start

1. Clone and configure

git clone https://github.com/prosolis/Melora.git
cd Melora
cp .env.example .env

Edit .env with your actual values (see Environment Variables below).

2. Run locally

pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000

3. Run with Docker

docker build -t melora .
docker run -d \
  --name melora \
  --env-file .env \
  -p 8000:8000 \
  -v melora-data:/app \
  melora

Environment Variables

Variable Required Default Description
MATRIX_HOMESERVER_URL Yes Base URL of your Matrix homeserver
MATRIX_BOT_USER_ID Yes Bot's Matrix user ID (e.g. @melora-bot:example.com)
MATRIX_BOT_ACCESS_TOKEN Yes Pre-authenticated access token for the bot
MATRIX_ARRIVALS_ROOM_ID Yes Room ID for arrival announcements (e.g. !abc123:example.com)
WEBHOOK_SECRET Yes Shared secret for *arr webhook authentication
DATABASE_PATH No melora.db Path to the SQLite database file

*arr Configuration

In each *arr instance, go to Settings → Connect → Add → Webhook and configure:

  • URL: http://melora-host:8000/webhook/radarr (or /sonarr, /lidarr)
  • Method: POST
  • Events: Enable On Import (and On Upgrade if desired)
  • Tags: Leave blank to capture all imports
  • Headers: Add X-Arr-Webhook-Secret with the same value as WEBHOOK_SECRET

Webhook Endpoints

POST /webhook/radarr
POST /webhook/sonarr
POST /webhook/lidarr
GET  /health

Each webhook endpoint validates the X-Arr-Webhook-Secret header, processes only Download events, and posts to the appropriate Matrix thread.

Matrix Room Structure

On first startup, Melora creates three thread root messages in the configured room. All subsequent announcements reply into the appropriate thread.

#new-arrivals:your.domain
  ├── 🎬 Movies    ← Radarr imports
  ├── 📺 Shows     ← Sonarr imports
  └── 🎵 Music     ← Lidarr imports

Thread root event_id values are stored in SQLite, so threads persist across restarts.

Message Format

Messages include both plain text and HTML (Matrix-flavored Markdown). New additions and quality upgrades are distinguished:

New movie:

🎬 The Substance (2024)
  ✅ New addition
  🎞️ Quality: Bluray-1080p

Quality upgrade:

🎬 The Substance (2024)
  ⬆️ Quality upgrade
  🎞️ Quality: Bluray-2160p

Project Structure

Melora/
├── app/
│   ├── __init__.py
│   ├── main.py          # FastAPI app, lifespan, startup
│   ├── config.py        # Environment variable loading
│   ├── database.py      # Async SQLite for thread roots and dedup
│   ├── matrix.py        # matrix-nio posting and thread management
│   ├── formatters.py    # Message formatting for each media type
│   └── webhooks.py      # Webhook route handlers
├── Dockerfile
├── requirements.txt
├── .env.example
└── README.md

Error Handling

  • Unknown or malformed payloads return 200 (prevents *arr retry storms)
  • Parsing and Matrix posting errors are logged but don't crash the service
  • Missing thread roots on startup halt with a clear error

License

See repository for license details.