Official self-hosted Discord bot for the System Locker Management API v2. Generate and manage license keys, read system statistics, manage server-side variables and resellers, and run Aegis IP lookups — from slash commands in your own server.
The bot is a server-side tool: it holds a management credential and speaks
only to https://systemlocker.net. It is intended for the staff of one
developer account, configured per Discord server.
- Python 3.10 or newer
- A Discord application with a bot user
- A Management API v2 credential from the System Locker developer portal (Systems page → select a system → create credential)
Create an application in the Discord developer portal and add a bot user.
Invite it to your server with both the bot and applications.commands
scopes. The bot needs no privileged intents — standard permissions are
View Channels, Send Messages, Embed Links, and Attach Files.
In the System Locker developer portal, open the Systems page, select a
system, and create a Management API v2 credential. Its complete value
(slm_<token_id>_<secret>) is shown once — copy it immediately.
Pick the least scopes the bot needs for the commands you plan to use:
| Scope | Used by |
|---|---|
systems.read |
/system, /stats |
systems.update |
/pause, /resume |
keys.create |
/gen |
individual_free_trial |
Public /trial and configured reaction-based individual trials |
keys.read |
/key |
keys.update |
/freeze, /unfreeze, /reset, /resetall, /addtime |
keys.delete |
/deletekey |
variables.* |
/variable … |
resellers.* |
/reseller … (removing an allowance uses resellers.delete) |
security.read |
/keylogs, /iplookup |
systems.delete is never used by this bot — leave it out.
cp config.example.json config.json # then edit it
pip install -r requirements.txt
python main.pyProvide the Discord token either as the DISCORD_TOKEN environment
variable (recommended) or in the token field of the configuration file.
Commands appear immediately in every server listed in the configuration.
Pass --sync-global once if you also want them available in servers that
are not configured (new servers still need a configuration entry before
commands work).
The configuration file maps Discord servers to systems and roles. Structure:
{
"token": "…optional, DISCORD_TOKEN preferred…",
"guilds": {
"<guild id>": {
"systems": { "<name>": { "credential": "slm_…", "system_id": "…" } },
"roles": { "support": [], "generate": [], "manage": [] },
"admins": [],
"log_channel": null
}
}
}| Key | Required | Meaning |
|---|---|---|
guilds.<id> |
yes | One entry per Discord server. Anything else gets "not configured". |
systems.<name> |
yes | A friendly name (used in commands) mapped to a credential and system ID. |
systems.<name>.credential |
yes | The Management API v2 credential bound to that system. |
systems.<name>.system_id |
yes | The 20-character system ID from the developer portal. |
roles.support |
no | Role IDs allowed to inspect keys, read stats, reset a HWID, freeze/unfreeze. |
roles.generate |
no | Role IDs additionally allowed to generate keys. |
roles.manage |
no | Role IDs additionally allowed to delete keys, add time, reset all HWIDs, pause/resume, manage variables and resellers, and run lookups. |
admins |
no | User IDs with full access regardless of roles. |
log_channel |
no | A channel that receives an embed for every mutation (key generation, deletion, pauses, variable changes). |
individual_free_trial |
no | Configures public individual trials; its enable switch remains false until explicitly changed. |
Enable Developer Mode in Discord (User Settings → Advanced) to copy IDs
via right-click. Tiers are cumulative — generate includes everything
support can do, and manage includes both. Members with the Discord
Administrator permission always act at manage.
A credential is bound to exactly one system, so each configured system uses its own credential. The bot shares one API client per credential and voluntarily respects the API's rate limit of 10 requests per 5 seconds.
Individual free trials are off unless a guild has an
individual_free_trial block with "enable": true. This extra switch
defaults to false, so copying a configuration example cannot issue keys.
They use the dedicated
individual_free_trial credential scope (not keys.create) and always use
the member's Discord ID as the trial identifier, so a member cannot request
another key for that system.
"individual_free_trial": {
"enable": true,
"system": "my-product",
"mode": "command",
"duration": "7d",
"notes": "Discord community trial"
}Change "enable" to true only when you are ready to issue keys. Use
"mode": "command" to let members run /trial, or use
"mode": "reaction" plus a channel ID to issue a key when a member adds
the configured emoji (default: 🎉) to any message in that channel:
"individual_free_trial": {
"enable": true,
"system": "my-product",
"mode": "reaction",
"channel": 666666666666666666,
"emoji": "🎉",
"duration": "7d"
}duration is required and starts on first redemption. The key is sent only
by DM and is never mirrored to the guild log channel. Members must permit
DMs from the server before requesting a trial.
| Command | Tier | Description |
|---|---|---|
/key system license_key |
support | Full details: redemption, claim, HWID, frozen, expiry, notes |
/keylogs system license_key |
support | The latest five authentication attempts for a key |
/system system |
support | Version, program hash, pause state |
/stats system |
support | Online and total user counts with computed-at times |
/systems |
support | Systems configured for this server |
/reset system license_key |
support | Reset the HWID claimed by a key |
/freeze / /unfreeze system license_key |
support | Freeze or unfreeze a key |
/gen system [count] [expiry] [duration] [expires_at] [notes] [free_trial] |
generate | Create 1–100 keys |
/trial |
— | Request an opted-in individual free-trial key by DM |
/addtime system license_key duration |
manage | Add time to a key's expiry |
/deletekey system license_key |
manage | Permanently delete a key (confirmation prompt) |
/resetall system |
manage | Reset every HWID in the system (confirmation prompt) |
/pause system |
manage | Pause authentication and end sessions (confirmation prompt) |
/resume system [compensate] |
manage | Resume; optionally extend key expiries by the paused duration |
/variable get/create/update/delete |
manage | Manage server-side variables |
/reseller list system / /reseller show system token |
manage | List resellers or show one reseller's permissions and allowance |
/reseller create system name … |
manage | Create a reseller with permissions and an optional allowance |
/reseller permissions system token [flags] |
manage | Replace permissions; omitted flags keep their current value |
/reseller allowance system token type [limits] |
manage | Replace (or disable) a reseller's allowance |
/reseller allowance-remove system token |
manage | Remove a reseller's allowance |
/reseller reset-password system token |
manage | Generate a new reseller password (shown once) |
/reseller delete system token |
manage | Permanently delete a reseller and its allowance (confirmation prompt) |
/iplookup system ip |
manage | Aegis manual IP lookup (requires an Aegis plan) |
/help |
— | Command and tier overview |
Staff-command responses are ephemeral — only the invoking staff member sees
them. An individual trial key is instead delivered only by DM to the member
who requested it.
/gen shows the first ten keys in the response and attaches a text file
when more are created.
/addtime and /gen's duration accept a unit-suffixed duration —
90s, 5m, 2h, 7d, 4w, 1y — or a combination like 1d12h.
Bare numbers are rejected so a typo can never quietly mean seconds.
Fixed expiry dates use YYYY-MM-DD or YYYY-MM-DD HH:MM (UTC), for
example 2026-09-01 or 2026-09-01 14:30.
Reseller management requires an active qualifying reseller plan on the developer account; otherwise these commands fail with a plan error from the API.
A reseller is created with a name (up to 80 characters), five permission flags (create, ban, freeze, reset HWID, access all keys), and an optional allowance. An allowance is either an overall total key limit or duration limits per day / week / month / 3 months / year / lifetime — all six are required for the duration type, and every limit is an integer from 0 to 4,294,967,295.
/reseller permissions replaces the complete permissions object; flags
you leave blank keep their current value instead of silently becoming
no.
The reseller password is returned only by /reseller create and
/reseller reset-password, and only in the ephemeral reply to the
invoking staff member — it is never mirrored to the log channel and
cannot be retrieved again later. Reseller tokens are visible to anyone
with the manage tier and appear in the audit log; treat them as
sensitive. Be careful about where you run these commands.
The bot writes to the console and keeps an append-only audit trail in
logs/audit.log (rotated at 2 MB) recording every command invocation with
its arguments. If log_channel is set, mutations are also mirrored there
as embeds. Both contain key material and, with /keylogs and /iplookup,
usernames and IP addresses — treat them as sensitive.
See SECURITY.md. In short: the configuration file holds live credentials and must never be committed or shared; create the credential with the least scopes the bot needs; revoke it immediately if it may have been exposed.
The earlier community bot targeted the legacy v1 API, which is being retired. This bot covers the same workflow with v2:
| Community bot (v1) | This bot |
|---|---|
/gen … expire=0–4 |
/gen with expiry presets, durations, or dates |
/checkkey, /expiration |
/key (full details including expiry) |
/users |
/stats (online + total, computed-at times) |
/reset, /resetall |
/reset, /resetall |
/deletekey |
/deletekey (now with confirmation) |
/adjustexpiry |
/addtime (v2 adds time instead of setting it) |
| local SQLite expiry tracker | not needed — v2 reports expiry per key |
/newreseller, /listresellers, /banreseller |
/reseller … (v2 reseller management) |
| raw-API commands | no equivalent — the bot exposes only typed commands |
Provided as-is for System Locker developers. Feel free to fork and adapt for your own server, but not for a competing service.