Owncastをプラグインで拡張する
Owncastはプラグインで拡張できます:サーバーが実行時に読み込む小さなプログラムで、チャットメッセージ、ストリームイベント、フェディバースの活動、およびHTTPリクエストに反応します。 プラグインはサンドボックス内で動作するため、プラグインがクラッシュしてもサーバーはダウンしません。また、ホストは明確な権限モデルを強制し、管理者はプラグインが何にアクセスできるかを常に知っています。
プラグインはOwncast 0.3.0で導入された新機能であり、APIはまだ進化中です。 バグに遭遇したり、提案がある場合は、問題を報告するか、コミュニティとライブチャットをします。
You can write a plugin with the JavaScript SDK, the Python SDK, or as a native WebAssembly module. The two SDKs are the recommended paths for most plugins. Native WebAssembly is an advanced option for compiled languages and direct access to the plugin wire protocol.
作成できるもの
- キーワードやコマンドに応答するチャットボット、リマインダーを投稿するボット、投票を実施するボット、またはスパムをモデレートするボット。
- 視聴者に届く前にチャットメッセージを再構成または削除するフィルター。
- ストリームの上に重ねられたオーバーレイが、プラグインのHTTPエンドポイントとやり取りします。
- OwncastをDiscord、フェディバース、ブラウザプッシュ、または任意のHTTPSサービスに接続する統合。
- プラグイン固有の設定のために、Owncastの管理UIにタブを追加する管理ツール。
- ストリームの下に表示されるアクションボタン、ウィジェット、寄付ページ、または提供するその他のものを起動します。
SDK内のすべての例プラグインは、コピーできる完全な出発点です。
Choose an authoring path
All three paths produce the same .ocpkg format and use the same manifest, permissions, events, and Owncast APIs.
- **JavaScript**は
@owncast/plugin-sdkを使用します。 Scaffold withnpx create-owncast-plugin, writedefinePlugin({ ... }), and build withnpm run package. - **Python**は
owncast-plugin-pyを使用します。 Scaffold withuvx owncast-plugin-py new, write decorated functions, and build withowncast-plugin-py package. - Native WebAssembly with Rust, TinyGo, AssemblyScript, Zig, or another compiled language. Implement the wire protocol directly and package the compiled module as
plugin.wasm.
The same echo bot in each SDK:
// JavaScript
const { definePlugin, owncast } = require('@owncast/plugin-sdk');
module.exports = definePlugin({
onChatMessage(msg) {
owncast.chat.send(`echo: ${msg.body}`);
},
});
# Python
from owncast_plugin import plugin, owncast
@plugin.on_chat_message
def echo(msg):
owncast.chat.send(f"echo: {msg.body}")
どのように機能するか
プラグインは、プラグインのマニフェスト、コンパイルされたコード、及び静的アセットを含む単一の.ocpkgファイルです。 管理者はファイルをOwncastのdata/plugins/ディレクトリにドロップし、管理のプラグインページからそれを有効にします。
有効化されると、プラグインはOwncastプロセス内で実行されます。 定義したハンドラーは、一致するイベントが発生すると発火します。 呼び出したAPI(チャット送信、設定読み取り、URL取得)はホストを通過し、マニフェストで宣言した権限をチェックします。
Each enabled plugin uses more server memory. JavaScript and Python share one runtime per language, so the first plugin in either language has a larger one-time cost. A native WebAssembly plugin loads its own compiled module instead of a shared language runtime.
プラグインができること
- イベントにサブスクライブします。 チャットメッセージ、ストリームの開始と停止、フェディバースのフォロー、新しいチャットユーザーの参加。 ハンドラーメソッドを定義すると、SDKがサブスクリプションを導出します。
- チャットをフィルタします。 ブロードキャストされる前にすべてのチャットメッセージを確認し、修正したり削除したりできます。
- Owncast APIを呼び出します。
owncast.chat.send(text)、owncast.kv.get(key)、owncast.http.fetch(url)など、ほとんどが宣言された権限によって制限されます。 - HTTPを提供します。 すべてのプラグインは、静的アセットおよび動的ハンドラーの両方のために、URL空間
/plugins/<your-slug>/...を所有できます。 - UIを追加します。 管理ページ、アクションボタン、プラグインのスタイルシート、プラグインのスクリプト、またはマニフェストでの追加コンテンツのHTMLブロックを宣言し、Owncastはそれらを自身のクロームにインラインで追加します。
- アクセス制限を設けます。 プラグインは、サイトの認証プロバイダーになることができます。 閲覧者がページ、動画、チャット、APIに到達する前にサインイン(OAuth、パスワード、HTTP経由の任意の方法)が必要です。
プラグインができないこと
設計上:
- ホストのファイルシステム、ネットワーク、またはプロセスに直接アクセスできません。 サンドボックスがこれを強制します。 プラグインはホストAPIが公開した機能のみを使用でき、宣言された権限のもとでのみ動作します。
- アイデンティティのなりすましは許可されません。 各プラグインは1つのチャットID(インストール時にOwncastが提供するボット)を取得し、外向きのフェディバース投稿はストリーマーの自アカウントから行われます。
- クロスプラグインの読み取りはできません。 各プラグインのキーと値のストアは名前空間で区切られています。
- 無限のチャットブロックはできません。 フィルター呼び出しは50msに制限され、繰り返してエラーを発生させるプラグインは自動的に無効化されます。
そのため、管理者はサードパーティプラグインをすべてのコード行を監査せずにインストールできます。 信頼境界はマニフェストの権限リストです。
次に進むべきステップ
- クイックスタート。 新しいプラグインをスキャフォールディングし、ビルドし、インストールします。
- JavaScript, Python, and Native WebAssembly. Choose a language and build path.
- マニフェストリファレンス。
plugin.manifest.jsonが含むことができるすべてのフィールド。 - チャットプラグイン。 ボット、自動化ツール、およびチャットフィルターを構築します。
- イベント。 プラグインがサブスクライブできるすべてのイベントと、ペイロードの形状。
- Owncast API。 すべての
owncast.*メソッド、その役割、および必要な権限。 - 権限。 完全なリストと、セキュリティモデルがどのように機能するか。
- HTTP提供。 プラグインからURLを提供し、ブラウザにリアルタイムイベントをプッシュします。
- UIの提供。 管理ページを登録し、ストリームの下にアクションボタンを追加します。
- テスト。 実際のランタイムを通じてプラグインを駆動するシナリオテスト。
- パッケージングと公開。
.ocpkgをバンドルし、インストールし、ディレクトリにリストします。
ソース
- SDKソース:github.com/owncast/plugin-sdk
- Example plugins: JavaScript · Python
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas