Rust rewrite of s3krit/society.py, a Matrix/Element bot for Kusama Society status.
The bot supports the original command set:
!ping!defender!info <address>!candidates [address]!head!set_address <address>!unset_address!me!period!countdown!skeptics!skeptic
It also polls the Kusama candidate period every 60 seconds and announces period transitions to the configured Matrix room. It subscribes to chain blocks and announces bid and unbid events to the configured Matrix room.
Copy .env.example to .env and fill in the Matrix bot settings.
cargo runTo point the bot at local Chopsticks without editing .env, pass:
cargo devTo send one sample Matrix message to the configured room and exit:
cargo run -- --sampleRequired:
MATRIX_ROOM: Matrix room ID (for example!room:matrix.org) or room alias (for example#room:matrix.org).MATRIX_TOKEN: access token for the bot account.
Optional:
MATRIX_HOMESERVER: defaults tohttps://matrix.org.MATRIX_USER_ID: defaults to@societybot:matrix.org, but you should set this explicitly to the bot account that ownsMATRIX_TOKEN.RPC_URL: defaults towss://kusama-rpc.polkadot.io/.DB_PATH: defaults to./society_overrides.db.PREFIX: defaults to!.RUST_LOG: tracing filter, for exampleinfoorelement_bot=debug.
The bot runs on a single DigitalOcean droplet via Docker Compose. Images are built in CI and pushed to GHCR on every merge to main.
See deploy/README.md for droplet bootstrap, firewall, updates, and backups.
After CI publishes a new image, run cargo deploy (requires DEPLOY_HOST=root@your-droplet-ip in .env).
Use this when you want to validate the bot against a real Matrix homeserver and live Kusama data instead of the Docker e2e harness.
Prerequisites:
- Rust installed locally.
- A Matrix bot account.
- An access token for that bot account.
- The bot account's Matrix user ID, such as
@societybot:matrix.org. - A dedicated Matrix test room.
- The bot account invited to that room and already joined.
- Outbound network access to the Matrix homeserver and
RPC_URL. - A writable path for
DB_PATH.
Setup:
- Copy
.env.exampleto.env. - Create a dedicated Matrix room for testing. A private room is safer because some commands write persistent address overrides.
- Invite the bot account to the room and confirm it has joined.
- Fill in at least these values in
.env:MATRIX_ROOMwith the room ID or alias for the test roomMATRIX_TOKENwith the bot access tokenMATRIX_USER_IDwith the exact user ID for that tokenMATRIX_HOMESERVERif you are not usinghttps://matrix.org
- Start the bot:
cargo run- Confirm startup succeeds and the bot begins syncing instead of failing with Matrix auth, room resolution, or RPC connection errors.
Suggested manual checks from another Matrix account in the test room:
!ping!head!period!defender!skeptics!candidates!info <known-kusama-address>!set_address <known-member-address>!me!unset_address
Expected results:
- The bot replies in the configured room.
- Read-only commands return live Kusama-backed data.
!set_addressstores the Matrix handle override and!meuses it.- After restarting the bot, the override is still present if you kept the same
DB_PATH.
Notes:
!infoand!set_addressare easiest to verify with a known Kusama Society member address.- Period transition announcements are only emitted when the live candidate period changes while the bot is running, so that behavior is not practical to fully verify on demand.
- Manual-test output changes over time because it depends on live chain state.
cargo fmt --check
cargo check
cargo testRust does not have a standard-library coverage reporter. Use cargo-llvm-cov, which wraps Rust/LLVM source-based coverage:
cargo install cargo-llvm-cov
./scripts/coverage.shReports are written to:
target/coverage/html/index.htmltarget/coverage/lcov.info
The Docker e2e harness starts Chopsticks, a mock Matrix homeserver, the bot, and an assertion runner. It exercises Matrix command handling, live Kusama state reads through Chopsticks, and SQLite address overrides:
docker compose -f tests/e2e/docker-compose.yml up --build --abort-on-container-exit --exit-code-from e2eThe default checked-in Chopsticks config is tests/e2e/kusama.yml, copied from ../kappasigmamu.github.io/config/kusama.yml. That file targets Asset Hub.
To run that copied config locally with a pinned fork block and instant block building:
cargo chopsticksTo use a different fork block:
KUSAMA_BLOCK_NUMBER=<block-number> cargo chopsticks --cleanChopsticks writes fetched storage and produced blocks to db.sqlite. cargo chopsticks resumes from that DB by default because it is the fastest local restart path.
To force a fresh local state:
cargo chopsticks --cleanUse --custom with either command to enable the custom runtime WASM override.
Run the bot against local Chopsticks:
cargo devThe repo includes custom-kusama-runtime as a submodule on branch customized-society-pallet. It shortens Society rotation periods for local testing.
Initialize the submodule once:
git submodule update --init --recursiveRun Chopsticks with the custom runtime WASM override:
cargo chopsticks --customIf ./custom-kusama-runtime.wasm is missing, this builds relay/kusama inside the submodule and copies the WASM blob to the repo root (first build can take several minutes).
Or start Chopsticks and the bot together:
cargo dev --customThis builds the WASM when needed, starts Chopsticks with wasm-override: ./custom-kusama-runtime.wasm, waits for ws://127.0.0.1:8000, then runs the bot in dev mode.
Submit a local bid or unbid:
cargo society:bid
cargo society:unbidThe bot should log observed bid/unbid events and send exactly one Matrix message per event, keyed by block hash and event index. If the bot restarts, in-memory dedupe resets and a previously announced event could be announced again.
**New bid** (block X)with the bidder and amount in KSM**Withdrawn bid** (block X)with the account
If Chopsticks logs Method not found: transactionWatch_v1_submitAndWatch, the caller is using an unsupported transaction-watch RPC. The local submit_bid and unbid helpers avoid that path and submit through the legacy-compatible extrinsic RPC.
Export a room's full Matrix history, then seed society_overrides.db from that export.
1. Export — paginates room history and writes JSONL plus media:
cargo export --output ./exports/kappasigmamuloungeDefaults to #kappasigmamulounge:parity.io when --room is omitted. Override with --room or set MATRIX_HOMESERVER for non-matrix.org homeservers.
Output:
events.jsonl— one JSON object per event (sender, timestamp, body, media URLs)metadata.json— export summarymedia/— downloaded attachments (images, files, video, audio)
Use --links-only to skip media downloads. Use --all-events to include membership/state events.
Retry only missing media from a previous export:
cargo export --retry-failed --output ./exports/kappasigmamulounge2. Seed — replays !set_address, !unset_address, and bot !me responses from events.jsonl into the override database:
cargo seed --input ./exports/kappasigmamulounge --dry-run
cargo seed --input ./exports/kappasigmamulounge --db-path ./society_overrides.dbcargo seed reads the export on disk only; it does not call Matrix. It defaults to @societybot:matrix.org for matching bot messages in the export. --input defaults to ./room-export.
Requires a Matrix token and room access for cargo export only.