No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-25 17:43:45 -04:00
.gitignore Initial commit of spawn-bot. 2026-08-25 10:31:47 -04:00
config.example.json Initial commit of spawn-bot. 2026-08-25 10:31:47 -04:00
README.md Fix same boss multi spawn message. 2026-08-25 17:43:45 -04:00
spawn-bot.service Initial commit of spawn-bot. 2026-08-25 10:31:47 -04:00
spawn_bot.py Fix same boss multi spawn message. 2026-08-25 17:43:45 -04:00

spawn_bot

Watches DemonicScans active_wave pages and posts boss alerts to Discord. Two kinds of message:

  • 🔥 SPAWNED — fires the instant a boss goes live in the wave, with a direct JOIN THE BATTLE link to its boss page (battle.php?id=<id>) so users click straight into the fight. Deduped by battle id: one alert per boss instance, suppressed for as long as that instance is listed (the card's data-expire lifetime — 2h to 48h depending on the boss). A respawn carries a new id, so it alerts again.
  • 👑 upcoming (optional, notify_upcoming) — advance " spawns in Xh" heads-up for bosses whose respawn timer is still in the future. Deduped by wave + boss + spawn-timestamp, so a new respawn cycle re-alerts.

Trash mobs are ignored. Upcoming alerts read the page's "Auto Summon Timers" section. For the SPAWNED alert the boss is identified in the monster grid structurally — a lone card (trash spawn in groups) whose max HP dwarfs every other card — because a boss's grid name/image can differ from its timer entry (e.g. a boss listed as one "form" in the timer fights under a different name in the grid), so name matching is unreliable.

About the "button"

Your webhook is a plain channel incoming webhook (application_id: null), and Discord only allows interactive button components on application-owned webhooks. So the join action is a prominent clickable link in the embed (title + a large "⚔️ JOIN THE BATTLE ▶" line), which drops users straight on the boss page. If you want a literal button, recreate the webhook through a Discord bot application and it'll light up automatically.

Why not plain cron?

Boss respawn times shift per cycle, so a fixed cron interval is awkward. The bot polls on an interval and dedupes via state.json, firing exactly once per scheduled spawn regardless of timing. Run it as a long-lived watcher (systemd). A one-shot mode is still provided if you'd rather drive it from cron.

Setup

  1. cp config.example.json config.json (already populated for you) and lock it down:
    chmod 600 config.json
    
  2. Fill in webhook_url, cookie, and the targets you want to watch.

Run

./spawn_bot.py --watch      # recommended: poll forever
./spawn_bot.py              # one poll cycle then exit (for cron)
./spawn_bot.py --dry-run    # parse + log, never post to Discord (test parsing)
./spawn_bot.py --reset-state  # forget seen monsters; next run re-baselines

On first run, any boss that is already live is announced as SPAWNED and any with a scheduled future spawn is announced as upcoming; after that it only fires on new events. Set notify_upcoming: false to send only the SPAWNED-now join alert.

mkdir -p ~/.config/systemd/user
cp spawn-bot.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now spawn-bot
journalctl --user -u spawn-bot -f      # follow logs

(loginctl enable-linger $USER if you want it running while logged out.)

Via cron (one-shot each minute)

* * * * * cd /home/<USERNAME>/workspace/forgejo/<USERNAME>/spawn_bot && /usr/bin/python3 spawn_bot.py >> spawn_bot.log 2>&1

Cron's floor is 1 minute; a spawn shorter than that could be missed. The watcher is preferred.

The page is behind Cloudflare. The cf_clearance cookie in config.json is tied to your IP + User-Agent and expires (~3060 min typical). The bot cannot self-renew it — that needs a real browser to solve the challenge. When it lapses, the bot detects the challenge page and sends a one-time Discord alert telling you to refresh cookie. To refresh: reopen the wave page in Firefox, copy the request as cURL, and paste the new Cookie value into config.json.

Files

  • spawn_bot.py — the watcher (stdlib only; shells to curl for the CF-guarded fetch)
  • config.json — your secrets/targets (chmod 600, git-ignored)
  • config.example.json — annotated template
  • state.json — already-announced boss spawns (auto-created)
  • spawn-bot.service — systemd user unit