SDK de Python
El SDK de Python, owncast-plugin-py, te permite crear plugins de Owncast en Python. Escribes Python ordinario con decoradores. Un paso de construcción lo convierte en un solo plugin instalable que se ejecuta en un entorno seguro dentro del servidor Owncast: el mismo formato .ocpkg y conjunto completo de funciones que el SDK de JavaScript, así que un plugin de Python es un compañero de primera clase de uno de JS.
Los SDK de plugins son completamente nuevos en Owncast 0.3.0 y la API aún está evolucionando. Si encuentras un error o tienes una sugerencia, por favor abre un problema o chatea en vivo con la comunidad.
Esta página es la capa específica de Python: instala, los decoradores @plugin, la CLI de owncast-plugin-py y pruebas. Manejadores, APIs, permisos y el manifiesto funcionan igual en ambos SDK y tienen sus propias páginas de referencia.
Cómo se mapea a la documentación de referencia
Los nombres de referencia compartidos manejadores y APIs en su forma canónica (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:
| En la referencia | En Python |
|---|---|
| Define un manejador | una función decorada con @plugin.* |
Manejador para un evento (por ejemplo, chat.message.received) | @plugin.on_chat_message |
Llama a una API del host (por ejemplo, owncast.chat.sendAction) | owncast.chat.send_action(text): snake_case |
Campos de carga útil (por ejemplo, msg.user.displayName) | msg.user.display_name, msg.client_id. msg.raw para el diccionario crudo |
Resultado del filtro (filter.pass()) | filter.pass_() (guion bajo final: pass es una palabra clave). También filter.modify(...) / filter.drop(reason) |
| Declare a plugin-owned custom hook | @plugin.on("my.event"). Owned as <your-slug>.my.event |
| Construye / prueba tu plugin | owncast-plugin-py package / owncast-plugin-py test |
Requisitos previos
- Un servidor Owncast que puedas administrar, versión 0.3.0 o más reciente.
- Python 3.8 o más reciente.
Instalar
Empieza un proyecto con new, pasando el slug. uvx ejecuta el generador directamente desde PyPI sin instalar nada:
uvx owncast-plugin-py new my-plugin
cd my-plugin
Instala el SDK para obtener la CLI de owncast-plugin-py en tu PATH para los pasos de construcción, prueba, servicio y empaquetado:
uv tool install owncast-plugin-py # or: pip install owncast-plugin-py
Tienes un directorio listo para construir:
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
Escribir un plugin
Importa plugin, owncast, y filter, y registra manejadores con decoradores. Cada decorador se suscribe a un evento. El SDK obtiene la lista de suscripciones del manifiesto a partir de los manejadores que defines.
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: el registro de decoradores.@plugin.on_chat_message,@plugin.filter_chat_message,@plugin.on_stream_started,@plugin.on_tick,@plugin.on_fediverse_follow, y el resto reflejan los eventos en tiempo de ejecución en la referencia de manejadores. 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. Dos no requieren clave:@plugin.on_page_stylesy@plugin.on_page_scriptsdevuelven CSS y JavaScript inyectados en la página de visualizador en el momento de la solicitud, bajoui.modify.owncast: el espacio de nombres de la API del host. Los nombres de los métodos sonsnake_case(owncast.chat.send_action,owncast.kv.get_json). Cada llamada está controlada por el permiso correspondiente declarado en tu manifiesto. Consulta la referencia de APIs.filter, resultados de filtro devueltos de un manejadorfilter_chat_message:filter.pass_()(guion bajo final,passes una palabra clave de 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.
Las cargas útiles son objetos atributo con accesores snake_case sobre el JSON de la red (msg.body, msg.user.display_name, msg.client_id). Usa msg.raw para el diccionario subyacente. Las llamadas del host que devuelven objetos JSON regresan como los mismos objetos atributo (owncast.server.info().name). Las listas regresan como listas de Python.
Dos más modismos de Python que vale la pena conocer, ambos documentados en su totalidad (con ejemplos de Python) en las páginas temáticas:
- Enrutamiento HTTP: los plugins con
http.servedeclaran rutas con decoradores:@plugin.get/post/put/delete/patch(path),@plugin.route(path, methods=[...]),@plugin.on_http_request(path), y un simple@plugin.on_http_requestcaptura todo. Un manejador devuelve undict({status, body, headers}), unstr(→ 200), oNone(→ 204). Consulta Sirviendo HTTP. - Comandos de chat:
plugin.commands({...})declara comandos con alias, control de moderador y tiempos de espera por usuario. El integrado!helplos lista automáticamente. Consulta Comandos de chat.
La CLI
Instalar el SDK te da owncast-plugin-py. Construyendo y empaquetando tu fuente y no necesitas compilador. 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 | Lo que hace |
|---|---|
owncast-plugin-py new my-plugin | Genera un nuevo proyecto de plugin en ./my-plugin |
owncast-plugin-py build | Construye src/plugin.py (sin empaquetar) |
owncast-plugin-py test | Construye, luego ejecuta los escenarios de __tests__/ |
owncast-plugin-py serve | Servidor de desarrollo local (-p/--port para cambiar el puerto, predeterminado 8080) |
owncast-plugin-py package | Construye + empaqueta → <slug>.ocpkg: el archivo que envías |
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. .ocpkg es el único artefacto de distribución. Consulta Empaquetado y distribución para lo que va dentro y cómo instalarlo.
Restricciones a conocer
Algunas cosas sobre cómo se construyen los plugins de Python moldean cómo los escribes. Importas owncast_plugin normalmente para soporte de editor y pruebas unitarias. La construcción se encarga del resto.
- Solo Pure-Python, y no
pip. No hay un pasopip install: agregas código de terceros copiando su fuente (Pure-Python) en tu proyecto. Dependencias con extensiones C (numpy, pandas, etc.) no se cargarán. Consulta Bibliotecas de terceros. Para HTTP saliente usaowncast.http.fetch, norequests. - No oscures nombres de la biblioteca estándar. Un
def json(...)de nivel superior (o cualquier otro nombre de stdlib) oscurece el módulo real y puede romper la construcción, y un archivo de módulo llamado como un módulo de stdlib (src/json.py) es ignorado a favor del real. Nómbralosjson_responsey cosas por el estilo. - La entrada no puede usar importaciones relativas. En
src/plugin.py, importa tus propios módulos de forma absoluta (from helpers import ...), nofrom . import helpers. Una importación relativa ahí falla la construcción, aunque las importaciones relativas dentro de los propios módulos de un paquete están bien. 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.
Bibliotecas de terceros
No hay pip install y no hay requirements.txt. Una biblioteca de terceros funciona solo si es Python puro y copias su fuente en src/, donde se convierte en uno de tus propios módulos.
Instalar un paquete en un virtualenv no afecta lo que envías, y import requests falla en tiempo de ejecución. Para usar una biblioteca, copia su fuente .py en src/ (un solo módulo o un directorio de paquete) y luego imprtala.
- Las extensiones C nunca funcionan. numpy, pandas, lxml, Pydantic v2, y cualquier otra cosa con código compilado no se cargarán.
- Tú posees todo el árbol. Si una biblioteca que copias importa otros paquetes de terceros, cópialos también, o elige uno más pequeño.
- Usa
owncast.http.fetchpara HTTP saliente, norequests.
La biblioteca estándar está disponible, siempre que el módulo sea Python puro (json, re, datetime, base64, etc.).
Por ejemplo, el ejemplo page-content-demo necesita plantillas Mustache. En lugar de copiar un paquete de plantillas, envía un pequeño renderizador de Mustache-subconjunto propio.
Pruebas
Las pruebas son archivos de escenario __tests__/*.test.json ejecutados con owncast-plugin-py test. El formato es idéntico al SDK de JS, así que un puerto de un plugin de Python puede reutilizar los escenarios de prueba de la versión de JS textualmente. Cada escenario despacha eventos / solicitudes HTTP y aserciones sobre efectos secundarios observados (chatSends, escrituras kv, respuestas HTTP, …).
[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]
El modelo de datos completo del escenario (tipos de pasos, estado given, aserciones expect) está en la página de Pruebas. Ten en cuenta que el JSON del escenario usa los nombres de campo wire (camelCase: displayName, clientId), ya que describe eventos del host, no tu código Python.
Estado
El tiempo de ejecución, la CLI de owncast-plugin-py (generar, construir, probar, servir, empaquetar), la API completa del host, el enrutamiento HTTP y el empaquetado .ocpkg funcionan todos hoy. Todos los plugins de ejemplo de JS tienen contrapartes de Python en examples/python/.
Dónde ir después
- Referencia de manejadores: cada evento al que puedes suscribirte (lee nombres como
snake_case). - Referencia de APIs: cada método
owncast.*y el permiso que necesita. - Pruebas: el modelo de datos completo del escenario.
- Empaquetado y distribución: construyendo el
.ocpkge instalándolo. - Ejemplos de plugins de Python: uno por característica, cada uno un punto de partida completo que puedes copiar.
- Código fuente del SDK: el paquete y la cadena de herramientas
owncast-plugin-py.
Improve this page
See something missing or incorrect? Edit the English version of this page or help improve translations.
Gabe Kangas