SyncSing Studio / SyncSing NC Lite の拡張機能(プラグイン)を C# で作るための小さなライブラリです。 ファイルは SyncSingPlugin.cs(接続)と SyncSingApi.g.cs(API 1.1 の全メソッドの型付き API・自動生成)の 2 つで、 外部の NuGet パッケージは使いません(参照は System.Web.Extensions だけ)。MIT ライセンスです。
使い方
using SyncSing.Plugin.Sdk;
static int Main()
{
using (var client = PluginClient.Connect()) // 標準入力の 1 行目 → 名前付きパイプ → hello
{
client.OnCommand = (command, source, pressed) =>
{
client.Api.NotifyShow(title: "押されました", body: command);
return null; // null は {"ok": true}
};
client.On(Events.Meters, data => { /* 音量メーター */ });
client.Subscribe(250, Events.Meters); // meters は 250 ms ごと
client.Panel("main").Send(Block.Status("動作中", "ok"), Block.Button("試す", "test", true));
client.Run(); // plugin.shutdown か切断まで待つ
}
return 0;
}PluginClient.Connect()は、作業フォルダ(アプリが manifest.json のあるフォルダで起動します)のmanifest.jsonからversionを読んで hello に入れます。違う版を名乗るとアプリに断られます。client.Audio()(1.4.0・API 1.1): 音専用のパイプ(AudioPipe)。OnFrame = (pcm, rate, seq, timeUs) => …でマイクの声(処理後)を受け取り、Write(pcm, 24000)で配信に乗せる音を送ります。先にaudio.capture.start/audio.inject.openを呼びます。PluginClient.Connect(pluginVersion, info, connectTimeoutMs, capabilities)(1.3.2):capabilitiesで hello に入れる対応の一覧を渡せます(既定はpanel・command。ツールを持つならnew[] { "panel", "command", "tool" })。公式の拡張機能もこの SDK でつないでいます。- 窓口のエラーは
PluginException(Codeはpermission_denied・busy・invalid_params・rate_limited・not_availableなど)。busy・rate_limitedは少し待ってやり直せます(Retryable)。 pingには SDK が自動で答えます。plugin.shutdownを受けたらOnShutdownを呼び、Run()が戻ります。5 秒以内に終わってください。- イベントのハンドラは専用の 1 本のスレッドで、届いた順に呼ばれます。
OnCommand・OnToolなどは ThreadPool のスレッドで呼ばれます。どちらも中からCallしてかまいません(読み取りスレッドからCallすると固まるので、SDK が例外にします)。
「押したのに何も起きない」を防ぐ決まり(SDK 1.2)
OnCommand/OnToolは すぐ返す(アプリは 10 秒で諦め、利用者には「返事がありません」と出ます)。時間のかかる処理(OBS への問い合わせ・ファイルの書き出しなど)はThreadPool.QueueUserWorkItemで別スレッドへ回し、先にPanel("main").Send(...)で「いま何をしているか」を出します。処理が 2 秒を超えるとplugin.logに警告が残ります(client.SlowRequestMsで変えられます)。- パネルは
client.Panel("パネル名").Send(ブロック...)で更新します。待たず、最後に渡した内容だけを順番どおりに送ります(1 回ごとに別スレッドでPanelUpdateすると、ほぼ同時に作った 2 つが逆の順で届いて古い画面が残ることがあります)。40 ブロックを超えたぶんは切られ、断られた理由はplugin.logに残ります(LastErrorでも分かります)。 command.invoke/tool.runを受け取ったことと、かかった時間は SDK がplugin.logに書きます([sdk] 12:34:56.789 command.invoke start→[sdk] command.invoke → ok (3 ms))。「押したのに何も起きない」ときは、まずここを見てください。- 押した結果は右下の通知に出ます:
OnCommand/OnToolがJson.Obj("ok", false, "message", "…")を返せばその文、Json.Obj("ok", true, "message", "…")なら成功の文。message を省けば、そのあと 8 秒のあいだにパネルの先頭のBlock.Statusが変わったときにその文が出ます(warn / error は ⚠ 付き)。「すぐ返事をして裏で確かめる」作りでは、裏で断った理由を先頭のBlock.Status(…, "warn")に出してください。 - 経過時間・残り時間(計測中のカウントなど)は
Block.Timer(text, 経過秒, 全体秒)で出します。画面が 1 秒ごとに自分で進めるので、秒数を文字として送り続ける(2 秒ごとの取り直しで飛び飛びに見える)必要がありません。 - イベントのハンドラの中で長く待たないこと(1 本のスレッドで順番に処理するので、後ろのイベントが待たされます)。
- 書いてよいファイルは
client.Info.DataDirとclient.Info.LogDirの中だけです(開発者規約)。 - **全メソッドは
client.Apiにあります**(client.Api.MicSet(strength: 3)・client.Api.ChatPost("こんにちは")など。名前はメソッド名を PascalCase に、引数は省略できるものがnull既定)。戻り値は〇〇Result(例:ChatPostResultのPosted・Failed・Text)で、項目ごとに型の付いたプロパティ(数値はlong?/double?、真偽はbool?、無いときはnull)と、返事そのもののRawを持ちます。入れ子の項目はDictionary<string, object>/List<object>です(形はsdk/schemas/api-1.1.jsonのresult)。 - イベントの名前は
Events.Meters・Events.ChatMessageなど。client.Call("メソッド名", Json.Obj(...))で直接呼ぶこともできます。 - 前からある
client.StateGet()・client.NotifyShow(…)・client.PanelUpdate(…)などの便利メソッドもそのまま使えます。 - 使える窓口は manifest の
permissionsに書き、利用者が許可したものだけです(client.Has("chat.read"))。
ビルド
- 雛形
sdk/templates/dotnet-basicはこのファイルをソースとして取り込むので、そのまま EXE 1 つになります。 - MSBuild(Visual Studio の「.NET デスクトップ開発」)が要ります。