traPtitech / traQ

traQ - traP Internal Messenger Application Backend
MIT License
424 stars 29 forks source link

BOTのWebRTC状態 / WebSocketのサポート #985

Closed motoki317 closed 3 years ago

motoki317 commented 4 years ago

NOTE: /wsとは別に、BOT用のWebSocketエンドポイントを作る BOT用のWebSocketエンドポイントを実装するにあたって、BOTのイベントも現在のWebhook通知とWebSocketのリアルタイム送受信の2種類を実装すると良いかもしれない

motoki317 commented 3 years ago

traQ-S WebSocket Bot - traP HackMD

traQ-S WebSocket Bot

Milestones

目的

Bot のイベントの受け取り方に従来の HTTP Mode に加えて WebSocket Mode を追加する。 Mode は作成時、また作成後に開発者によって変更可能。 WebSocket Mode では、Bot がイベントを受け取るための static IP を用意する必要が無く、例えば Firewall の内側でもホスト可能 (Intro to Socket Mode | Slack を参照)。

また、WebSocket の Bot → traQ の通信で通常のクライアントのように Qall session の同期が可能になる。

WebSocket Mode では繋いでいないときにはイベントを送らないで済むため、長期的にはサーバー側の負荷軽減にも繋がる... かもしれない。

仕様策定

Endpoint

WebSocket BOT は GET /api/v3/bots/ws に接続する。

通常ユーザーが traQ-S_UI クライアントを通じて使用する GET /api/v3/ws とは異なるエンドポイントであり、当然仕様も異なるため注意。

Payload

Mode によって形式がやや異なる。 HTTP Mode では Header で送っている情報があるため。

HTTP Mode

ref: traQ BOT Console

...
Content-Type: application/json
X-TRAQ-BOT-REQUEST-ID: RequestID(UUID)
X-TRAQ-BOT-TOKEN: VerificationToken
X-TRAQ-BOT-EVENT: PING
...

{"eventTime":"2019-05-07T04:50:48.582586882Z"}

WebSocket Mode

{"type":"PING","reqId":"requestId","body":{"eventTime":"2019-05-07T04:50:48.582586882Z"}}

Command

WebSocket Mode では、WebSocket を通じてイベントを受け取るだけでなく、Bot から traQ 側に情報を送信することができる。 便宜上 WebSocket command と呼ぶ。

Command が成功した場合、サーバーからの返信は無い。 失敗した場合、以下の Message が送られる。

{"type":"ERROR","body":"message"}

rtcstate Command

Qall の session 同期をする。 Qall に参加する Bot は session を同期することが望ましい。必須ではないが、しない場合は Qall ユーザー一覧として表示されなくなる。

通常クライアントと同じ動作。

マイクミュートの場合、state.micmutedを追加する。

e.g. チャンネルAで行われているQall(sessionId1)に参加 ↑ bot1 rtcstate:channelA:qall:sessionId1 チャンネルAで行われているQall(sessionId1)にてマイクをミュート ↑ bot1 rtcstate:channelA:qall.micmuted:sessionId1

SDK

メジャーな各言語で WebSocket Mode 用 SDK を用意できると良いかもしれない。

イメージ: Intro to Socket Mode | Slack

const { App } = require('@slack/bolt');

const app = new App({
  token: process.env.BOT_TOKEN,
  appToken: process.env.SLACK_APP_TOKEN,
  socketMode: true,
});

(async () => {
  await app.start();
  console.log('⚡️ Bolt app started');
})();