Commit Graph

31 Commits

Author SHA1 Message Date
倪程伟
54e3f7f3fa
fix(feishu): properly close WebSocket connection to prevent resource leak (#221)
stopWebSocket() only nullified the wsClient reference without calling
disconnect() on the SDK client. This left the old WebSocket connection's
pingLoop thread and ExecutorService running, leaking file descriptors
and threads on each reconnect. Over time, accumulated leaks prevented
new connections from being established, causing the Feishu channel to
silently stop receiving messages.

Fix: use reflection to access the SDK's protected `conn` field and call
close(1000) on the OkHttp WebSocket, triggering the SDK's onClosed →
disconnect() cleanup chain.

Note: oapi-sdk 2.7.1 adds a public close() method that would make this
reflection unnecessary. Consider upgrading as a follow-up.

Closes #220
2026-05-25 21:28:05 +08:00
倪程伟
6bf64449bd
fix(channel/feishu): register all IM event handlers to prevent HandlerNotFoundException (#196)
The Lark SDK throws HandlerNotFoundException for any event type without
a registered handler. This exception is caught internally by the SDK's
WebSocket client, which then sends a 500 response to the Feishu server.
The server may close the connection as a result, and the exception is
swallowed — never reaching the application layer.

Added empty handlers for all remaining IM event types:
- P2MessageReadV1 (read receipts)
- P2MessageRecalledV1 (message recall)
- P2ChatMemberBotDeletedV1 (bot removed from chat)
- P2ChatMemberUserAddedV1 / UserDeletedV1 / UserWithdrawnV1
- P2ChatUpdatedV1 (chat info update)
- P2ChatDisbandedV1 (chat disbanded)
- P2ChatAccessEventBotP2pChatEnteredV1 (bot entered p2p chat)

Also added explicit logback config for com.lark.oapi at WARN level
to ensure SDK internal errors are not silently filtered.

Refs: larksuite/oapi-sdk-java#185
2026-05-22 16:10:05 +08:00
matevip
6a4318c268 fix(channel): IM conversations respect per-conversation model selection (#183) 2026-05-20 17:49:03 +08:00
matevip
43136fc663 fix(channel): sweep orphan tool rows + guard rules; scope rule name to channel 2026-05-20 16:35:57 +08:00
matevip
12ff190392 feat(feishu): transcribe inbound voice messages via SttService 2026-05-20 16:35:46 +08:00
matevip
6b4456043e fix(feishu): outbound generated-file URLs become native attachments 2026-05-20 16:01:03 +08:00
matevip
6b397a10ed feat(feishu): inbound file/audio/video download — SDK path + cache push 2026-05-20 15:53:40 +08:00
matevip
71e08b015e sync: Feishu approval card 5-chain hotfix — verified end-to-end in production 2026-05-20 15:24:47 +08:00
matevip
a9fa8e7fb1 sync: interactive approval card on Feishu via Schema-2.0 button + card.action callback 2026-05-20 12:05:47 +08:00
matevip
3554da8dbc sync: inject sender context into agent prompt + Feishu DONE ack hook 2026-05-20 11:51:58 +08:00
matevip
35f010d7a1 sync: Feishu CardKit streaming-card adapter via cardkit/v1 SDK 2026-05-20 11:37:12 +08:00
matevip
0e1b8ca564 sync: componentized media upload SPI + Feishu SDK-backed file sender 2026-05-20 11:23:33 +08:00
matevip
eb827d8cc5 fix(feishu): three small follow-ups from the post-merge audit 2026-05-20 10:58:15 +08:00
matevip
8d91197a46 refactor(feishu): drop dead helper, allow configurable card header, tidy regex 2026-05-20 10:13:53 +08:00
matevip
56ab7b3095 fix(feishu): fall back to text when Interactive Card payload exceeds Feishu limit 2026-05-20 10:13:47 +08:00
matevip
3c3c2a938f perf(feishu): prefetch bot open_id + DCL + 60s negative cache 2026-05-20 10:13:40 +08:00
matevip
73ab31a13c fix(feishu): fail open when bot open_id is unavailable 2026-05-20 10:13:34 +08:00
倪程伟
af3e68d271 fix(feishu): use SDK mentions field for require_mention group filtering (#163)
Closes #162

require_mention=true previously degraded to a no-op when botPrefix was unset:
shouldProcess() returned true for all messages and checkAccess() fell through
unconditionally, so any group message would be answered — including ones where
the @mention targeted another user.

FeishuChannelAdapter now consults the Feishu SDK's mentions field directly:

- WebSocket: read EventMessage.getMentions(); webhook: read mentions[] from the
  JSON payload. In both paths each mention's id.open_id is compared against the
  bot's own open_id.
- Bot open_id is fetched lazily via /open-apis/bot/v3/info and cached on the
  adapter instance. If the call fails the message is allowed through, matching
  the previous behaviour.
- The require_mention gate is applied at the top of handleFeishuMessage so 1:1
  chats are unaffected.

Tests: 15 unit cases covering null/empty inputs, bot mentioned, only-other
mentioned, bot among multiple mentions, and malformed payloads.
2026-05-20 08:49:11 +08:00
倪程伟
ee0c229f52
feat(feishu): support Interactive Card JSON for structured message rendering (#161)
Auto-route Agent replies to Feishu Interactive Card (schema 2.0) when the
content carries structure — JSON object / array, Markdown with code blocks
or headings, or long-form prose — and keep the original text path for
short plain replies.

- FeishuCardFormatter: package-private detect() + render() helper
  - JSON object → two-column summary card
  - JSON array (≤4 fields) → table component; (>4 fields) → div per item
  - Markdown → lark_md card with 'AI 助手' header
  - Long text (>300 chars with paragraph breaks) → plain_text card
  - JSON embedded in Markdown code blocks is recognised across all fences
- FeishuChannelAdapter
  - sendMessage() honours channel config 'card_format' (auto | always | never)
  - sendCard() POSTs interactive messages; ou_-prefixed targets use open_id
  - updateCard() PATCHes an existing message (streaming-update hook)
- Tests: 32 unit cases covering every detect path and render branch

Closes #141
2026-05-20 08:42:02 +08:00
倪程伟
b763022810
fix(feishu): add handler for im.chat.member.bot.added_v1 event
Register a no-op handler for the bot-added-to-chat event on the Feishu WebSocket EventDispatcher. Without it, adding the bot to a group chat raises HandlerNotFoundException and drops the long connection. Mirrors the existing reaction-event handlers. Fixes #153.
2026-05-18 17:46:28 +08:00
matevip
033854a3ff fix(feishu): restore reaction-event no-op handlers dropped by an over-eager dedup revert 2026-05-16 17:39:19 +08:00
matevip
819c5d5c50 docs(memory): drop external-project attribution from javadoc 2026-05-16 17:30:49 +08:00
matevip
c8d908078a style(feishu): write reaction-handler comment in English
Project convention: all code comments must be in English.
2026-05-16 15:34:02 +08:00
倪程伟
a386b31e9b
fix(feishu): register no-op handlers for reaction events (#140)
The Feishu SDK EventDispatcher had no handler registered for im.message.reaction.created_v1 / deleted_v1, so adding or removing an emoji reaction raised HandlerNotFoundException and logged an ERROR stack trace. Register no-op handlers to silently ignore these events.
2026-05-16 15:28:59 +08:00
matevip
3f1f83bbfe fix(channel): gate WS/long-polling channels on a distributed leader lease (#85) 2026-05-11 10:50:00 +08:00
matevip
b0f5dadbe6 fix(feishu): default-on image download for vision pipelines 2026-05-02 15:40:17 +08:00
matevip
fcab47ffb9 fix(feishu): split long messages instead of silent truncate 2026-05-02 15:40:11 +08:00
matevip
b982d4a2d0 feat(feishu): default connection to WebSocket and hide webhook UI when unused
Backend (FeishuChannelAdapter):
- Default connection_mode flips webhook -> websocket on doStart and doReconnect.
- Stale event filter: drop events whose message.create_time is older than
  stale_event_threshold_seconds (default 30s) so SDK reconnect replays do not
  re-trigger the agent.
- Silent disconnect watchdog runs every 60s; if no events arrive for
  silent_disconnect_threshold_seconds (default 1800s) after the first event,
  call onDisconnected to force a reconnect cycle. Setting the threshold to 0
  disables the watchdog. The watchdog is scheduled before wsClient.start() on
  the bring-up path because that call blocks indefinitely.
- Quoted message context: when a reply has parent_id set, fetch the parent
  via GET /open-apis/im/v1/messages/{id}, summarize per msg_type (text / post
  first paragraph / [Image]/[File]/[Audio]/[Video] placeholders, capped at
  200 chars), and prepend [Quoted: ...] to both content text and the first
  content part. LRU-cached (200) per message_id.
- AbstractChannelAdapter gains getConfigLong helper for numeric config keys.

Frontend:
- types/index.ts feishu fields: default connection_mode is websocket; the
  recommended option moves to the top; verification_token and encrypt_key
  get showIf so they only render in webhook mode; new enable_quoted_context
  switch (default on) exposes the quoted-message feature.
- ChannelEditModal builds a feishu-specific WEBHOOK_GUIDES path that picks
  webhookStep vs websocketStep based on connection_mode, so users only see
  steps for the mode they're using.
- i18n: split feishu.step3/step4 into webhookStep/websocketStep, rename
  step5 to permissionStep. Channel type labels in zh-CN drop bilingual
  prefix (e.g. 'Feishu / Lark (飞书)' -> '飞书').

Migrations:
- V52 was a no-op the first time it ran (matched compact JSON only) and
  Flyway refused to re-run after the SQL was fixed. V52 is documented as a
  no-op; V53 carries the actual UPDATE with REPLACE covering both compact
  and pretty-printed JSON, and an idempotent WHERE for rows already on
  websocket. h2 and mysql variants stay in lockstep.
2026-04-28 11:09:17 +08:00
matevip
7d8d16e458 feat: 5 defensive hardenings
- ConversationWindowManager: cap reserve token at 50% of effective max
  to prevent negative historyBudget on small-context models (8K/16K)
- common.security.SecretEquals: new constant-time comparison utility
  (MessageDigest.isEqual wrapper) for secrets/tokens/signatures
- WeixinChannelAdapter: migrate context_token comparison to SecretEquals
- FeishuChannelAdapter: fail-fast on empty encrypt_key when connection_mode=webhook
- TelegramChannelAdapter: sanitize attachment captions — strip control bytes
  (\p{Cc} except \t\r\n) + format chars (\p{Cf}) + 4096 char cap
- AgentGraphBuilder: fallback Anthropic max_tokens to 4096 on null/0/negative

Tests: SecretEqualsTest (5) + TelegramCaptionSanitizeTest (5) — all green.
2026-04-15 22:14:55 +08:00
matevip
d5e3502829 fix(wiki): backend hardening + batch delete UI 2026-04-13 15:37:12 +08:00
matevip
579d60125b Initial commit: MateClaw — Java + Vue 3 AI Assistant System
Full-stack AI assistant built on Spring AI Alibaba.
Features: ReAct Agent, Plan-and-Execute, MCP Protocol, Multi-Model, Multi-Channel.

Apache-2.0 License
2026-04-04 19:03:49 +08:00