Python SDK
The Python SDK, owncast-plugin-py, lets you author Owncast plugins in Python. 你用装饰器编写普通的 Python 代码。 构建步骤会将其打包为一个可安装的插件,在 Owncast 服务器内沙箱运行:使用相同的 .ocpkg 格式并具有与 JavaScript SDK 相同的完整功能集,因此 Python 插件是与 JS 插件并列的一等公民。
本页为 Python 专用层:安装、@plugin 装饰器、owncast-plugin-py CLI,以及测试。 处理器、API、权限和清单在两个 SDK 中的工作方式相同,并各自拥有参考页面。
如何映射到参考文档
共享参考文档以规范(camelCase)形式命名处理器和 API。 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:
| 在参考文档中 | 在 Python 中 |
|---|---|
| 定义一个处理器 | 一个由 @plugin.* 装饰的函数 |
事件的处理器(例如 chat.message.received) | @plugin.on_chat_message |
调用宿主 API(例如 owncast.chat.sendAction) | owncast.chat.send_action(text): 使用 snake_case |
有效负载字段(例如 msg.user.displayName) | msg.user.display_name, msg.client_id. msg.raw 表示原始 dict |
过滤结果(filter.pass()) | filter.pass_()(尾随 _:pass 是一个关键字)。 还有 filter.modify(...) / filter.drop(reason) |
| Declare a plugin-owned custom hook | @plugin.on("my.event"). Owned as <your-slug>.my.event |
| 构建 / 测试你的插件 | owncast-plugin-py package / owncast-plugin-py test |
前置条件
- 你可以管理的 Owncast 服务器,版本 0.3.0 或更高。
- Python 3.8 或更高。
安装
使用 new 搭建项目脚手架,传入 slug。 uvx 从 PyPI 直接运行脚手架生成器,无需安装任何东西:
uvx owncast-plugin-py new my-plugin
cd my-plugin
安装 SDK 以将 owncast-plugin-py CLI 放入你的 PATH,用于构建、测试、serve 和 package 等步骤:
uv tool install owncast-plugin-py # or: pip install owncast-plugin-py
你会得到一个可直接构建的目录:
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
编写插件
导入 plugin、owncast 和 filter,并使用装饰器注册处理器。 每个装饰器订阅一个事件。 SDK 根据你定义的处理器派生清单的订阅列表。
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:装饰器注册表。@plugin.on_chat_message,@plugin.filter_chat_message,@plugin.on_stream_started,@plugin.on_tick,@plugin.on_fediverse_follow, 以及其他项与运行时事件一一对应,见处理器参考。 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. 有两个不需要 key:@plugin.on_page_styles和@plugin.on_page_scripts在请求时返回注入到观众页面的 CSS 和 JavaScript,受ui.modify限制。owncast:宿主 API 命名空间。 方法名为snake_case(owncast.chat.send_action、owncast.kv.get_json)。 每次调用均受你在清单中声明的相应权限限制。 详见 API 参考。filter:由filter_chat_message处理器返回的过滤结果:filter.pass_()(尾随下划线,pass是 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.
有效负载是带有属性访问器的对象,以 snake_case 访问底层的 wire JSON(msg.body、msg.user.display_name、msg.client_id)。 使用 msg.raw 获取底层 dict。 返回 JSON 对象的宿主调用会以相同的属性对象形式返回(owncast.server.info().name)。 列表将作为 Python 列表返回。
另外两个值得了解的 Python 习惯用法,均在对应章节中有完整文档(含 Python 示例):
- HTTP 路由:带有
http.serve的插件使用装饰器声明路由:@plugin.get/post/put/delete/patch(path)、@plugin.route(path, methods=[...])、@plugin.on_http_request(path),以及不带参数的@plugin.on_http_request捕获所有请求。 处理器返回dict({status, body, headers})、str(→ 200)或None(→ 204)。 参见 HTTP 服务。 - 聊天命令:
plugin.commands({...})声明具有别名、版主权限控制和每用户冷却时间的命令。 内置的!help会自动列出这些命令。 参见 聊天命令。
命令行工具 (CLI)
安装 SDK 会为你提供 owncast-plugin-py。 构建和打包会将你的源代码打包,不需要编译器。 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):
| 命令 | 功能说明 |
|---|---|
owncast-plugin-py new my-plugin | 在 ./my-plugin 中生成新的插件项目。 |
owncast-plugin-py build | 构建 src/plugin.py(不打包) |
owncast-plugin-py test | 先构建,然后运行 __tests__/ 场景。 |
owncast-plugin-py serve | 本地开发服务器(使用 -p/--port 更改端口,默认 8080) |
owncast-plugin-py package | 构建 + 打包 → <slug>.ocpkg:这是你要分发的文件 |
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 是唯一的分发产物。 有关其中内容及安装方法,请参见 打包与分发。
需要了解的限制
关于 Python 插件的构建方式有一些注意事项,会影响你的编写方式。 为了编辑器支持和单元测试,你通常会导入 owncast_plugin。 构建过程会处理其余部分。
- 仅限纯 Python,且不使用
pip。 不存在pip install步骤:你需要通过将第三方代码的(纯 Python)源代码复制到你的项目中来添加它。 带有 C 扩展的依赖(如 numpy、pandas 等)无法加载。 参见 第三方库。 对于外发 HTTP 请求请使用owncast.http.fetch,不要使用requests。 - 不要覆盖标准库名称。 顶层的
def json(...)(或任何其他标准库名称)会遮蔽真实模块并可能破坏构建,而以标准库模块命名的模块文件(src/json.py)会被忽略,优先使用真实的模块。 把它们命名为json_response等。 - The entry can't use relative imports. In
src/plugin.py, import your own modules absolutely (from helpers import ...), notfrom . import helpers. 那里的相对导入会导致构建失败,不过包自身模块内的相对导入是可以的。 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.
第三方库
没有 pip install 步骤,也没有 requirements.txt。 第三方库只有在为纯 Python 并且你将其源代码复制到 src/ 中时才能工作,在那里它将成为你的模块之一。
将包安装到 virtualenv 对最终分发无效,且 import requests 在运行时会失败。 要使用某个库,请将其 .py 源文件复制到 src/(单个模块或包目录),然后导入它。
- C 扩展永远不起作用。 numpy、pandas、lxml、Pydantic v2 以及任何带有已编译代码的库都无法加载。
- 你负责整个依赖树。 如果你复制的库导入了其他第三方包,也要一并复制,或者选择更小的库。
- 对于外发 HTTP 使用
owncast.http.fetch,不要使用requests。
标准库可用,只要模块为纯 Python(json、re、datetime、base64 等)。
例如,page-content-demo 示例需要 Mustache 模板。 它没有复制模板包,而是随附了一个小型的 Mustache 子集渲染器。
测试
测试是 __tests__/*.test.json 场景文件,使用 owncast-plugin-py test 来运行。 格式与 JS SDK 完全相同,因此插件的 Python 移植版可以逐字重用 JS 版本的测试场景。 每个场景会派发事件 / HTTP 请求,并断言观测到的副作用(chatSends、kv 写入、HTTP 响应等)。
[
{
"name": "echoes the message",
"events": [
{
"event": "chat.message.received",
"payload": { "user": { "id": "u1", "displayName": "alice" }, "body": "hi" }
}
],
"expect": { "chatSends": ["alice said: hi"] }
}
]
完整的场景数据模型(步骤类型、given 状态、expect 断言)请见 测试 页面。 注意场景 JSON 使用 wire 字段名(camelCase:displayName、clientId),因为它描述的是宿主事件,而不是你的 Python 代码。
状态
运行时、owncast-plugin-py CLI(scaffold、build、test、serve、package)、完整的宿主 API、HTTP 路由和 .ocpkg 打包目前均可使用。 所有 JS 示例插件在 examples/python/ 目录下都有对应的 Python 版本。
下一步
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas