Files
clientCommands/docs/sdk-notes.md
T
lyuksovannyyandClaude Opus 5.5 46a1f6ee1f Client Commands: telegram-panel slash commands with Discord-style parameters
Lists the panel account's client commands as Telegram slash commands (hourly
refresh, cached on the device), shows their parameters while typing, runs
complete commands on the panel instead of sending them and shows the answer
as an "Only visible to you" message (latest 20 kept in memory). Holding send
offers Run / Send as message / Send response in chat (red ! badge); an
answer's menu starts with Send in chat. Other plugins register their own
commands through the client_commands library module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:19:20 +02:00

131 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# exteraGram plugin SDK — condensed notes
Gathered from <https://plugins.exteragram.app/docs>, the SDK type stubs (`stubs.zip` from
`exteraSquad/plugins-pysdk-builds`), the official `exteragram-utils` PyPI package
(`exteragram_utils/dev_client.py`), and the app's `gradle.properties`. Not exhaustive — see the
full docs site for anything not covered here.
## Plugin identity and metadata
Read statically via AST from top-level constant assignments in the plugin's single `.py` file
— the app never executes the file just to read metadata. Docs:
<https://plugins.exteragram.app/docs/metadata>.
| Constant | Required | Notes |
|---|---|---|
| `__id__` | yes | 2–32 chars, starts with a letter, only `A-Za-z0-9_-` |
| `__name__` | yes | display name |
| `__description__` | no | |
| `__author__` | no | |
| `__version__` | no | |
| `__icon__` | no | e.g. `"exteraPlugins/1"` |
| `__app_version__` | no | e.g. `">=12.5.1"`; legacy alias `__min_version__` |
| `__sdk_version__` | no | e.g. `">=1.4.4.3"` |
| `__requirements__` | no | list of pip package specs the host installs before loading |
## `BasePlugin` lifecycle and hooks
<https://plugins.exteragram.app/docs/base-plugin>
- `on_plugin_load(self)` — called once when the plugin is enabled/loaded. Hooks must be
registered here (e.g. `self.add_on_send_message_hook()`); registering later has no effect.
- `on_plugin_unload(self)` — called on disable/removal; release resources here.
- `create_settings(self)` — returns a list of `ui.settings` rows rendered in the plugin's
settings screen.
- Hooks: `on_send_message_hook(self, account, params)`, `pre_request_hook(self, ...)`,
`post_request_hook(self, ...)`, `on_update_hook(s)(self, ...)`, `on_app_event(self, ...)`.
Each returns a `HookResult`.
- `HookResult(strategy=HookStrategy.MODIFY, params=params)` applies the mutated `params` back
to the host; `HookStrategy.DEFAULT` (a bare `HookResult()`) passes through unchanged;
`HookStrategy.CANCEL` drops the action; `HookStrategy.MODIFY_FINAL` applies changes and
short-circuits any later hooks.
- Settings persistence: `self.get_setting(key, default)` / `self.set_setting(key, value)`.
## `ui.settings` row types
<https://plugins.exteragram.app/docs/ui-settings>
`Header`, `Input`, `Switch`, `Text`, `Selector`, `Divider` — plain kwargs-holding row
descriptors consumed by the host's settings renderer; construct and return them from
`create_settings`.
## Logging
<https://plugins.exteragram.app/docs/logging>
`android_utils.log(...)` writes to logcat. Chaquopy additionally redirects
`sys.stdout`/`sys.stderr` to the logcat tags `python.stdout` / `python.stderr` — useful when a
dependency logs with plain `print()`.
## Background work
<https://plugins.exteragram.app/docs/client-utils>
`client_utils.run_on_queue(...)` schedules work off the UI thread; use it for anything that
shouldn't block message handling.
## Reflection / private API access
<https://plugins.exteragram.app/docs/hook-utils>
`hook_utils.find_class(...)` and related helpers locate app classes and private fields for
plugins that need to reach beyond the public SDK surface.
## UI helpers
- `ui.alert.AlertDialogBuilder` — native alert dialogs. <https://plugins.exteragram.app/docs/ui-alert>
- `ui.bulletin` — transient bottom bulletins/toasts. <https://plugins.exteragram.app/docs/ui-bulletin>
- `intents.IntentsManager` — Android intent integration, **exteraGram 12.6.4+ only**.
<https://plugins.exteragram.app/docs/intents>
## On-device dev server protocol
<https://plugins.exteragram.app/docs/dev-server>
TCP `127.0.0.1:42690`, reachable from a PC via `adb forward tcp:42690 tcp:42690`. Wire format
is consecutive UTF-8 JSON objects with **no length framing** — parse with
`json.JSONDecoder().raw_decode` over an accumulating buffer, never assume one JSON object per
`recv()`. Requests: `{"@": <command>, "#": <id>, ...}`; responses echo `"#"`.
Commands used by this repo's `tools/devclient.py`: `ping`, `get_plugins`, `enable_plugin`,
`disable_plugin`, `reload_plugin`, `remove_plugin` (field `plugin_id`), `write_plugin`
(`plugin_id`, `content` — installs or updates), `start_debugger`/`stop_debugger` (`host`,
`port`, `platform` ∈ `vscode`|`pycharm`). The official `exteragram-utils` client (PyPI) uses
integer request ids from 1, a 30 s response timeout, a 1.0 s socket timeout, a
`write_plugin` → sleep 0.3 s → `reload_plugin` deploy sequence, and `adb forward` (not
`reverse`) for both the dev-server port and the debugger port. `elyx_*` commands exist for the
Elyx engine and are out of scope here.
## Runtime
Chaquopy 16 / Python 3.11. Anything shipped in a bundle must be 3.11-compatible.
## Why not Elyx/ElyxBuilder
The official structured plugin format (Elyx/ElyxBuilder) is a real alternative to a single
`.plugin` file, but it:
- requires the separate Elyx engine to be installed and enabled on the device;
- ships archives, not the `.plugin` files the base plugin engine understands;
- needs a manual first install before live reload works.
This repo's bundler instead compiles a normal multi-file `src/` project down to the one
`.plugin` file the base engine already knows how to load, so the dev-server write/reload loop
works from the first deploy with no extra on-device engine.
## Gotchas found on exteraGram 12.10.6 (client_commands)
- **Class checks.** `hook_utils.find_class` returns a Chaquopy class wrapper without
`isInstance`. Use `jutil.java_class(name)`, which gives the real `java.lang.Class`.
- **Plugin-made modules.** A module a plugin puts into `sys.modules` doesn't reliably survive
that plugin's reload. State that must outlive a reload (the library registry) hangs off
`builtins` instead.
- **Suggestion list.** `MentionsAdapter.notifyDataSetChanged` diffs rows by their item (the
command string). Rows whose label stays the same aren't rebound, so call
`notifyItemRangeChanged` too. The adapter's `:` emoji search is async and replaces the
rows, so replace the search (skip the method) instead of patching its result.
- **Recycled cells.** A cell restyled by an earlier load of the plugin keeps that style.
Remember the original in the view's tag, not in plugin memory.
- **Ephemeral messages.** A tap opens their menu; a long press does nothing. Deleting one goes
through `MessagesController.deleteEphemeralMessage`, which also asks Telegram to delete it.