Python SDK
Das Python SDK, owncast-plugin-py, ermöglicht es Ihnen, Owncast-Plugins in Python zu erstellen. Sie schreiben gewöhnliches Python mit Dekoratoren. Ein Build-Schritt verwandelt es in ein einzelnes installierbares Plugin, das innerhalb des Owncast-Servers in einer Sandbox ausgeführt wird: dasselbe .ocpkg-Format und das volle Funktionsspektrum wie das JavaScript SDK, sodass ein Python-Plugin ein gleichwertiger Partner eines JS-Plugins ist.
Die Plugin-SDKs sind brandneu in Owncast 0.3.0, und die API entwickelt sich immer noch weiter. Wenn Sie auf einen Fehler stoßen oder einen Vorschlag haben, bitte öffnen Sie ein Problem oder chatten Sie live mit der Community.
Diese Seite ist die Python-spezifische Schicht: Installation, die Dekoratoren @plugin, die CLI owncast-plugin-py und Tests. Handler, APIs, Berechtigungen und das Manifest funktionieren in beiden SDKs gleich und haben ihre eigenen Referenzseiten.
Wie es in die Referenzdokumente passt
Die gemeinsamen Referenznamen von Handlern und APIs in ihrer kanonischen (camelCase) Form. To read it as Python, apply one rule: decorators, host methods, and payload attribute access are snake_case. Raw wire dictionaries (msg.raw) and scenario JSON keep their camelCase wire names. Quick orientation:
| In der Referenz | In Python |
|---|---|
| Definieren Sie einen Handler | eine mit @plugin.* dekorierte Funktion |
Handler für ein Ereignis (z.B. chat.message.received) | @plugin.on_chat_message |
Rufen Sie eine Host-API auf (z.B. owncast.chat.sendAction) | owncast.chat.send_action(text): snake_case |
Payload-Felder (z.B. msg.user.displayName) | msg.user.display_name, msg.client_id. msg.raw für das rohe dict. |
Filterergebnis (filter.pass()) | filter.pass_() (trailing _: pass ist ein Schlüsselwort). Auch filter.modify(...) / filter.drop(reason) |
| Declare a plugin-owned custom hook | @plugin.on("my.event"). Owned as <your-slug>.my.event |
| Bauen / Testen Sie Ihr Plugin | owncast-plugin-py package / owncast-plugin-py test |
Voraussetzungen
- Ein Owncast-Server, den Sie verwalten können, Version 0.3.0 oder neuer.
- Python 3.8 oder neuer.
Installieren
Erstellen Sie ein Projekt mit new, indem Sie den Slug übergeben. uvx führt den Scaffolder direkt von PyPI aus, ohne etwas zu installieren:
uvx owncast-plugin-py new my-plugin
cd my-plugin
Installieren Sie das SDK, um die CLI owncast-plugin-py in Ihren PATH für die Schritte Bauen, Testen, Bereitstellen und Paketieren zu erhalten:
uv tool install owncast-plugin-py # or: pip install owncast-plugin-py
Sie erhalten ein bereit zum Bauen Verzeichnis:
my-plugin/
├── plugin.manifest.json name, slug, version, permissions
├── README.md how to build, test, package, and install it
├── INSTRUCTIONS.md optional, rendered as a tab in the admin
├── AGENTS.md notes for AI coding agents
├── .agents/ a bundled skill for AI coding agents
├── src/plugin.py your code, with a sample handler
└── __tests__/*.test.json a sample scenario test
Schreiben Sie ein Plugin
Importieren Sie plugin, owncast und filter, und registrieren Sie Handler mit Dekoratoren. Jeder Dekorator abonniert ein Ereignis. Das SDK leitet die Abonnentenliste des Manifests davon ab, welche Handler Sie definieren.
from owncast_plugin import plugin, owncast, filter
@plugin.on_chat_message
def greet(msg):
name = msg.user.display_name if msg.user else "someone"
owncast.chat.send(f"{name} said: {msg.body}")
@plugin.filter_chat_message
def block_spam(msg):
return filter.drop("spam") if "spam" in msg.body else filter.pass_()
The module exports five things:
plugin: das Dekorator-Register.@plugin.on_chat_message,@plugin.filter_chat_message,@plugin.on_stream_started,@plugin.on_tick,@plugin.on_fediverse_followund der Rest spiegeln die Laufzeitevents in der Handlerreferenz wider. Two take a key:@plugin.on("custom.event")declares a local custom hook that the host owns as<your-slug>.custom.event, while@plugin.on_tab_content("slug")and@plugin.on_page_content("slug")provide dynamic viewer-page HTML. For tab content, the decorator argument matches amanifest.tabsobject key. For extra page content, it matchesmanifest.extraPageContent.slug. Zwei benötigen keinen Schlüssel:@plugin.on_page_stylesund@plugin.on_page_scriptsgeben CSS und JavaScript zurück, die zur Anfragezeit in die Viewer-Seite injiziert werden, getrennt vonui.modify.owncast: der Host-API-Namespace. Methodennamen sindsnake_case(owncast.chat.send_action,owncast.kv.get_json). Jeder Aufruf ist an die entsprechende Berechtigung gebunden, die Sie in Ihrem Manifest deklarieren. Siehe die APIs-Referenz.filter, filtert Ergebnisse, die von einemfilter_chat_message-Handler zurückgegeben werden:filter.pass_()(trailing underscore,passist ein Python-Schlüsselwort),filter.modify(...),filter.drop(reason).auth_check: verdict helpers for the@plugin.on_auth_checkhandler of anauth.gateplugin:auth_check.ok(),auth_check.refresh(ttl=...),auth_check.deny(reason).CommandContext: what a declared command'srun()receives:.msg,.user,.command,.invoked_as,.args, and.arg_string, plusreply(text)andreply_privately(text)helpers. Import it for type hints.
Payloads sind Attributobjekte mit snake_case-Zugriffsnamen über das Wire-JSON (msg.body, msg.user.display_name, msg.client_id). Verwenden Sie msg.raw für das zugrunde liegende dict. Hostaufrufe, die JSON-Objekte zurückgeben, kommen als dieselben Attributobjekte zurück (owncast.server.info().name). Listen kommen als Python-Listen zurück.
Zwei weitere Python-Idiome, die es wert sind, bekannt zu sein, werden beide umfassend dokumentiert (mit Python-Beispielen) auf den Fachseiten:
- HTTP-Routing: Plugins mit
http.servedeklarieren Routen mit Dekoratoren:@plugin.get/post/put/delete/patch(path),@plugin.route(path, methods=[...]),@plugin.on_http_request(path)und ein bloßes@plugin.on_http_request, das alles abfängt. Ein Handler gibt eindictzurück ({status, body, headers}), einstr(→ 200) oderNone(→ 204). Siehe HTTP-Dienste. - Chat-Befehle:
plugin.commands({...})deklariert Befehle mit Aliassen, Moderator-Gating und pro Benutzer Cooldowns. Der integrierte!helplistet sie automatisch auf. Siehe Chat-Befehle.
Die CLI
Die Installation des SDK gibt Ihnen owncast-plugin-py. Bauen und Paketieren bündelt Ihren Quellcode und benötigt keinen Compiler. The test, serve, and package commands fetch the prebuilt host binaries on first use (package runs its install-time load check through the test binary):
| Befehl | Was es tut |
|---|---|
owncast-plugin-py new my-plugin | Erstellen Sie ein neues Plugin-Projekt in ./my-plugin |
owncast-plugin-py build | Bauen Sie src/plugin.py (ohne Verpackung) |
owncast-plugin-py test | Bauen Sie dann die Szenarien von __tests__/ aus |
owncast-plugin-py serve | Lokaler Entwicklungsserver (-p/--port zum Ändern des Ports, standardmäßig auf 8080) |
owncast-plugin-py package | Build + Bundle → <slug>.ocpkg: die Datei, die Sie versenden |
owncast-plugin-py package # produces my-plugin.ocpkg
owncast-plugin-py test
owncast-plugin-py serve # POST /_dev/chat to drive event handlers
All four run against the current directory. The positional project argument defaults to ., so inside the project you pass nothing. From elsewhere, pass the project directory: owncast-plugin-py package my-plugin. Die .ocpkg ist das einzige Distributionsartefakt. Siehe Verpackung & Verteilung für Inhalte und Installationsanweisungen.
Einschränkungen, die Sie kennen sollten
Einige Dinge darüber, wie Python-Plugins gebaut werden, beeinflussen, wie Sie sie schreiben. Sie importieren owncast_plugin normalerweise für Editor-Unterstützung und Unit-Tests. Der Build kümmert sich um den Rest.
- Nur reines Python und kein
pip. Es gibt keinenpip installSchritt: Sie fügen Drittanbieter-Code hinzu, indem Sie dessen (reinen Python-) Quellcode in Ihr Projekt kopieren. Abhängigkeiten mit C-Erweiterungen (numpy, pandas und dergleichen) laden nicht. Siehe Drittanbieter-Bibliotheken. Für ausgehendes HTTP verwenden Sieowncast.http.fetch, nichtrequests. - Schattieren Sie keine Namen aus der Standardbibliothek. Eine top-level
def json(...)(oder ein anderer stdlib-Name) schattet das echte Modul und kann den Build unterbrechen, und eine Moduldatei, die nach einem stdlib-Modul benannt ist (src/json.py), wird zugunsten des echten ignoriert. Nennen Sie siejson_responseund dergleichen. - Der Eingang kann keine relativen Importe verwenden. In
src/plugin.pyimportieren Sie Ihre eigenen Module absolut (from helpers import ...), nichtfrom . import helpers. Ein relativer Import dort schlägt fehl, obwohl relative Importe innerhalb der Module eines Pakets in Ordnung sind. snake_casein the code you write, in contrast to the JS SDK's camelCase:send_action,get_json,msg.user.display_name,filter.pass_(). Raw wire dictionaries (msg.raw) and scenario JSON stay camelCase.
Drittanbieter-Bibliotheken
Es gibt kein pip install und keine requirements.txt. Eine Drittanbieterbibliothek funktioniert nur, wenn sie reines Python ist und Sie ihren Quellcode in src/ kopieren, wo sie zu einem Ihrer eigenen Module wird.
pip install macht nichtsDas Installieren eines Pakets in einem virtuellen Umfeld hat keine Auswirkung auf das, was versendet wird, und import requests schlägt zur Laufzeit fehl. Um eine Bibliothek zu verwenden, kopieren Sie deren .py-Quellcode nach src/ (ein einzelnes Modul oder ein Paketverzeichnis) und importieren Sie es.
- C-Erweiterungen funktionieren niemals. numpy, pandas, lxml, Pydantic v2 und alles andere mit kompiliertem Code wird nicht geladen.
- Sie besitzen den gesamten Baum. Wenn eine Bibliothek, die Sie einfügen, andere Drittanbieterpakete importiert, kopieren Sie diese ebenfalls, oder wählen Sie eine kleinere aus.
- Verwenden Sie
owncast.http.fetchfür ausgehendes HTTP, nichtrequests.
Die Standardbibliothek ist verfügbar, solange das Modul reines Python ist (json, re, datetime, base64 und dergleichen).
Zum Beispiel benötigt das page-content-demo Beispiel eine Mustache-Vorlagendatei. Statt ein Vorlagenpaket zu kopieren, versendet es einen kleinen Mustache-Subset-Renderer.
Tests
Tests sind __tests__/*.test.json-Szenario-Dateien, die mit owncast-plugin-py test ausgeführt werden. Das Format ist identisch mit dem des JS SDK, sodass ein Python-Port eines Plugins die Testszenarien der JS-Version unverändert wiederverwenden kann. Jedes Szenario dispatches Ereignisse / HTTP-Anfragen und beansprucht auf beobachtete Nebeneffekte (chatSends, kv-Schreibungen, HTTP-Antworten, …).
[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]
Das vollständige Szenariodatenmodell (Schrittarten, given-Zustand, expect-Assertions) finden Sie auf der Seite Testing. Beachten Sie, dass das Szenario-JSON die Draht-Feldnamen (camelCase: displayName, clientId) verwendet, da es Hostereignisse beschreibt und nicht Ihren Python-Code.
Status
Die Laufzeit, die owncast-plugin-py-CLI (Scaffold, Build, Test, Serve, Package), die vollständige Host-API, HTTP-Routing und .ocpkg-Verpackung funktionieren alle heute. Alle JS-Beispielplugins haben Python-Entsprechungen unter examples/python/.
Wohin Sie als Nächstes gehen können
- Handlerreferenz: jedes Ereignis, auf das Sie sich abonnieren können (lesen Sie Namen als
snake_case). - APIs-Referenz: jede
owncast.*-Methode und die Berechtigung, die sie benötigt. - Testing: das vollständige Szenariodatenmodell.
- Verpackung & Verteilung: Bauen des
.ocpkgund Installation. - Python-Beispielplugins: eines pro Funktion, jedes ein vollständiger Ausgangspunkt, den Sie kopieren können.
- SDK-Quellcode: das
owncast-plugin-py-Paket und das Toolchain.
Improve this page
See something missing or incorrect? Edit the English version of this page or help improve translations.
Gabe Kangas