§ Ember — habit & ritual tracker · Telegram — nudges & check-ins
Framework upgrade: inline buttons + callbacks
To give plugins tappable buttons, the bot framework's IPC contract and router grew three things — all backward-compatible (existing plugins untouched; 98 router + 8 SDK tests green).
1. Richer replies (router ← plugin)
ReplyFrame and AlertFrame gained optional fields: buttons (an inline keyboard as rows of {text,data}), plus edit and answer for callbacks, and style on alerts ("notify" = a clean message with no ⚠ [plugin] prefix). Old plugins that send a plain {text} still work — the fields default to null.
2. A new callback frame (router → plugin)
CallbackFrame {id, data, ctx} carries a button tap to the owning plugin. The supervisor got invoke_callback(data, ctx) (mirrors invoke, collects reply frames); the lifecycle got invoke_callback(plugin, data, ctx) that looks the plugin up by name.
3. Routing the taps (telegram_client)
build_markup(plugin, buttons)turns a plugin's buttons into anInlineKeyboardMarkupwhosecallback_datais namespaced:"<plugin>~<payload>". That namespace is how a tap finds its way home.- A
CallbackQueryHandlersplits the data on~, allowlist-checks the chat, callsinvoke_callback, then either edits the source message (edit=True) or sends a new one, and alwaysanswer()s the query to stop the spinner.
SDK additions (plugin author side)
from bot_plugin import command, on_callback, watcher, Reply, Alert, button, run
@on_callback
def cb(ctx, data): # data = your payload, e.g. "done:habit:5"
return Reply("🔥 done", edit=True, answer="Done!")
# a command or watcher can return buttons:
Reply("Pick one", buttons=[button("Yes","y"), button("No","n")](/s/ember-rituals/button-yes-y-button-no-n))
Alert(key=..., text="…", style="notify", buttons=[button("✅","done:habit:5")](/s/ember-rituals/button-done-habit-5))
Design note: callbacks are routed by plugin-name prefix, not pre-registered. A plugin just declares one
@on_callbackhandler and parses its own payload — so adding buttons never touches the router.