SDK Python
Lo SDK Python, owncast-plugin-py, ti permette di creare plugin per Owncast in Python. Scrivi Python ordinario usando i decoratori. Un passaggio di build lo converte in un singolo plugin installabile che viene eseguito in sandbox all'interno del server Owncast: lo stesso formato .ocpkg e l'insieme completo di funzionalità dello SDK JavaScript, perciò un plugin Python è un pari di primo livello rispetto a uno JS.
Gli SDK per i plugin sono nuovi in Owncast 0.3.0 e l'API è ancora in evoluzione. Se trovi un bug o hai un suggerimento, per favore apri un issue o chatta in tempo reale con la community.
Questa pagina è lo strato specifico per Python: installazione, i decorator @plugin, la CLI owncast-plugin-py e i test. Handler, API, permessi e il manifesto funzionano allo stesso modo in entrambi gli SDK e hanno pagine di riferimento dedicate.
Come corrisponde alla documentazione di riferimento
La documentazione di riferimento condivisa nomina handler e API nella loro forma canonica (camelCase). 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:
| Nella reference | In Python |
|---|---|
| Definire un handler | una funzione decorata @plugin.* |
Handler per un evento (es. chat.message.received) | @plugin.on_chat_message |
Chiamare un'API host (es. owncast.chat.sendAction) | owncast.chat.send_action(text): snake_case |
Campi del payload (es. msg.user.displayName) | msg.user.display_name, msg.client_id. msg.raw per il dict grezzo |
Risultato del filtro (filter.pass()) | filter.pass_() (underscore finale _: pass è una parola chiave). Anche filter.modify(...) / filter.drop(reason) |
| Declare a plugin-owned custom hook | @plugin.on("my.event"). Owned as <your-slug>.my.event |
| Compila / testa il tuo plugin | owncast-plugin-py package / owncast-plugin-py test |
Prerequisiti
- Un server Owncast che puoi amministrare, versione 0.3.0 o successiva.
- Python 3.8 o successivo.
Installazione
Scaffolda un progetto con new, passando lo slug. uvx esegue lo scaffolder direttamente da PyPI senza installare nulla:
uvx owncast-plugin-py new my-plugin
cd my-plugin
Installa lo SDK per ottenere la CLI owncast-plugin-py nel tuo PATH per le fasi di build, test, serve e package:
uv tool install owncast-plugin-py # or: pip install owncast-plugin-py
Hai una directory pronta per la build:
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
Scrivi un plugin
Importa plugin, owncast e filter, e registra gli handler con i decorator. Ogni decoratore si sottoscrive a un evento. Lo SDK ricava la lista di sottoscrizioni del manifesto dagli handler che definisci.
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: il registro dei decorator.@plugin.on_chat_message,@plugin.filter_chat_message,@plugin.on_stream_started,@plugin.on_tick,@plugin.on_fediverse_follow, e il resto rispecchiano gli eventi di runtime nel riferimento agli handler. 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. Due non richiedono una chiave:@plugin.on_page_stylese@plugin.on_page_scriptsrestituiscono CSS e JavaScript iniettati nella pagina del visualizzatore al momento della richiesta, soggetti aui.modify.owncast: lo spazio dei nomi dell'API host. I nomi dei metodi sono insnake_case(owncast.chat.send_action,owncast.kv.get_json). Ogni chiamata è soggetta alla corrispondente autorizzazione che dichiari nel manifesto. Vedi il Riferimento API.filter, risultati del filtro restituiti da un handlerfilter_chat_message:filter.pass_()(underscore finale,passè una parola chiave di Python),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.
I payload sono oggetti ad attributi con accessor in snake_case sul JSON trasmesso (msg.body, msg.user.display_name, msg.client_id). Usa msg.raw per il dict sottostante. Le chiamate host che restituiscono oggetti JSON ritornano come gli stessi oggetti ad attributi (owncast.server.info().name). Le liste tornano come liste Python.
Due ulteriori idiomi Python da conoscere, entrambi documentati per intero (con esempi in Python) nelle pagine dedicate:
- Routing HTTP: i plugin con
http.servedichiarano rotte con decorator:@plugin.get/post/put/delete/patch(path),@plugin.route(path, methods=[...]),@plugin.on_http_request(path), e un@plugin.on_http_requestsenza argomenti come catch-all. Un handler restituisce undict({status, body, headers}), unastr(→ 200), oNone(→ 204). Vedi Servire HTTP. - Comandi chat:
plugin.commands({...})dichiara comandi con alias, restrizione ai moderatori e cooldown per utente. Il!helpintegrato li elenca automaticamente. Vedi Comandi chat.
La CLI
Installando lo SDK ottieni owncast-plugin-py. Le operazioni di build e packaging impacchettano il tuo sorgente e non richiedono un compilatore. 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):
| Comando | Cosa fa |
|---|---|
owncast-plugin-py new my-plugin | Crea un nuovo progetto plugin in ./my-plugin |
owncast-plugin-py build | Costruisce src/plugin.py (senza packaging) |
owncast-plugin-py test | Costruisce, poi esegue gli scenari in __tests__/ |
owncast-plugin-py serve | Server di sviluppo locale (-p/--port per cambiare la porta, predefinita 8080) |
owncast-plugin-py package | Build + bundle → <slug>.ocpkg: il file che distribuisci |
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. Il .ocpkg è l'unico artefatto di distribuzione. Vedi Packaging e distribuzione per cosa contiene e come installarlo.
Vincoli da conoscere
Alcune cose su come i plugin Python vengono costruiti plasmano il modo in cui li scrivi. Importi owncast_plugin normalmente per il supporto dell'editor e per i test unitari. La build si occupa del resto.
- Solo Python puro e niente
pip. Non esiste un passopip install: aggiungi codice di terze parti copiando il suo sorgente (solo Python puro) nel tuo progetto. Le dipendenze con estensioni C (numpy, pandas e simili) non verranno caricate. Vedi Librerie di terze parti. Per le richieste HTTP in uscita usaowncast.http.fetch, nonrequests. - Non oscurare i nomi della libreria standard. Una
deftop-leveljson(...)(o qualsiasi altro nome di stdlib) oscura il modulo reale e può rompere il build, e un file di modulo nominato come un modulo della stdlib (src/json.py) viene ignorato a favore di quello reale. Nominalijson_responsee simili. - The entry can't use relative imports. In
src/plugin.py, import your own modules absolutely (from helpers import ...), notfrom . import helpers. Un import relativo lì fa fallire il build, anche se gli import relativi all'interno dei moduli di un package sono validi. 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.
Librerie di terze parti
Non esiste pip install e non c'è requirements.txt. Una libreria di terze parti funziona solo se è pura Python e ne copi il sorgente in src/, dove diventa uno dei tuoi moduli.
pip install non ha effettoInstallare un pacchetto in un virtualenv non influisce su ciò che viene distribuito, e import requests fallisce a runtime. Per usare una libreria, copia il suo sorgente .py in src/ (un singolo modulo o una directory di package) e importalo.
- Le estensioni C non funzionano mai. numpy, pandas, lxml, Pydantic v2 e qualsiasi altra cosa con codice compilato non verrà caricata.
- Sei responsabile dell'intero albero. Se una libreria che copi importa altri pacchetti di terze parti, copiali anche tu o scegli una alternativa più piccola.
- Usa
owncast.http.fetchper le richieste HTTP in uscita, nonrequests.
La libreria standard è disponibile, purché il modulo sia puro Python (json, re, datetime, base64 e simili).
Per esempio, l'esempio page-content-demo necessita del templating Mustache. Piuttosto che copiare un pacchetto di templating, fornisce un piccolo renderer di un sottoinsieme di Mustache proprio.
Test
I test sono file scenario __tests__/*.test.json eseguiti con owncast-plugin-py test. Il formato è identico a quello dello SDK JS, quindi una porta Python di un plugin può riutilizzare gli scenari di test della versione JS letteralmente. Ogni scenario invia eventi / richieste HTTP e asserisce sugli effetti collaterali osservati (chatSends, scritture kv, risposte HTTP, …).
[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]
Il modello di dati completo degli scenari (tipi di step, stato given, asserzioni expect) è sulla pagina Test. Nota che il JSON degli scenari usa i nomi dei campi wire (camelCase: displayName, clientId), poiché descrive eventi host, non il tuo codice Python.
Stato
Il runtime, la CLI owncast-plugin-py (scaffold, build, test, serve, package), l'API host completa, il routing HTTP e il packaging .ocpkg funzionano già oggi. Tutti i plugin d'esempio JS hanno controparti Python sotto examples/python/.
Prossimi passi
- Riferimento agli handler: ogni evento a cui puoi iscriverti (leggi i nomi in
snake_case). - Riferimento API: ogni metodo
owncast.*e l'autorizzazione che richiede. - Test: il modello di dati completo degli scenari.
- Packaging e distribuzione: creazione del
.ocpkge installazione. - Esempi di plugin Python: uno per funzionalità, ognuno è un punto di partenza completo che puoi copiare.
- Sorgente SDK: il package
owncast-plugin-pye la toolchain.
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas