5.2 KiB
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 ofui.settingsrows 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 aHookResult. HookResult(strategy=HookStrategy.MODIFY, params=params)applies the mutatedparamsback to the host;HookStrategy.DEFAULT(a bareHookResult()) passes through unchanged;HookStrategy.CANCELdrops the action;HookStrategy.MODIFY_FINALapplies 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-alertui.bulletin— transient bottom bulletins/toasts. https://plugins.exteragram.app/docs/ui-bulletinintents.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
.pluginfiles 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.