Créer un plugin
La référence complète du SDK pour les développeurs de plugins — Python sandboxé, livré sous forme de zip, distribué via la marketplace.
Les plugins sont des processus Python isolés. Écrivez un gestionnaire, déclarez vos capacités, envoyez un zip — nous l'exécutons dans un conteneur Docker verrouillé et lui transmettons les événements Discord via JSON-RPC.
Référence — ctx attributs
| Attribut | Type |
|---|---|
| ctx.server_id | str — ID du serveur Discord (guilde) |
| ctx.plugin_id | str — l'id de votre plugin |
| ctx.version | str — chaîne de version installée |
| ctx.capabilities | set[str] — capacités approuvées par l'utilisateur |
| ctx.discord | Surface REST Discord |
| ctx.interaction | Surface de réponse aux interactions |
| ctx.kv | Stockage KV par serveur |
| ctx.sql | SQL en bac à sable (capacité) |
| ctx.ephemeral | Compteurs / délais de recharge / indicateurs Redis |
| ctx.http | HTTP sortant via proxy |
| ctx.ws | Persistent WebSocket connections via the platform broker (capability) |
| ctx.secrets | Encrypted secret storage (capability) |
| ctx.metrics | Métriques de séries temporelles |
| ctx.request_id | str — correlation ID for the current interaction ("" outside one) |
| ctx.log(message, *, level, tags, **extra) | Journal structuré du plugin |
| ctx.has_capability(name) | bool — vérification de capacité |
ctx.discord
| Méthode | Capacité |
|---|---|
send_message(*, channel_id, content="", embeds=None, components=None, files=None) Le gestionnaire retourne {"message_id", "channel_id"} | discord:send_message |
edit_message(*, channel_id, message_id, content=None, embeds=None, components=None) — None leaves components unchanged, [] clears them | discord:edit_message |
| delete_message(*, channel_id, message_id) | discord:delete_message |
| bulk_delete_messages(*, channel_id, message_ids) | discord:delete_message |
| add_reaction(*, channel_id, message_id, emoji) | discord:add_reaction |
| pin_message(*, channel_id, message_id) | discord:send_message |
| unpin_message(*, channel_id, message_id) | discord:send_message |
| get_messages(*, channel_id, limit=50, before=None, after=None) | discord:read |
iter_messages(*, channel_id, batch_size=50, before=None, after=None) v0.6.1 — generator that pages through full channel history (newest→oldest, or oldest→newest with after=) | discord:read |
| get_guild() | discord:read |
| get_channel(*, channel_id) | discord:read |
| list_channels() | discord:read |
| get_member(*, user_id) | discord:read |
| list_members(*, role_id=None, limit=100, after=None) | discord:read |
| search_members(query, *, limit=25) | discord:read |
| list_roles() | discord:read |
| create_channel(*, name, channel_type=0, category_id=None, topic=None, user_limit=None) | discord:manage_channels |
| edit_channel(*, channel_id, name=None, topic=None, user_limit=None) | discord:manage_channels |
| delete_channel(*, channel_id) | discord:manage_channels |
| set_channel_permissions(*, channel_id, target_id, allow="0", deny="0", target_type=0) | discord:manage_channels |
| delete_channel_permission(*, channel_id, target_id) | discord:manage_channels |
| create_thread(*, channel_id, name, thread_type=11, auto_archive_duration=1440) | discord:manage_channels |
| edit_thread(*, thread_id, archived=None, locked=None, name=None, auto_archive_duration=None) | discord:manage_channels |
| timeout_member(*, user_id, duration_seconds, reason="") | discord:moderate_members |
| set_nickname(*, user_id, nickname=None) | discord:moderate_members |
| kick_member(*, user_id, reason="") | discord:kick_members |
| ban_member(*, user_id, reason="", delete_message_seconds=0) | discord:ban_members |
| unban_member(*, user_id) | discord:ban_members |
| add_role(*, user_id, role_id, reason="") | discord:manage_roles |
| remove_role(*, user_id, role_id, reason="") | discord:manage_roles |
| add_role_bulk / remove_role_bulk / timeout_bulk / kick_bulk | (correspond à une capacité d'action unique) |
| create_webhook(*, channel_id, name) | discord:manage_webhooks |
| execute_webhook(*, webhook_id, webhook_token, content="", embeds=None, username=None, avatar_url=None) | discord:manage_webhooks |
| delete_webhook(*, webhook_id) | discord:manage_webhooks |
edit_message, delete_message and bulk_delete_messages only operate on messages your plugin sent — attempts on other messages are rejected, and a bulk call fails closed on the whole batch if any ID isn't plugin-owned.
ctx.ws
Requires proxy:websocket. Connections are named; the platform's broker holds the socket. Full guide on the Storage & I/O tab.
| Méthode | Utiliser |
|---|---|
| ensure(name, url, *, secret_auth=None, auth=None, subscribe=None, binary=False) | Idempotently open (or confirm) the named connection. Returns {"conn_id", "name", "state"}. subscribe frames are re-sent after every reconnect. |
| send(name, data) | One frame: str sends text, bytes sends binary. |
| close(name) | Hang up the named connection. |
| allow_host(host) / revoke_host(host) | Authorize a WebSocket destination a server admin supplies at setup time; must be called from a slash handler run by that admin. |
ctx.interaction
All four methods require the interaction:respond capability (auto-added when your manifest declares slash_commands).
| Méthode | Utiliser |
|---|---|
| respond(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None, update_message=False) | Première réponse (dans les 3s suivant la réception de l'interaction). allowed_mentions reflète le champ API de Discord — p. ex. {"parse": []} supprime tous les pings. |
| defer(*, ephemeral=False) | « Je travaille dessus » — vous accorde jusqu'à 15 minutes pour un suivi |
| followup(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None) | Reply after a defer (or send additional messages). Returns {"message_id", "channel_id"} so you can edit the message later. |
| send_modal(*, title, custom_id, fields=None) | Ouvrir un modal — à utiliser comme PREMIÈRE réponse, pas après un différé |
ctx.kv / ctx.sql / ctx.ephemeral / ctx.http / ctx.metrics
Full method signatures live on the Storage & I/O tab: KV, SQL, Ephemeral, HTTP (including secret-backed Authorization injection via secret_auth=), Metrics.
Décorateurs de plugin
| Décorateur | Signature du gestionnaire |
|---|---|
| @plugin.on_ready | (ctx) — per (worker, server) before that server's first event in pool mode; once per boot locally |
| @plugin.on_event(event_type) | (ctx, event) |
| @plugin.on_slash_command(name) | (ctx, event) |
| @plugin.on_component(custom_id=None, *, prefix=None) | (ctx, event) — exactly one of exact custom_id or prefix match |
| @plugin.on_modal_submit(custom_id) | (ctx, event) |
| @plugin.on_ws_message(name) | (ctx, frame) — {"name", "conn_id", "data", "binary"}, binary data base64. "prefix:*" wildcard matches by prefix; exact names win |
| @plugin.on_ws_open(name) | (ctx) |
| @plugin.on_ws_close(name) | (ctx, info) — {"reason", "will_reconnect"} |
| @plugin.on_dashboard(method_name) | (ctx, params) → dict |
| @plugin.schedule(seconds) | (ctx) — local yourbot dev only; does not run in pool mode (marketplace plugins), see Pool mode |
| @plugin.cron(spec) | (ctx) — 5-field UTC cron string. Runs in production when declared in the manifest "cron" array (5-minute floor, 5 entries max); see Cron schedules |
| @plugin.on_install / on_enable / on_disable / on_uninstall | (ctx) — advisory signals, delivered best-effort when a worker is live, with a tenant-scoped ctx that keeps your approved capabilities. Required init still belongs in on_ready |
Référence du payload d'événement
Typed payload shapes for all 16 events live in yourbot_sdk.events (MessageCreate, MemberJoin, …). Defaults produced by yourbot_sdk.testing.make_event:
make_event("message_create", content="hi")
# {message_id, channel_id, guild_id, author_id, author_username, author_bot,
# content, timestamp}
make_event("interaction_create", command_name="ban", custom_id="")
# {interaction_id, interaction_type, guild_id, channel_id, user_id,
# command_name, custom_id, modal_values}
make_event("reaction_add", emoji="🎉")
# {message_id, channel_id, user_id, emoji, guild_id}
WebSocket handlers receive their own shapes (not events): on_ws_message gets {"name", "conn_id", "data", "binary"} (binary data base64, frames per connection arrive in order) and on_ws_close gets {"reason", "will_reconnect"}.
Référence des exceptions
Consultez le tableau complet sous Gestion des erreurs.
capacité
Everything you can put in capabilities_required, with the badge customers see on the install consent screen. ctx.metrics, ctx.ephemeral and ctx.log need no capability.
| Capacité | Badges | Ce qu'elle déverrouille |
|---|---|---|
| storage:kv | Sûr | Stockage KV par serveur |
| discord:send_message | Sûr | send_message, pin_message, unpin_message |
| discord:edit_message | Sûr | edit_message |
| discord:add_reaction | Sûr | add_reaction |
| interaction:respond | Sûr | Gestion des commandes slash et des composants |
| discord:read | Tableau d'honneur | Lire l'historique des messages, les membres, les salons, les rôles |
| discord:delete_message | Tableau d'honneur | delete_message, bulk_delete_messages |
| discord:manage_webhooks | Tableau d'honneur | create_webhook, execute_webhook, delete_webhook |
| storage:secrets | Tableau d'honneur | Créé ctx.secrets.* + secret-backed auth on ctx.http/ctx.ws |
| storage:sql Examiné | Tableau d'honneur | Schéma SQL isolé |
| proxy:http | Tableau d'honneur | Sortant ctx.http.* — vous avez déclaré |
| events:message_content | Dangereux | Message text and author detail in events — without it events arrive meta-only (IDs and timestamps). Privacy-sensitive; request it only if you parse what people type |
| discord:manage_channels | Dangereux | Créer/modifier/supprimer des salons & fils de discussion, définir les permissions |
| discord:moderate_members | Dangereux | timeout_member, timeout_bulk, set_nickname |
| discord:kick_members | Dangereux | kick_member, kick_bulk |
| discord:ban_members | Dangereux | ban_member, unban_member |
| discord:manage_roles | Dangereux | add_role, remove_role, bulk variants (admin roles blocked) |
| proxy:websocket Examiné | Dangereux | Historique des versions ctx.ws.* — exact host required (or admin-approved via allow_host) |
Dangerous-tier calls are logged at WARNING level. Capability narrative (auto-detection at upload, consent flow, CapabilityError semantics) lives on the Production tab.
Commandes Discord
These names belong to YourBot's built-in plugins and are refused at upload (yourbot validate checks them locally):
announce appeal ban beacon case giveaway group
group-admin group-alerts help history kick leaderboard
lockdown maid-bug-report music note poll purge
quarantine quests raidmode report slowmode stats
temp-role ticket tickets timeout unban unlockdown
unquarantine untimeout warn welcome yourbot
Grandfathering: if your plugin published a command before its name joined this list, you keep publishing it and on each server it registers under the alias /yourpluginid-name instead. Your handlers need no changes; events still arrive under the manifest name, and your command sync status shows the alias.
Limits and quotas
Every enforced number in one place. Each row links to the section that explains it.
| Prêt | Limite |
|---|---|
| Page | 25 MB zipped, 100 MB uncompressed, 500 files, 10 MB per file |
| Magasin KV | 50,000 keys per (server, plugin), 500,000 per plugin globally; 64 KB values, 512-byte keys; get_many 50 / set_many 25 / list 1000 keys / list_values 100 entries per call |
| Secret | 64-char keys, 4096-char values |
| SQL | 1000 rows per query (hard cap), one statement per call, 100 tables, 10,000,000 rows per (plugin, server), DDL 20/hour |
| — éphémère | 256-char keys, 24 h max TTL (silently clamped); writes cost 0.1 outbound action |
| HTTP | 500 KB request, 200 KB response, 8 s timeout, 30 requests/min |
| WebSockets | 6 connects/min, 4 MB/min traffic shared across both directions |
| Icône | 5-minute minimum interval, 5 entries per manifest, UTC minute resolution |
| Métriques | 1,000,000 stored points per (server, plugin), 10 tags per record, 128-char tag values, 90-day query window; names 1–64 chars |
| Journaux | 4000-char messages, 10 tags, 20 extra kwargs at 500 chars each |
| Disponibilité | 50 events/s, 60 outbound actions/min; 64 MB / 0.25 vCPU per worker (24 MB / 0.1 in pool mode); 64 PIDs; 16 MB /tmp |
| Tableau de bord | 8 s widget RPC timeout, 600 bridge calls/min |
| Actions | 3 s to first response; defer() buys 15 minutes |
Journal des modifications du SDK
v0.8.4
- Added production cron delivery:
@plugin.crontasks declared in the manifest"cron"array now fire in pool mode with a tenant-scoped ctx (Cron schedules).yourbot validatechecks specs (5-minute floor, 5 entries) and warns when a decorated task has no manifest entry. - Ajout des décorateurs de cycle de vie :
on_install,on_enable,on_disable,on_uninstall.
v0.8.3
- Added slash-command consistency checks to
yourbot validate: declared commands with no matching handler, uppercase decorator names, reserved names and malformed command/option names are caught locally, exactly like the platform does at upload. - Ajout d'opérations Discord en masse :
add_role_bulk,remove_role_bulk,kick_bulk,timeout_bulk. - Fixed pool-mode tenant resolution (every RPC now carries the correlation ID of its originating event). Previously documented for 0.7.1, but the code did not ship in that wheel; 0.8.3 ships it.
- Fixed dashboard handler error logs to name the handler that actually failed.
v0.8.2
- Added wildcard WebSocket handlers:
@plugin.on_ws_message("name:*")(andon_ws_open/on_ws_close) match every connection sharing the prefix; exact-name registrations take precedence.
v0.8.1
- Added
ctx.ws.allow_host(host)/ctx.ws.revoke_host(host)— a server admin authorizes a WebSocket destination at setup time that a static allow-list can't express.
v0.8.0
- Added
ctx.ws— persistent WebSocket connections held by the platform broker (ensure/send/close,on_ws_message/on_ws_open/on_ws_close). Capability:proxy:websocket. - Added secret-backed auth injection for
ctx.httpandctx.ws:secret_auth="NAME"(orauth={"scheme": ..., "secret": ...}) makes the platform setAuthorizationfrom a domain-bound secret your code never sees. Unblocks Bearer-token APIs. - Ajout des décorateurs de cycle de vie :
on_install,on_enable,on_disable,on_uninstall. - Added
MockContext.wsto the test harness.
v0.7.1
- Changed
ctx.kv.increment(key, amount)to acceptamountpositionally (was keyword-only). - Fixed
RateLimitError.retry_afterto carry the host's actual retry hint as a float.
v0.7.0
- Added
ctx.interaction.respond(update_message=True)— edit the message a button / select menu is attached to (Discord UPDATE_MESSAGE) instead of sending a new reply. Component interactions only; degrades to a normal reply on older platforms.
v0.6.1
- Added
ctx.discord.iter_messages()— auto-paginating channel history generator. - Added machine-readable
.codeon every SDK exception (CAPABILITY_DENIED,RATE_LIMITED, …). - Added typed Discord response shapes (
yourbot_sdk.responses) and a PEP 561py.typedmarker, so mypy / pyright / IDEs pick up SDK type hints. - Changed
MockContextto enforce capabilities by default (gated calls raiseCapabilityErrorlike production); several mock-fidelity fixes. - Fixed KV quota errors to raise
KvQuotaErrorinstead of a genericRateLimitError.
v0.6.0
- Renamed the SDK from
mmo-maid-sdktoyourbot-sdk(pip install yourbot-sdk,from yourbot_sdk import Plugin). Nothing breaks:import mmo_maid_sdkkeeps working via a compatibility package and the old PyPI name resolves to the new one. - Ajout d'opérations Discord en masse :
add_role_bulk,remove_role_bulk,kick_bulk,timeout_bulk.
v0.5.1
- Ajout du paramètre
allowed_mentionssurctx.interaction.respond()etctx.interaction.followup()— contrôle les mentions qui envoient réellement un ping (p. ex.{"parse": []}supprime tous les pings).
v0.5.0
- Ajout des sous-API
ctx.metrics,ctx.sql,ctx.ephemeral. - Ajout du module
mmo_maid_sdk.testingavecMockContextetmake_event. - Ajout des décorateurs de cycle de vie :
on_install,on_enable,on_disable,on_uninstall. - Ajout de
@plugin.cron(spec)pour la planification UTC sur horloge murale (cron à 5 champs). - Ajout d'opérations Discord en masse :
add_role_bulk,remove_role_bulk,kick_bulk,timeout_bulk. - Ajout de classes d'exceptions typées (10) pour la gestion des erreurs exploitables.
- Sécurité 7 failles critiques + 1 faille haute SDK / sandbox corrigées (audit, avril 2026).
SDK v0.8.4 · Dernière mise à jour juillet 2026 · Retour au portail développeur