R1999 Code Bot watches public Reverse: 1999 redemption-code sources, extracts likely codes, deduplicates them in SQLite, and publishes new-code messages to Bluesky. It is built for long-running automated operation, but it can also run in dry-run mode for local verification.
If you only want regular code notifications, you do not need to host this bot. Follow the long-running Bluesky account instead: https://bsky.app/profile/r1999-code-kanban.bsky.social
public sources
├─ Nitter RSS: GL / JP active source
├─ Facebook FetchRSS: TW active source
├─ Bahamut fixed redemption-code thread: TW / JP / GL fallback
├─ Wikiru JP redemption-code page: JP supplement
├─ Fandom Promotional Code page: GL supplement
└─ Pocket Tactics codes page: GL attributable-guide supplement
↓
normalization → coarse filtering → deterministic parsers / optional LLM extraction
↓
SQLite deduplication and resume state
↓
Bluesky formatter and publisher
Source health is source-aware. A Wikiru, Fandom, or Pocket Tactics outage is recorded as supplemental-source health, not as an official server failure. A server is considered failed only when both its active source and the relevant Bahamut fallback path are unavailable.
Requirements: Python 3.12+, SQLite through Python sqlite3, and the packages in requirements.txt.
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
cp .env.example .env
python -m pytest -q
PUBLISH_DRY_RUN=true python main.pyThe app reads process environment variables. It does not automatically load .env; load it with your shell, process manager, Docker, or another supervisor before starting Python.
All deployment-specific values belong in environment variables. Do not commit local .env files, databases, logs, or real credentials.
Common settings:
| Variable | Purpose |
|---|---|
DB_PATH |
SQLite database path; default is data/r1999_codes.db. |
NITTER_PRIMARY_BASE_URL / NITTER_FALLBACK_BASE_URLS |
Nitter RSS base URLs for GL / JP collection. |
FACEBOOK_FETCHRSS_FEED_URL |
FetchRSS feed URL for the TW Facebook source. |
BAHAMUT_THREAD_URL |
Fixed Bahamut redemption-code thread URL (display thread only; the Jina Reader path targets the canonical Co single-floor route). |
BAHAMUT_FETCH_PROVIDER |
jina_reader (default, raw HTML) or desktop_html (legacy rollback). |
BAHAMUT_JINA_MAX_BODY_BYTES |
Bounded Jina response cap; bodies above the cap fail closed (default 1048576). |
BAHAMUT_JINA_READER_BASE_URL |
Jina Reader base URL (default https://r.jina.ai). |
WIKIRU_PAGE_URL |
JP Wikiru redemption-code page. |
FANDOM_GL_PAGE_URL / FANDOM_GL_API_URL |
GL Fandom page / MediaWiki API source. |
POCKETTACTICS_URL / ENABLE_POCKETTACTICS |
GL Pocket Tactics attributable-guide source and enable flag. |
LLM_PRIMARY_* / LLM_FALLBACK_* |
Optional OpenAI-compatible extraction models. |
PUBLISH_DRY_RUN |
Keep true until live posting is intentionally enabled. |
BLUESKY_HANDLE / BLUESKY_APP_PASSWORD |
Required only for live Bluesky publishing. |
CYCLE_INTERVAL_HOURS |
Scheduler interval; default is 6 hours. |
For live posting, set PUBLISH_DRY_RUN=false only after the database, logs, and formatted output have been checked.
Bahamut is fetched credential-free through Jina Reader using the canonical Co.php?bsn=75028&sn=13937 first-floor route. The bot validates the expected DOM identity and parses only that floor.
- Trust boundary: Jina Reader is the upstream trust root. The bot does not verify an origin signature and cannot detect content injected inside the accepted article.
- Fail-closed: Invalid DOM identity, challenge pages, transport failures, malformed checkpoint state, and responses above
BAHAMUT_JINA_MAX_BODY_BYTESdo not advance the checkpoint. - Upgrade: Keep the existing v1.2.1 database. The first successful parse absorbs the current code snapshot as a semantic baseline without publishing historical codes.
- Verification and rollback: Review one dry-run checkpoint after deployment. Use
BAHAMUT_FETCH_PROVIDER=desktop_htmlfor degraded rollback orENABLE_BAHAMUT=falseto disable the source.
Run the offline default test suite. Live external integration tests are excluded and no network request is made:
python -m pytest -qRun the two real LLM integration tests only when the required credentials and network access are intentionally available:
python -m pytest tests/test_integration.py -m integration -vRun one scheduled bot process in dry-run mode:
PUBLISH_DRY_RUN=true python main.pydocker build -t r1999-code-bot .
mkdir -p ./data
docker run --rm \
--env-file .env \
-e DB_PATH=/app/data/r1999_codes.db \
-v "$(pwd)/data:/app/data" \
r1999-code-botKeep PUBLISH_DRY_RUN=true for first-run validation. Use a persistent mounted data directory for the SQLite database.
| Path | Purpose |
|---|---|
main.py, main_policy.py |
Scheduler, pipeline orchestration, health and checkpoint policy. |
collectors/ |
Nitter, Facebook, Bahamut, Wikiru, Fandom, and Pocket Tactics collectors and deterministic parsers. |
filters/ |
Multilingual coarse-filter signals. |
llm/ |
Optional OpenAI-compatible fine-filter extraction and batching. |
publisher/ |
Bluesky formatting and publishing. |
storage/ |
SQLite schema and database access. |
docs/ |
Deployment notes. |
- Keep
.env, local databases, logs, caches, generated support bundles, and real credentials out of git. - Use Bluesky App Passwords for live publishing; do not store the account password.
- Treat FetchRSS feed URLs, LLM endpoints, model names, API keys, and Bluesky credentials as deployment-specific values.
.env.examplemust stay placeholder-only and safe to publish.
Thanks to the public Bahamut thread maintainers, wiki contributors, Pocket Tactics, and the Nitter project for public source availability that makes cross-source validation possible.
MIT. See LICENSE.