SDK Python
O SDK Python, owncast-plugin-py, permite que você crie plugins Owncast em Python. Você escreve Python comum usando decoradores. Um passo de build transforma isso em um único plugin instalável que roda isolado dentro do servidor Owncast: o mesmo formato .ocpkg e o conjunto completo de recursos do SDK JavaScript, portanto um plugin Python é um equivalente de primeira classe de um em JS.
Os SDKs de plugin são novos no Owncast 0.3.0 e a API ainda está evoluindo. Se encontrar um bug ou tiver uma sugestão, por favor abra uma issue ou converse ao vivo com a comunidade.
Esta página é a camada específica para Python: instalação, os decoradores @plugin, a CLI owncast-plugin-py e testes. Manipuladores, APIs, permissões e o manifesto funcionam da mesma forma em ambos os SDKs e têm suas próprias páginas de referência.
Como isso se relaciona com a documentação de referência
A referência compartilhada nomeia manipuladores e APIs em sua 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:
| Na referência | Em Python |
|---|---|
| Definir um manipulador | uma função decorada @plugin.* |
Manipulador para um evento (por exemplo chat.message.received) | @plugin.on_chat_message |
Chamar uma API do host (por exemplo owncast.chat.sendAction) | owncast.chat.send_action(text): snake_case |
Campos do payload (por exemplo msg.user.displayName) | msg.user.display_name, msg.client_id. msg.raw para o dict bruto |
Resultado do filtro (filter.pass()) | filter.pass_() (sufixo _: pass é uma palavra-chave). Também filter.modify(...) / filter.drop(reason) |
| Declare a plugin-owned custom hook | @plugin.on("my.event"). Owned as <your-slug>.my.event |
| Construa / teste seu plugin | owncast-plugin-py package / owncast-plugin-py test |
Pré-requisitos
- Um servidor Owncast que você possa administrar, versão 0.3.0 ou superior.
- Python 3.8 ou mais recente.
Instalação
Estruture um projeto com new, passando o slug. uvx executa o scaffolder diretamente do PyPI sem instalar nada:
uvx owncast-plugin-py new my-plugin
cd my-plugin
Instale o SDK para ter a CLI owncast-plugin-py no seu PATH para as etapas build, test, serve e package:
uv tool install owncast-plugin-py # or: pip install owncast-plugin-py
Você terá um diretório pronto para 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
Escreva um plugin
Importe plugin, owncast e filter, e registre manipuladores com decoradores. Cada decorador se inscreve em um evento. O SDK deriva a lista de assinaturas do manifesto a partir dos manipuladores que você define.
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: o registro de decoradores.@plugin.on_chat_message,@plugin.filter_chat_message,@plugin.on_stream_started,@plugin.on_tick,@plugin.on_fediverse_follow, e o resto espelham os eventos em tempo de execução na referência de manipuladores. 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. Dois não recebem chave:@plugin.on_page_stylese@plugin.on_page_scriptsretornam CSS e JavaScript injetados na página do visualizador no momento da requisição, condicionados porui.modify.owncast: o namespace da API do host. Os nomes de métodos sãosnake_case(owncast.chat.send_action,owncast.kv.get_json). Cada chamada é controlada pela permissão correspondente que você declara no seu manifesto. Veja a referência de APIs.filter: resultados de filtro retornados de um manipuladorfilter_chat_message:filter.pass_()(sufixo_,passé uma palavra-chave do 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.
Os payloads são objetos de atributos com acessos em snake_case sobre o JSON transmitido (msg.body, msg.user.display_name, msg.client_id). Use msg.raw para o dict subjacente. Chamadas ao host que retornam objetos JSON voltam como esses mesmos objetos de atributos (owncast.server.info().name). As listas retornam como listas Python.
Mais duas práticas idiomáticas do Python que vale a pena conhecer, ambas documentadas por completo (com exemplos em Python) nas páginas correspondentes:
- Roteamento HTTP: plugins com
http.servedeclaram rotas com decoradores:@plugin.get/post/put/delete/patch(path),@plugin.route(path, methods=[...]),@plugin.on_http_request(path), e um@plugin.on_http_requestsimples como captura geral. Um manipulador retorna umdict({status, body, headers}), umastr(→ 200), ouNone(→ 204). Veja Servindo HTTP. - Comandos de chat:
plugin.commands({...})declara comandos com apelidos, controle por moderador e cooldowns por usuário. O!helpembutido os lista automaticamente. Veja Comandos de chat.
A CLI
Ao instalar o SDK você obtém owncast-plugin-py. Os comandos de build e package empacotam seu código-fonte e não necessitam de 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 | O que faz |
|---|---|
owncast-plugin-py new my-plugin | Cria um novo projeto de plugin em ./my-plugin |
owncast-plugin-py build | Compila src/plugin.py (sem empacotar) |
owncast-plugin-py test | Compila, então executa os cenários em __tests__/ |
owncast-plugin-py serve | Servidor local de desenvolvimento (-p/--port para mudar a porta, padrão 8080) |
owncast-plugin-py package | Build + bundle → <slug>.ocpkg: o arquivo que você distribui |
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. O .ocpkg é o único artefato de distribuição. Veja Empacotamento e distribuição para saber o que vai dentro e como instalá-lo.
Restrições a conhecer
Algumas características de como plugins Python são construídos influenciam a forma de escrevê-los. Você importa owncast_plugin normalmente para suporte do editor e testes unitários. O build cuida do resto.
- Apenas Python puro, e sem
pip. Não há etapapip install: você adiciona código de terceiros copiando seu código-fonte (em Python puro) para dentro do seu projeto. Dependências com extensões em C (numpy, pandas e similares) não irão carregar. Veja Bibliotecas de terceiros. Para HTTP de saída useowncast.http.fetch, nãorequests. - Não oculte nomes da biblioteca padrão. Um
def json(...)no nível superior (ou qualquer outro nome da stdlib) oculta o módulo real e pode quebrar o build, e um arquivo de módulo chamado como um módulo da stdlib (src/json.py) é ignorado em favor do real. Nomeie-osjson_responsee semelhantes. - The entry can't use relative imports. In
src/plugin.py, import your own modules absolutely (from helpers import ...), notfrom . import helpers. Uma importação relativa ali falha no build, embora importações relativas dentro dos módulos do próprio pacote funcionem. 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 terceiros
Não existe pip install nem requirements.txt. Uma biblioteca de terceiros funciona somente se for Python puro e você copiar seu código-fonte para src/, onde ela se torna um dos seus próprios módulos.
Instalar um pacote em um virtualenv não afeta o que é distribuído, e import requests falha em tempo de execução. Para usar uma biblioteca, copie seu código .py para src/ (um único módulo ou um diretório de pacote) e importe-o.
- Extensões em C nunca funcionam. numpy, pandas, lxml, Pydantic v2 e qualquer outra coisa com código compilado não irão carregar.
- Você é responsável por toda a árvore. Se uma biblioteca que você copia importa outros pacotes de terceiros, copie-os também, ou escolha uma menor.
- Use
owncast.http.fetchpara HTTP de saída, nãorequests.
A biblioteca padrão está disponível, desde que o módulo seja Python puro (json, re, datetime, base64 e similares).
Por exemplo, o exemplo page-content-demo precisa de template Mustache. Ao invés de copiar um pacote de template, ele inclui um pequeno renderizador de um subconjunto de Mustache próprio.
Testes
Os testes são arquivos de cenário __tests__/*.test.json executados com owncast-plugin-py test. O formato é idêntico ao do SDK JS, então uma porta Python de um plugin pode reutilizar os cenários de teste da versão JS literalmente. Cada cenário dispara eventos / requisições HTTP e verifica efeitos colaterais observados (chatSends, gravações de kv, respostas HTTP, …).
[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]
O modelo completo de dados de cenário (tipos de passo, estado given, asserções expect) está na página Testes. Observe que o JSON do cenário usa os nomes de campo da wire (camelCase: displayName, clientId), pois descreve eventos do host, não o seu código Python.
Status
O runtime, a CLI owncast-plugin-py (scaffold, build, test, serve, package), a API completa do host, roteamento HTTP e o empacotamento .ocpkg já funcionam hoje. Todos os plugins de exemplo em JS têm contrapartes em Python em examples/python/.
Próximos passos
- Referência de manipuladores: todo evento ao qual você pode se inscrever (leia os nomes como
snake_case). - Referência de APIs: cada método
owncast.*e a permissão que ele necessita. - Testes: o modelo completo de dados de cenário.
- Empacotamento e distribuição: construir o
.ocpkge instalá-lo. - Plugins de exemplo em Python: um por funcionalidade, cada um um ponto de partida completo que você pode copiar.
- Fonte do SDK: o pacote
owncast-plugin-pye a cadeia de ferramentas.
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas