https://claude.ai/code/session_01B7YPGrE3NatkwadCXVjv2j
Pastel
A Matrix bot that posts gaming deals and free game alerts to a specified Matrix room.
Deals are sourced from PC/digital storefronts only (Steam, GOG, Humble Store, GreenManGaming, Epic Games Store) — universally accessible regardless of region.
Data Sources
- CheapShark — polled every 2 hours for top deals across Steam, GOG, Humble Store, and GreenManGaming
- IsThereAnyDeal — polled every 2 hours for deals across all tracked stores, with built-in historical low detection (requires API key)
- Epic Games Store — polled daily for free game promotions
CheapShark and IsThereAnyDeal can be used individually or together — configure via the DEAL_SOURCES variable. When used as a deal source, IsThereAnyDeal provides historical low flags directly. When only CheapShark is active, IsThereAnyDeal can still optionally enrich deals with historical low info via the ITAD_API_KEY.
Quick Start
- Copy
.env.exampleto.envand fill in your Matrix credentials and room ID - Run with Docker:
docker build -t pastel .
docker run --env-file .env -v pastel-data:/data pastel
Or run directly with Python 3.12+:
pip install -r requirements.txt
python -m gaming_deals_bot
Obtaining a Matrix Bot Access Token
-
Create a bot account on your homeserver (e.g. via Element: register a new account like
@dealsbot:example.com). -
Log in and get the access token using
curl:curl -XPOST "https://matrix.example.com/_matrix/client/v3/login" \ -H "Content-Type: application/json" \ -d '{ "type": "m.login.password", "identifier": { "type": "m.id.user", "user": "dealsbot" }, "password": "YOUR_PASSWORD" }'The response will contain an
access_tokenfield — copy that value into your.envasMATRIX_BOT_ACCESS_TOKEN. -
Invite the bot to your deals room, then have the bot accept the invite (the bot does this automatically on startup).
Tip: After extracting the token you can change the bot account's password without invalidating the token. Store the token securely — anyone with it can act as the bot.
Configuration
All configuration is via environment variables (see .env.example):
| Variable | Required | Default | Description |
|---|---|---|---|
MATRIX_HOMESERVER_URL |
Yes | — | Matrix homeserver URL |
MATRIX_BOT_USER_ID |
Yes | — | Bot's Matrix user ID |
MATRIX_BOT_ACCESS_TOKEN |
Yes | — | Bot's access token |
MATRIX_DEALS_ROOM_ID |
Yes | — | Room ID to post deals in |
ITAD_API_KEY |
No | — | IsThereAnyDeal API key (required when itad is in DEAL_SOURCES, optional otherwise for historical low detection) |
DEAL_SOURCES |
No | cheapshark | Comma-separated deal sources: cheapshark, itad, or cheapshark,itad |
MIN_DEAL_RATING |
No | 8.0 | Minimum CheapShark deal rating (0-10) |
MIN_DISCOUNT_PERCENT |
No | 50 | Minimum discount percentage |
MAX_PRICE_USD |
No | 20 | Maximum sale price in USD |
SEND_INTRO_MESSAGE |
No | false | Send "The deals must flow." to the room on startup |
DATABASE_PATH |
No | deals.db | Path to SQLite database file |
Preflight Check
Run --check to validate your configuration and test connectivity to all services before starting the bot:
python -m gaming_deals_bot --check
This verifies:
- Matrix — authentication token is valid and bot has joined the target room
- CheapShark — API is reachable (skipped if not in
DEAL_SOURCES) - Epic Games Store — API is reachable
- Frankfurter — exchange rate API is reachable
- IsThereAnyDeal — API key is valid (required when
itadis inDEAL_SOURCES, skipped otherwise if not configured)
The command exits with code 0 on success and 1 on failure, so it works in CI and Docker health-checks.
Behavior
- First run: fetches current deals and records them in the database without posting (avoids spamming the room with existing deals)
- Deduplication: deals are tracked by game ID + timestamp; duplicates are never reposted
- Pruning: deals older than 30 days are pruned from the database so they can be reposted if they return
- One message per deal: each deal is posted individually so messages are independently linkable and dismissible
- Multi-currency pricing: deal prices are shown in USD, CAD, EUR, and GBP using live exchange rates from the Frankfurter API (ECB data, no API key required). Rates are cached and refreshed twice daily.