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.
| Modo | Qué hace |
|---|---|
| Notificación | Push de alerta a un grupo de Feishu/Lark vía webhook de bot custom. |
| IM bridge | Chat 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:
{
"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:
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
- En el grupo de Feishu → 设置 → 群机器人 → 添加机器人 → 自定义机器人. Dale un nombre y avatar.
- Marca 签名校验; copia el secret.
- 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). - 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_keypara la variante de stream — es solo-webhook.
Mapeo de credenciales
Columna im_apps | Significado en Feishu |
|---|---|
provider | "feishu" |
mode | "stream" (recomendado) o "webhook" |
app_id | app_id de Feishu (cli_…). |
app_secret | app_secret de Feishu. |
verify_token | Opcional. Usado por la verificación de firma del modo webhook. |
encrypt_key | Requerido en modo webhook, ignorado en modo stream. |
allow_from | Opcional. 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
- 开发者后台 → 创建企业自建应用. Obtén
app_id+app_secret. - 应用功能 → 机器人 → habilita. Añade el bot a tu chat de prueba.
- 权限管理 → 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_bot—SendText/EditText.
- 事件订阅 → 长连接模式 (long-connection) → habilita.
- En Ongrid: Settings → IM bridge → New → Provider =
feishu→ Mode =stream→ pegaapp_id+app_secret. Save y Enable. - 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:
sig = sha256(timestamp + nonce + encrypt_key + body), hex-encodedSí — 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_fromhoy — la propia plataforma Feishu está gated por tenant, así que solo miembros empresariales alcanzan al bot. Si tu tenant tiene invitados / colaboradores externos, rellenaallow_fromcon sus valoresopen_idpara 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
RootIdse captura comoImThread.ImThreadIDpara que las respuestas dentro de un hilo continúen la misma sesión — sin necesidad de que el usuario resetee contexto por pregunta.