Skip to content

Larksuite (Feishu)

Feishu (国内版) e Lark Suite (海外版) compartilham a mesma superfície OpenAPI. O provider Ongrid os trata como uma única integração; escolha a base URL que casa com seu tenant.

ModoO que faz
NotificaçãoPush de alerta para grupo Feishu/Lark via webhook de bot custom.
Ponte IMChat two-way com o agent usando long-connection WebSocket.

Modo notificação (bot custom)

O sender Feishu posta um payload de bot custom Feishu / Lark à URL do webhook que seu admin de bot fornece. Formato do payload:

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>"
}

Assinatura — campo sign

Quando você liga 签名校验 (verificação de assinatura) no bot custom Feishu, o Feishu te dá um secret compartilhado. O sender computa o campo sign como:

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

Sim — o secret assume o papel tanto de material de chave para o HMAC quanto de parte do string-to-sign. Esse é o algoritmo que o Feishu documenta, e é o que signFeishu implementa.

Se você deixar o campo Secret em branco, o Ongrid posta sem um sign / timestamp — utilizável apenas quando seu bot tem a verificação de assinatura desligada (ou tem allowlist de IP no lugar).

Configuração

  1. No grupo Feishu → 设置 → 群机器人 → 添加机器人 → 自定义机器人. Dê nome e avatar.
  2. Marque 签名校验; copie o secret.
  3. Copie a URL do webhook — algo como https://open.feishu.cn/open-apis/bot/v2/hook/<uuid> (ou …/open.larksuite.com/… para Lark Suite).
  4. No Ongrid: Settings → Channels → New → Provider = feishu → Endpoint = a URL do webhook → Secret = o secret de assinatura.

Bot custom ≠ App

O modo notificação usa um bot custom (escopo de grupo, só webhook). O modo ponte IM usa um app Feishu (escopo de tenant, OAuth + events). São conceitos diferentes; as credenciais não se sobrepõem.

Modo ponte IM (stream long-connection)

A ponte two-way usa a long-connection Feishu entregue pelo client oficial github.com/larksuite/oapi-sdk-go/v3/ws. O manager disca para o endpoint de events do Feishu e o Feishu empurra events pelo WebSocket — nenhuma URL pública de webhook é necessária.

Por que stream e não webhook

O modo webhook (mode=webhook) é suportado no schema para back-compat, mas o stream long-connection é o caminho recomendado:

  • Sem necessidade de ingress público no manager.
  • Reaproveita o reconnect-with-backoff do supervisor (o SDK tem seu próprio reconnect; o supervisor adiciona um loop externo para falhas terminais).
  • O SDK lida com verificação de assinatura + descriptografia AES internamente, então você não precisa preencher encrypt_key para a variante stream — é só de webhook.

Mapeamento de credenciais

Coluna im_appsSignificado Feishu
provider"feishu"
mode"stream" (recomendado) ou "webhook"
app_idapp_id Feishu (cli_…).
app_secretapp_secret Feishu.
verify_tokenOpcional. Usado pela verificação de assinatura em modo webhook.
encrypt_keyObrigatório em modo webhook, ignorado em modo stream.
allow_fromOpcional. Feishu é tenant-gated então a allowlist é não-obrigatória.

default_locale

Set para zh para um tenant Feishu cujos operadores escrevem em chinês, para en para um time Lark em inglês. Vazio (padrão) significa que o LLM espelha o usuário.

Configuração

  1. 开发者后台 → 创建企业自建应用. Pegue app_id + app_secret.
  2. 应用功能 → 机器人 → enable. Adicione o bot ao seu chat de teste.
  3. 权限管理 → conceda:
    • im:message — ler mensagens endereçadas ao bot.
    • im:message.group_at_msg — events de menção em grupo.
    • im:message.p2p_msg — events de DM.
    • im:message:send_as_botSendText / EditText.
  4. 事件订阅 → 长连接模式 (long-connection) → enable.
  5. No Ongrid: Settings → IM bridge → New → Provider = feishu → Mode = stream → cole app_id + app_secret. Salve e Enable.
  6. @ o bot no chat. O agent pega.

Cache de tenant_access_token

Chamadas outbound (SendText, EditText) autenticam com um tenant_access_token, refreshado proativamente quando dentro de 200s do vencimento. O token é cacheado por instância de Client, protegido por um sync.Mutex; rotacionar credenciais significa rebuildar o client. Veja tenantAccessToken.

msg_type é obrigatório em edits também

O PUT /open-apis/im/v1/messages/<id> do Feishu exige msg_type no body — omiti-lo retorna code: 99992402 (validação de campo falhou) mesmo que a página de docs para "edit message" não deixe isso óbvio. O provider sempre envia msg_type: text.

Modo webhook (legado, mantido por compatibilidade)

O modo webhook ainda é selecionável na UI; o supervisor verifica assinaturas e decrypta payloads ele mesmo.

Verificação de assinatura

O handler HTTP lê X-Lark-Signature e roda VerifyEventSignature:

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

Sim — SHA-256, não HMAC. O encrypt_key faz o papel de shared-secret dentro do input de hash. O verifier nunca panica em input ruim; mismatch retorna ErrBadSignature.

Descriptografia de payload

Quando encrypt_key está setado, o Feishu envelopa o JSON do event em AES-256-CBC. A descriptografia usa:

  • Chave: SHA-256(encrypt_key).
  • IV: os primeiros 16 bytes do ciphertext decodificado em base64.
  • Padding: PKCS#7.

Veja DecryptEvent.

Prefira modo stream se puder

Modo webhook requer um endpoint HTTPS público e a fiação de encrypt_key acima. O stream long-connection evita ambos. Vá no modo webhook só quando você especificamente precisa (ex.: integrar events Feishu com um router de webhook público existente).

Pegadinhas

  • O client stream Feishu não impõe allow_from hoje — a própria plataforma Feishu é tenant-gated, então só membros da empresa alcançam o bot. Se seu tenant tem guests / colaboradores externos, preencha allow_from com seus valores de open_id para travar a conversa mais.
  • Stickers, files, cards, e mensagens ricas são descartadas (só msg_type == "text" dispara o agent). Mesmo contrato S1 que Telegram / Slack.
  • O campo RootId é capturado como ImThread.ImThreadID para que respostas dentro de uma thread continuem a mesma sessão — sem necessidade de o usuário resetar contexto por pergunta.