Cockatrice/servatrice/scripts/account_monitor
Zach H 80426d77bc
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
Add Discord account registration monitor (#7013)
A read-only cron script that posts new Servatrice account registrations to a
Discord channel via webhook. Dedups by an auto-increment id high-water-mark
(single-integer state, no duplicates, nothing missed across downtime). Reads DB
credentials and the webhook from a servatrice-style ini via --config.
2026-06-21 01:09:57 -04:00
..
.gitignore Add Discord account registration monitor (#7013) 2026-06-21 01:09:57 -04:00
account_monitor.py Add Discord account registration monitor (#7013) 2026-06-21 01:09:57 -04:00
README.md Add Discord account registration monitor (#7013) 2026-06-21 01:09:57 -04:00
requirements.txt Add Discord account registration monitor (#7013) 2026-06-21 01:09:57 -04:00

Account registration monitor

Posts a Discord message whenever a new account is registered in Servatrice (cockatrice_users). Each message includes the username, real name (if set), email, and registration time.

It runs as a periodic read-only query against the production database. It does not modify the database and does not touch the running Servatrice process.

How it decides what is "new"

Accounts get an auto-increment id, so "new since last time" is just id > last_seen_id. The monitor stores that single high-water-mark id in its state file. Each run it posts every account above the mark, oldest first, then advances the mark to the highest id it posted.

Because the mark only moves forward and an id is posted exactly once, there are no duplicate messages and nothing is missed, even if the monitor is down for a while. The state file holds a single number, so it never grows.

The first run (when no state file exists yet) records the current maximum id as the baseline and posts nothing. This prevents the entire existing user base from being dumped into the channel. Only accounts registered after that baseline are posted.

If a post to Discord fails, the monitor stops there without advancing the mark past it, so that account and everything after it are retried on the next run.

Privacy note

Messages contain personal data (real name and email). Discord stores message content on their servers, so post only to a private channel that the right people can see, and treat the webhook URL as a secret. It lives in the config ini alongside the database password, so keep that file readable only by the user that runs the monitor.

Setup

The monitor reads its database credentials and the webhook from a servatrice-style ini file passed with --config (or the CONFIG_FILE env var). You can point it at your existing servatrice.ini, or keep a small separate ini just for the monitor.

1. Create a read-only database user

Run as a DB admin. Adjust the host ('%' allows any host; restrict it to the machine running the monitor if you can) and the table prefix if yours is not the default cockatrice.

CREATE USER 'account_monitor'@'%' IDENTIFIED BY 'a-strong-password';
GRANT SELECT (id, name, realname, email, registrationDate)
  ON servatrice.cockatrice_users TO 'account_monitor'@'%';
FLUSH PRIVILEGES;

Using a read-only user is recommended over pointing --config at the real servatrice.ini, because Servatrice's own DB account usually has write access the monitor does not need.

2. Create the Discord webhook and add it to the config

In Discord: open the target channel, then Edit Channel -> Integrations -> Webhooks -> New Webhook. Name it, pick the channel, and copy the webhook URL.

Add a [discord] section with the URL to the ini you will pass to --config. If you want the read-only user above, set the [database] section to use it. A small dedicated monitor.ini looks like this:

[database]
hostname=127.0.0.1
database=servatrice
user=account_monitor
password=a-strong-password
prefix=cockatrice

[discord]
new_user_activation_webhook=https://discord.com/api/webhooks/XXXX/YYYY

If you would rather use one file, add the [discord] section to the real servatrice.ini instead. Servatrice ignores sections it does not use. Note that Servatrice (a Qt app) rewrites ini values it touches in quoted, backslash-escaped form, for example "https\://...". The monitor strips that encoding from the webhook automatically, so either the plain or the escaped form works.

3. Install

cd servatrice/scripts/account_monitor
python3 -m venv venv
./venv/bin/pip install -r requirements.txt

4. Verify before scheduling

# Confirm the webhook works (sends one test message to the channel)
./venv/bin/python ./account_monitor.py --config /path/to/monitor.ini --test-webhook

# Confirm DB access and see what it would do, without posting or writing state
./venv/bin/python ./account_monitor.py --config /path/to/monitor.ini --dry-run --verbose

The first real run seeds the baseline and posts nothing:

./venv/bin/python ./account_monitor.py --config /path/to/monitor.ini

After that, test it end to end by registering a throwaway account and confirming a message appears on the next run.

Run it every 2 minutes with cron

Edit the crontab of the user that owns the script directory (crontab -e) and add one line. This runs the monitor every 2 minutes, using the venv's Python and your config ini, and appends output to a log:

*/2 * * * * cd /opt/cockatrice/servatrice/scripts/account_monitor && ./venv/bin/python ./account_monitor.py --config /etc/servatrice/servatrice.ini >> /var/log/account_monitor.log 2>&1

Adjust the three paths to your install: the script directory after cd, and the --config and log paths. The */2 field is what makes it run every 2 minutes; change it to */5 for every 5, and so on.

By default the high-water-mark is stored in state.json next to the script, so the directory must be writable by the cron user. To put it elsewhere, set STATE_FILE:

*/2 * * * * STATE_FILE=/var/lib/account_monitor/state.json cd /opt/cockatrice/servatrice/scripts/account_monitor && ./venv/bin/python ./account_monitor.py --config /etc/servatrice/servatrice.ini >> /var/log/account_monitor.log 2>&1

The interval only controls how often it checks; it is not a lookback window, so a longer interval never causes missed accounts. The query is cheap: an indexed range scan on the primary key for id > last_seen.

Options

  • --config PATH / -c PATH — read DB settings from [database] and the webhook from [discord] new_user_activation_webhook of a servatrice-style ini (falls back to the CONFIG_FILE env var).
  • --dry-run — query and log what would be posted; no Discord posts, no state write.
  • --test-webhook — send one test message to the webhook and exit (does not need DB credentials).
  • --verbose — debug logging.

Configuration reference

Settings come from the --config ini, with environment variables available as overrides if you need them (env takes precedence over the ini).

Setting ini (--config) Environment override
DB host [database] hostname DB_HOST
DB port [database] port (optional) DB_PORT
DB name [database] database DB_NAME
DB user [database] user DB_USER
DB password [database] password DB_PASSWORD
Table prefix [database] prefix DB_TABLE_PREFIX
Webhook URL [discord] new_user_activation_webhook DISCORD_WEBHOOK_URL
DB TLS DB_SSL / DB_SSL_CA
State file path STATE_FILE (default: state.json next to the script)