Skip to content

Larksuite (Feishu)

Feishu (国内版) y Lark Suite (海外版) comparten la misma superficie OpenAPI. El provider de Ongrid los trata como una integración; elige la base URL que matchea tu tenant.

ModoQué hace
NotificaciónPush de alerta a un grupo de Feishu/Lark vía webhook de bot custom.
IM bridgeChat bidireccional con el agente usando la WebSocket long-connection.

Modo notificación (bot custom)

El sender de Feishu postea un payload de bot custom de Feishu / Lark a la URL de webhook que el administrador de bots provee. Forma del 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>"
}

Firma — campo sign

Cuando enciendes 签名校验 (signature verification) en el bot custom de Feishu, Feishu te da un secret compartido. El sender computa el campo sign como:

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

Sí — el secret toma el rol tanto de material de key para HMAC como parte del string-to-sign. Este es el algoritmo que Feishu documenta, y lo que implementa signFeishu.

Si dejas el campo Secret en blanco, Ongrid postea sin sign / timestamp — usable solo cuando tu bot tiene la verificación de firma apagada (o tiene IP allowlisting en su lugar).

Setup

  1. En el grupo de Feishu → 设置 → 群机器人 → 添加机器人 → 自定义机器人. Dale un nombre y avatar.
  2. Marca 签名校验; copia el secret.
  3. Copia la URL del webhook — se ve como https://open.feishu.cn/open-apis/bot/v2/hook/<uuid> (o …/open.larksuite.com/… para Lark Suite).
  4. En Ongrid: Settings → Channels → New → Provider = feishu → Endpoint = la URL del webhook → Secret = el secret de firma.

Bot custom ≠ App

El modo notificación usa un bot custom (alcance de grupo, solo webhook). El modo IM bridge usa una app de Feishu (alcance de tenant, OAuth + events). Son conceptos distintos; las credenciales no se traslapan.

Modo IM bridge (stream long-connection)

El bridge bidireccional usa la long-connection de Feishu entregada por el cliente oficial github.com/larksuite/oapi-sdk-go/v3/ws. El manager marca al endpoint de eventos de Feishu y Feishu empuja eventos sobre la WebSocket — no se requiere URL webhook pública.

Por qué stream y no webhook

El modo webhook (mode=webhook) está soportado en el schema para back-compat, pero el stream long-connection es la ruta recomendada:

  • Sin ingress público necesario en el manager.
  • Reutiliza el reconnect-con-backoff del supervisor (el SDK tiene su propio reconnect; el supervisor añade un loop externo para fallos terminales).
  • El SDK maneja verificación de firma + desencriptación AES internamente, así que no necesitas rellenar encrypt_key para la variante de stream — es solo-webhook.

Mapeo de credenciales

Columna im_appsSignificado en Feishu
provider"feishu"
mode"stream" (recomendado) o "webhook"
app_idapp_id de Feishu (cli_…).
app_secretapp_secret de Feishu.
verify_tokenOpcional. Usado por la verificación de firma del modo webhook.
encrypt_keyRequerido en modo webhook, ignorado en modo stream.
allow_fromOpcional. Feishu está gated por tenant así que la allowlist no es obligatoria.

default_locale

Setea a zh para un tenant de Feishu cuyos operadores escriben chino, a en para un equipo Lark en inglés. Vacío (default) significa que el LLM espeja al usuario.

Setup

  1. 开发者后台 → 创建企业自建应用. Obtén app_id + app_secret.
  2. 应用功能 → 机器人 → habilita. Añade el bot a tu chat de prueba.
  3. 权限管理 → otorga:
    • im:message — leer mensajes dirigidos al bot.
    • im:message.group_at_msg — eventos de mención en grupo.
    • im:message.p2p_msg — eventos de DM.
    • im:message:send_as_botSendText / EditText.
  4. 事件订阅 → 长连接模式 (long-connection) → habilita.
  5. En Ongrid: Settings → IM bridge → New → Provider = feishu → Mode = stream → pega app_id + app_secret. Save y Enable.
  6. Haz @ al bot en el chat. El agente lo recoge.

Caching de tenant_access_token

Las llamadas salientes (SendText, EditText) autentican con un tenant_access_token, refrescado proactivamente cuando está dentro de 200s de expiración. El token se cachea por instancia de Client, protegido por un sync.Mutex; rotar credenciales significa reconstruir el cliente. Ver tenantAccessToken.

msg_type también es requerido en ediciones

El PUT /open-apis/im/v1/messages/<id> de Feishu requiere msg_type en el body — omitirlo devuelve code: 99992402 (field validation failed) aunque la página de docs para "edit message" no lo hace obvio. El provider siempre envía msg_type: text.

Modo webhook (legacy, mantenido para compatibilidad)

El modo webhook sigue siendo seleccionable en la UI; el supervisor verifica firmas y desencripta payloads por sí mismo.

Verificación de firma

El handler HTTP lee X-Lark-Signature y corre VerifyEventSignature:

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

Sí — SHA-256, no HMAC. El encrypt_key juega el rol de secret compartido dentro del input del hash. El verifier nunca paniquea ante input malo; un mismatch devuelve ErrBadSignature.

Desencriptación de payload

Cuando encrypt_key está seteado, Feishu envuelve el event JSON en AES-256-CBC. La desencriptación usa:

  • Key: SHA-256(encrypt_key).
  • IV: los primeros 16 bytes del ciphertext base64-decoded.
  • Padding: PKCS#7.

Ver DecryptEvent.

Elige modo stream si puedes

El modo webhook requiere un endpoint HTTPS público y la plomería de encrypt_key arriba. El stream long-connection evita ambos. Ve al modo webhook solo cuando lo necesites específicamente (p. ej. integrar events de Feishu con un router de webhook público existente).

Quirks

  • El cliente stream de Feishu no refuerza allow_from hoy — la propia plataforma Feishu está gated por tenant, así que solo miembros empresariales alcanzan al bot. Si tu tenant tiene invitados / colaboradores externos, rellena allow_from con sus valores open_id para bloquear más la conversación.
  • Stickers, archivos, cards y mensajes ricos se descartan (solo msg_type == "text" dispara al agente). Mismo contrato S1 que Telegram / Slack.
  • El campo RootId se captura como ImThread.ImThreadID para que las respuestas dentro de un hilo continúen la misma sesión — sin necesidad de que el usuario resetee contexto por pregunta.