Dokumentation / Developers / Dashboards / Manifest mode
BuildSDK 0.10.1

Manifest mode

These docs are public. To publish a plugin you need a YourBot account: sign in to open the Dev Portal.

Declare "mode": "manifest" and a list of pages with widgets. rpc_method values are the names you registered with @plugin.on_dashboard(…):

{
  "mode": "manifest",
  "pages": [
    {
      "id": "overview",
      "title": "Overview",
      "widgets": [
        {
          "id": "active_users",
          "type": "stat_card",
          "label": "Active users",
          "icon": "📊",
          "width": "quarter",
          "rpc_method": "get_active_users"
        },
        {
          "id": "messages_chart",
          "type": "chart",
          "chart_type": "line",
          "title": "Messages per day",
          "refresh_seconds": 30,
          "rpc_method": "get_messages_chart"
        },
        {
          "id": "settings_form",
          "type": "form",
          "title": "Settings",
          "rpc_method": "get_settings",
          "save_rpc_method": "save_settings",
          "fields": [
            {"key": "channel_id", "label": "Welcome channel", "type": "text"},
            {"key": "message",    "label": "Welcome text",    "type": "textarea"}
          ]
        }
      ]
    }
  ]
}
  • Widths: "quarter", "third", "half", "two_thirds", "full" (default).
  • Auto-refresh: "refresh_seconds": 30 re-fetches periodically; a manual refresh button appears on every widget.
  • Chart types: line, bar, area, scatter, pie, doughnut, gauge. Charts also honor "chart_options" with colors (array), show_legend, x_axis_label and y_axis_label.
  • Form field types: text (default), textarea, number, select, multi_select, toggle, color, date, range. Every field renders label and help_text. placeholder applies to text, textarea and number. required is enforced on every type except toggle, color and range. select and multi_select need "options": [{"value": "...", "label": "..."}]. number and range accept min, max and step.
  • Extra RPC args: "rpc_params": {"stat": "total"} is merged into the params your handler receives.
  • Shared handlers: a widget may set "path" (e.g. "kpis.0" or "activity") to extract its slice from a bigger RPC response, so one get_overview handler can drive a whole page of widgets.
  • Access control: pages and widgets accept "permission": "viewer" | "manager" | "owner"; manifest values can only raise the bar, never lower it. Saves always require at least manager, enforced server-side regardless of what your UI shows.

Prefer Python over raw JSON? yourbot_sdk.dashboard ships a fluent builder for every widget type — Manifest, Page, StatCard, Chart (with Chart.line / Chart.bar / Chart.pie / Chart.gauge factories), Table, List, ProgressBar, Text, Markdown, Alert and Form (with per-field helpers like .text(), .select(), .toggle()), plus Column and Option for tables and selects. They emit exactly this JSON, and invalid widths / permissions / chart types raise ValueError at build time instead of failing at render.

Supported widget types

TypeHandler returns
stat_card{"value": 1234, "change": "+12%", "trend": "up", "color": "green"} (trend: up / down / flat; color: green / red / gold / cyan)
chart{"labels": [...], "series": [{"name": "...", "data": [...]}]}
table{"rows": [{...}, ...], "total": 100, "page_size": 20} — declare "columns": [{"key": "...", "label": "..."}] on the widget (required); headers and cell order come from it. Your handler receives params["page"] (1-indexed) when the viewer pages with Prev/Next
list{"items": [{"label": "...", "value": "..."}, ...]}
progress_bar{"value": 75, "max": 100, "label": "…"}
text{"content": "Plain text content"}
markdown{"markdown": "# Heading\n- bullet"} (safe markdown, raw HTML is stripped)
alert{"level": "info", "message": "…"} — level: info / warning / error / success
formget → {"values": {...}}; save → {"ok": true} or {"ok": false, "error": "…"}

Python handlers

@plugin.on_dashboard("get_active_users")
def active_users(ctx: Context, params: dict):
    n = ctx.kv.count(prefix="active_user:")
    return {"value": n, "change": "+12%", "trend": "up"}

@plugin.on_dashboard("get_messages_chart")
def messages_chart(ctx: Context, params: dict):
    return ctx.metrics.query("messages", period="7d")

@plugin.on_dashboard("get_settings")
def get_settings(ctx: Context, params: dict):
    return {"values": {
        "channel_id": ctx.kv.get("welcome_channel") or "",
        "message":    ctx.kv.get("welcome_message") or "Welcome!",
    }}

@plugin.on_dashboard("save_settings")
def save_settings(ctx: Context, params: dict):
    # Form saves arrive as params["values"], keyed by each field's "key".
    values = params.get("values") or {}
    message = str(values.get("message", "")).strip()
    if len(message) > 500:
        return {"ok": False, "error": "Keep the welcome text under 500 characters"}
    ctx.kv.set("welcome_channel", values.get("channel_id", ""))
    ctx.kv.set("welcome_message", message)
    return {"ok": True}
  • 8-second timeout per handler call; the widget shows an error past it.
  • Fresh Context per call: every dashboard RPC gets a ctx scoped to the server the viewer is looking at, even in pool mode (one shared worker serves every server that installed your plugin). params carries discord_srv_id (delivered as a string — compare with str(params.get("discord_srv_id")) to stay safe across paths) plus the viewer's identity: discord_user_id, viewer_user_id, viewer_username and username.
  • Read caching: widget responses are cached ~5 seconds per (plugin, server, method, params); each table page caches separately and writes bypass the cache.
  • Serialized: only one dashboard RPC runs at a time per worker, and handlers share the process with your event handlers.
  • Cold start: if no worker is running for your plugin yet, the widget shows “Starting plugin…” and retries up to 5 times at 1.5-second intervals.
YourBot docs Reference tables are generated from the code that is running. Ask in Discord Suggest a correction