# The library: commands from other plugins Client Commands publishes the module `client_commands`. Another plugin registers commands with it. Those commands show in the `/` list with their parameters and run on the device, and their answers show like the panel's: only to you, or in the chat when *Send response in chat* is on. ## Registering ```python import random import sys import types from base_plugin import BasePlugin __id__ = "dice" __name__ = "Dice" def setup(api): api.register( "roll", roll, summary="Rolls a die", params=[ api.Param("sides", "integer", required=False, default=6, description="How many sides"), api.Param("style", "choice", required=False, default="plain", choices=("plain", "big")), ], owner=__id__, ) def roll(ctx, sides=6, style="plain"): api = sys.modules["client_commands"] if sides < 2: raise api.CommandError("A die needs at least 2 sides") result = random.randint(1, sides) return f"🎲 {result}" if style == "big" else f"🎲 {result}" class DicePlugin(BasePlugin): def on_plugin_load(self): # Works whichever plugin loads first. api = sys.modules.setdefault("client_commands", types.ModuleType("client_commands")) if hasattr(api, "register"): setup(api) else: api.__dict__.setdefault("pending", []).append(setup) def on_plugin_unload(self): api = sys.modules.get("client_commands") if api is not None and hasattr(api, "unregister_owner"): api.unregister_owner(__id__) ``` ## API | Name | What it does | |---|---| | `register(name, handler, *, params=(), summary="", description="", aliases=(), reply="none", owner=None)` | Adds or replaces a command. `reply` is `none`, `optional` or `required`. `params` are `Param`s, or dicts shaped like the panel's. | | `unregister(name)` | Removes one command. | | `unregister_owner(owner)` | Removes every command `owner` registered. Call it from `on_plugin_unload`. | | `commands()` | The registered commands, as dicts. | | `Param(name, type="string", required=True, description="", default=None, choices=())` | `type` is `string`, `integer`, `number`, `boolean` or `choice`. | | `CommandError` | Raise it for a user-facing error. The message is shown as the answer. | | `escape(text)` | Makes text safe to put into an HTML answer. | | `VERSION`, `available` | The API version, and whether Client Commands is loaded right now. | `handler(ctx, **values)` runs off the UI thread. Its arguments are the parsed parameter values, with defaults for those not given. It returns the answer as Telegram HTML, or None (the answer is then "✓ Done"). These tags work: `b`, `i`, `u`, `s`, `code`, `pre`, `a href`, `blockquote`, `tg-spoiler`, `tg-emoji` and `br`. `ctx` has: - `account`, `dialog_id` and `topic_id`; - `reply_to`, the replied `MessageObject` or None; - `text`, the command as typed, and `command`, its name; - `values`, the parsed parameters; - `send_to_chat`, whether the answer goes into the chat as the account; - `client`, the SDK's `AccountClient` of the account. Parsing follows the same rules as the panel's commands (see [how-it-works.md](how-it-works.md#parsing)). A panel command with the same name shadows yours. Registrations survive Client Commands being updated or turned off and on. A `client_commands` module kept from before still works.