Vai al contenuto principale

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.

Python plugins require Owncast v0.3.0

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 referenceIn Python
Definire un handleruna 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 pluginowncast-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 a manifest.tabs object key. For extra page content, it matches manifest.extraPageContent.slug. Due non richiedono una chiave: @plugin.on_page_styles e @plugin.on_page_scripts restituiscono CSS e JavaScript iniettati nella pagina del visualizzatore al momento della richiesta, soggetti a ui.modify.
  • owncast: lo spazio dei nomi dell'API host. I nomi dei metodi sono in snake_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 handler filter_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_check handler of an auth.gate plugin: auth_check.ok(), auth_check.refresh(ttl=...), auth_check.deny(reason).
  • CommandContext: what a declared command's run() receives: .msg, .user, .command, .invoked_as, .args, and .arg_string, plus reply(text) and reply_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.serve dichiarano rotte con decorator: @plugin.get/post/put/delete/patch(path), @plugin.route(path, methods=[...]), @plugin.on_http_request(path), e un @plugin.on_http_request senza argomenti come catch-all. Un handler restituisce un dict ({status, body, headers}), una str (→ 200), o None (→ 204). Vedi Servire HTTP.
  • Comandi chat: plugin.commands({...}) dichiara comandi con alias, restrizione ai moderatori e cooldown per utente. Il !help integrato 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):

ComandoCosa fa
owncast-plugin-py new my-pluginCrea un nuovo progetto plugin in ./my-plugin
owncast-plugin-py buildCostruisce src/plugin.py (senza packaging)
owncast-plugin-py testCostruisce, poi esegue gli scenari in __tests__/
owncast-plugin-py serveServer di sviluppo locale (-p/--port per cambiare la porta, predefinita 8080)
owncast-plugin-py packageBuild + 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 passo pip 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 usa owncast.http.fetch, non requests.
  • Non oscurare i nomi della libreria standard. Una def top-level json(...) (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. Nominali json_response e simili.
  • The entry can't use relative imports. In src/plugin.py, import your own modules absolutely (from helpers import ...), not from . 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_case in 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.

Owncat cautions you
pip install non ha effetto

Installare 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.fetch per le richieste HTTP in uscita, non requests.

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


Improve this page

See something missing or incorrect? Edit this page and improve the documentation for everyone.

Contributors to this documentation
Gabe KangasGabe Kangas
O
Owncast