Skip to content

Larksuite(Feishu)

Feishu(国内版)と Lark Suite(海外版)は同じ OpenAPI 表面を共有します。 Ongrid プロバイダーは両者を 1 つの統合として扱います。テナントに合う ベース URL を選んでください。

モード何をするか
通知カスタムボット webhook 経由で Feishu/Lark グループに アラートをプッシュ。
IM ブリッジWebSocket 長接続を使った双方向エージェントチャット。

通知モード(カスタムボット)

Feishu 送信者は Feishu / Lark カスタムボット ペイロードを、ボット 管理者が提供する webhook URL にポストします。ペイロードの形:

json
{
  "msg_type": "text",
  "content": {"text": "[CRITICAL] swap_high node-01\nswap_in_pages > 1000 for 5m\nsource: alert\ndedupe: alert:swap_high:device=7"},
  "timestamp": "1717012345",
  "sign": "<base64-hmac>"
}

署名 —— sign フィールド

Feishu カスタムボットで 签名校验 (signature verification) をオンに すると、Feishu が共有 secret を渡してくれます。送信者は sign フィールドを こう計算します:

text
stringToSign = timestamp + "\n" + secret
sign         = base64(HMAC-SHA256(key=stringToSign, message=<empty>))

そう —— secret が HMAC の 鍵素材 と string-to-sign の一部の両方を 担います。これは Feishu がドキュメント化しているアルゴリズムで、 signFeishu が実装するものです。

Secret フィールドを空欄にすると、Ongrid は sign / timestamp なしで ポストします —— ボットの署名検証をオフ(または IP allowlist 経由)に しているときだけ使えます。

セットアップ

  1. Feishu グループで → 设置 → 群机器人 → 添加机器人 → 自定义机器人。 名前とアバターを設定。
  2. 签名校验 にチェック、secret をコピー。
  3. webhook URL をコピー —— こんな感じ: https://open.feishu.cn/open-apis/bot/v2/hook/<uuid>(Lark Suite なら …/open.larksuite.com/…)。
  4. Ongrid で:Settings → Channels → New → Provider = feishu → Endpoint = webhook URL → Secret = 署名 secret。

カスタムボット ≠ アプリ

通知モードは カスタムボット(グループスコープ、webhook のみ)を 使います。IM ブリッジモードは Feishu アプリ(テナントスコープ、OAuth

  • events)を使います。これらは異なる概念で、認証情報は重なりません。

IM ブリッジモード(長接続ストリーム)

双方向ブリッジは公式 github.com/larksuite/oapi-sdk-go/v3/ws クライアント が配信する Feishu 長接続 を使います。manager が Feishu のイベント エンドポイントにダイヤルし、Feishu が WebSocket でイベントをプッシュ します —— 公開 webhook URL は不要です。

なぜ webhook ではなくストリームか

webhook モード(mode=webhook)は後方互換のためスキーマでサポート されていますが、長接続ストリームが推奨パスです:

  • manager 上の公開 ingress 不要。
  • supervisor の backoff 付き再接続を再利用(SDK が独自の再接続を持ち、 supervisor は終端的な失敗に対する外側のループを追加)。
  • SDK が内部で署名検証 + AES 復号を処理するので、ストリーム版で encrypt_key を埋める必要はありません —— webhook 専用です。

認証マッピング

im_appsFeishu での意味
provider"feishu"
mode"stream"(推奨)または "webhook"
app_idFeishu app_idcli_…)。
app_secretFeishu app_secret
verify_tokenオプション。webhook モードの署名検証で使う。
encrypt_keywebhook モードでは必須、ストリームモードでは無視。
allow_fromオプション。Feishu はテナントゲートされるので allowlist は必須でない。

default_locale

中国語で書く operator の Feishu テナントなら zh、英語の Lark チームなら en。空(デフォルト)は LLM がユーザーをミラーします。

セットアップ

  1. 开发者后台 → 创建企业自建应用app_id + app_secret を取得。
  2. 应用功能 → 机器人 → 有効化。テストチャットにボットを追加。
  3. 权限管理 → 付与:
    • im:message —— ボット宛のメッセージを読む。
    • im:message.group_at_msg —— グループメンションイベント。
    • im:message.p2p_msg —— DM イベント。
    • im:message:send_as_bot —— SendText / EditText
  4. 事件订阅 → 长连接模式 (long-connection) → 有効化。
  5. Ongrid で:Settings → IM bridge → New → Provider = feishu → Mode = streamapp_id + app_secret を貼り付け。Save して Enable
  6. チャットでボットを @。エージェントが拾います。

tenant_access_token キャッシュ

アウトバウンド呼び出し(SendTextEditText)は tenant_access_token で認証し、有効期限の 200 秒以内になると先回りでリフレッシュします。 トークンは Client インスタンスごとにキャッシュされ、sync.Mutex で 保護されます。認証情報の rotation はクライアントの再構築を意味します。 tenantAccessToken 参照。

edit にも msg_type が必要

Feishu の PUT /open-apis/im/v1/messages/<id> はボディに msg_type を 要求します —— 省略すると code: 99992402(フィールドバリデーション失敗) が返ります(「edit message」のドキュメントページにはそれが明示されて いないにもかかわらず)。プロバイダーは常に msg_type: text を送ります。

Webhook モード(レガシー、互換性のため保持)

webhook モードは UI で選択可能なまま。supervisor が自分で署名を検証し、 ペイロードを復号します。

署名検証

HTTP ハンドラーは X-Lark-Signature を読み、 VerifyEventSignature を走らせます:

text
sig = sha256(timestamp + nonce + encrypt_key + body), hex-encoded

そう —— HMAC ではなく SHA-256 です。encrypt_key がハッシュ入力内で 共有 secret の役割を果たします。verifier は悪い入力で panic することは ありません。不一致は ErrBadSignature を返します。

ペイロード復号

encrypt_key が設定されていると、Feishu はイベント JSON を AES-256-CBC で ラップします。復号は以下を使います:

  • 鍵:SHA-256(encrypt_key)
  • IV:base64 デコードした暗号文の最初の 16 バイト。
  • パディング:PKCS#7。

DecryptEvent 参照。

可能ならストリームモードを選んでください

webhook モードには公開 HTTPS エンドポイントと上記の encrypt_key 配管が必要です。長接続ストリームは両方を回避します。webhook モードに 手を伸ばすのは特にそれが必要なときのみ(例:Feishu イベントを既存の 公開 webhook ルーターと統合)。

クセ

  • Feishu ストリームクライアントは今日 allow_from を強制しません —— Feishu プラットフォーム自体がテナントゲートされているので、企業 メンバーのみがボットに届きます。テナントにゲスト / 外部協力者が いるなら、allow_from に彼らの open_id 値を埋めて会話をさらに ロックダウンしてください。
  • スタンプ、ファイル、カード、リッチメッセージは落とされます (msg_type == "text" だけがエージェントをトリガー)。Telegram / Slack と同じ S1 契約。
  • RootId フィールドは ImThread.ImThreadID として取得されるので、 スレッド内の返信は同じセッションを継続します —— ユーザーは質問ごとに コンテキストをリセットする必要はありません。