Passer au contenu principal

SDK Python

Le SDK Python, owncast-plugin-py, vous permet de créer des plugins Owncast en Python. Vous écrivez du Python ordinaire avec des décorateurs. Une étape de construction le transforme en un seul plugin installable qui fonctionne en mode isolé à l'intérieur du serveur Owncast : le même format .ocpkg et l'ensemble des fonctionnalités que le SDK JavaScript, donc un plugin Python est un égal de premier niveau d'un plugin JS.

Python plugins require Owncast v0.3.0

Les SDK de plugins sont tout nouveaux dans Owncast 0.3.0 et l'API est encore en évolution. Si vous rencontrez un bug ou avez une suggestion, veuillez ouvrir un problème ou discuter en direct avec la communauté.

Cette page est la couche spécifique à Python : installation, les décorateurs @plugin, l'interface en ligne de commande owncast-plugin-py, et les tests. Les gestionnaires, API, permissions et le manifeste fonctionnent de la même manière dans les deux SDK et ont leurs propres pages de référence.

Comment cela se rapporte aux documents de référence

Les noms de référence partagés des gestionnaires et des API dans leur forme canonique (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:

Dans la référenceEn Python
Définir un gestionnaireune fonction décorée @plugin.*
Gestionnaire pour un événement (par exemple chat.message.received)@plugin.on_chat_message
Appeler une API hôte (par exemple owncast.chat.sendAction)owncast.chat.send_action(text) : snake_case
Champs de charge utile (par exemple msg.user.displayName)msg.user.display_name, msg.client_id. msg.raw pour le dict brut
Résultat de filtre (filter.pass())filter.pass_() (underscore final : pass est un mot clé). Aussi filter.modify(...) / filter.drop(reason)
Declare a plugin-owned custom hook@plugin.on("my.event"). Owned as <your-slug>.my.event
Construire / tester votre pluginowncast-plugin-py package / owncast-plugin-py test

Prérequis

  • Un serveur Owncast que vous pouvez administrer, version 0.3.0 ou plus récente.
  • Python 3.8 ou plus récent.

Installer

Créer un projet avec new, en passant le slug. uvx exécute le créateur directement depuis PyPI sans rien installer :

uvx owncast-plugin-py new my-plugin
cd my-plugin

Installez le SDK pour obtenir l'interface en ligne de commande owncast-plugin-py dans votre PATH pour les étapes de construction, de test, de service et d'emballage :

uv tool install owncast-plugin-py # or: pip install owncast-plugin-py

Vous obtenez un répertoire prêt à être construit :

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

Écrire un plugin

Importez plugin, owncast et filter, et enregistrez des gestionnaires avec des décorateurs. Chaque décorateur s'abonne à un événement. Le SDK dérive la liste d'abonnement du manifeste à partir des gestionnaires que vous définissez.

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 : le registre des décorateurs. @plugin.on_chat_message, @plugin.filter_chat_message, @plugin.on_stream_started, @plugin.on_tick, @plugin.on_fediverse_follow, et les autres reflètent les événements d'exécution dans la référence des gestionnaires. 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. Deux ne prennent pas de clé : @plugin.on_page_styles et @plugin.on_page_scripts renvoient CSS et JavaScript injectés dans la page du visualiseur au moment de la demande, sous contrôle de ui.modify.
  • owncast : l'espace de noms de l'API hôte. Les noms de méthode sont snake_case (owncast.chat.send_action, owncast.kv.get_json). Chaque appel est soumis à la permission correspondante que vous déclarez dans votre manifeste. Voir la référence des API.
  • filter, filtre les résultats retournés d'un gestionnaire filter_chat_message : filter.pass_() (underscore final, pass est un mot clé 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.

Les charges utiles sont des objets d'attribut avec des accesseurs snake_case sur le JSON de transport (msg.body, msg.user.display_name, msg.client_id). Utilisez msg.raw pour le dict sous-jacent. Les appels hôtes qui renvoient des objets JSON reviennent en tant que mêmes objets d'attribut (owncast.server.info().name). Les listes reviennent sous forme de listes Python.

Deux autres idiomes Python qui valent la peine d'être connus, tous documentés en détail (avec des exemples Python) sur les pages de sujet :

  • Routage HTTP : les plugins avec http.serve déclarent des routes avec des décorateurs : @plugin.get/post/put/delete/patch(path), @plugin.route(path, methods=[...]), @plugin.on_http_request(path), et un simple @plugin.on_http_request catch-all. Un gestionnaire renvoie un dict ({status, body, headers}), un str (→ 200), ou None (→ 204). Voir Serving HTTP.
  • Commandes de chat : plugin.commands({...}) déclare des commandes avec des alias, un contrôle modérateur et des temps de cooldown par utilisateur. Le !help intégré les liste automatiquement. Voir Commandes de chat.

L'interface en ligne de commande

L'installation du SDK vous donne owncast-plugin-py. Construire et empaqueter votre source et ne nécessite pas de compilateur. 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):

CommandeCe qu'elle fait
owncast-plugin-py new my-pluginCréer un nouveau projet de plugin dans ./my-plugin
owncast-plugin-py buildConstruire src/plugin.py (sans empaquetage)
owncast-plugin-py testConstruire, puis exécuter les scénarios de __tests__/
owncast-plugin-py serveServeur de développement local (-p/--port pour changer le port, par défaut 8080)
owncast-plugin-py packageConstruire + empaqueter → <slug>.ocpkg : le fichier que vous expédiez
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. Le .ocpkg est l'unique artefact de distribution. Voir Emballage & distribution pour ce qui est à l'intérieur et comment l'installer.

Contraintes à connaître

Quelques choses sur la façon dont les plugins Python sont construits influencent la façon dont vous les écrivez. Vous importez owncast_plugin normalement pour le support de l'éditeur et les tests unitaires. La construction s'occupe du reste.

  • Uniquement du Python pur, et pas de pip. Il n'y a pas d'étape pip install : vous ajoutez du code tiers en copiant sa source (Python pur) dans votre projet. Les dépendances avec des extensions C (numpy, pandas, et similaires) ne se chargeront pas. Voir Bibliothèques tierces. Pour HTTP sortant, utilisez owncast.http.fetch, pas requests.
  • Ne masquez pas les noms de la bibliothèque standard. Un def json(...) de niveau supérieur (ou tout autre nom de stdlib) masque le véritable module et peut briser la construction, et un fichier de module nommé d'après un module stdlib (src/json.py) est ignoré au profit du véritable. Nommez-les json_response et des choses similaires.
  • L'entrée ne peut pas utiliser d'importations relatives. Dans src/plugin.py, importez vos propres modules de manière absolue (from helpers import ...), pas from . import helpers. Un import relatif là échoue la construction, bien que les importations relatives à l'intérieur des propres modules d'un package soient acceptables.
  • 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.

Bibliothèques tierces

Il n'y a pas de pip install et pas de requirements.txt. Une bibliothèque tierce ne fonctionne que si elle est pure Python et que vous copiez sa source dans src/, où elle devient un de vos propres modules.

Owncat cautions youpip install ne fait rien

L'installation d'un package dans un virtualenv n'a aucun effet sur ce qui est expédié, et import requests échoue à l'exécution. Pour utiliser une bibliothèque, copiez son source .py dans src/ (un seul module ou un répertoire de package) et importez-la.

  • Les extensions C ne fonctionnent jamais. numpy, pandas, lxml, Pydantic v2, et tout autre code compilé ne se chargera pas.
  • Vous êtes responsable de l'ensemble de l'arbre. Si une bibliothèque que vous copiez importe d'autres packages tiers, copiez-les aussi, ou choisissez un plus petit.
  • Utilisez owncast.http.fetch pour HTTP sortant, pas requests.

La bibliothèque standard est disponible, tant que le module est en Python pur (json, re, datetime, base64, et similaires).

Par exemple, l'exemple page-content-demo nécessite une modélisation Mustache. Plutôt que de copier un package de modélisation, il expédie un petit moteur de rendu Mustache-subset qui lui est propre.

Tests

Les tests sont des fichiers de scénario __tests__/*.test.json exécutés avec owncast-plugin-py test. Le format est identique à celui du SDK JS, donc un port Python d'un plugin peut réutiliser les scénarios de test de la version JS tels quels. Chaque scénario envoie des événements / requêtes HTTP et affirme les effets secondaires observés (chatSends, écritures kv, réponses HTTP, …).

[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]

Le modèle de données complet du scénario (types d'étapes, état given, assertions expect) est sur la page Testing. Notez que le JSON du scénario utilise les noms de champs wire (camelCase : displayName, clientId), car il décrit les événements hôtes, et non votre code Python.

Statut

L'exécution, l'interface en ligne de commande owncast-plugin-py (créer, construire, tester, servir, empaqueter), l'intégralité de l'API hôte, le routage HTTP, et l'emballage .ocpkg fonctionnent tous aujourd'hui. Tous les plugins d'exemple JS ont des équivalents Python sous examples/python/.

Où aller ensuite


Improve this page

See something missing or incorrect? Edit the English version of this page or help improve translations.

Contributors to this documentation
Gabe KangasGabe Kangas
O
Owncast