Simplify feishu group_policy: default to mention, clean up mention detection

This commit is contained in:
Re-bin
2026-03-12 04:45:57 +00:00
parent 94e9b06086
commit bd1ce8f144
3 changed files with 22 additions and 87 deletions

View File

@@ -503,7 +503,7 @@ Uses **WebSocket** long connection — no public IP required.
"encryptKey": "", "encryptKey": "",
"verificationToken": "", "verificationToken": "",
"allowFrom": ["ou_YOUR_OPEN_ID"], "allowFrom": ["ou_YOUR_OPEN_ID"],
"groupPolicy": "open" "groupPolicy": "mention"
} }
} }
} }
@@ -511,18 +511,7 @@ Uses **WebSocket** long connection — no public IP required.
> `encryptKey` and `verificationToken` are optional for Long Connection mode. > `encryptKey` and `verificationToken` are optional for Long Connection mode.
> `allowFrom`: Add your open_id (find it in nanobot logs when you message the bot). Use `["*"]` to allow all users. > `allowFrom`: Add your open_id (find it in nanobot logs when you message the bot). Use `["*"]` to allow all users.
> `groupPolicy`: `"mention"` (default — respond only when @mentioned), `"open"` (respond to all group messages). Private chats always respond.
**Group Chat Policy** (optional):
| Option | Values | Default | Description |
|--------|--------|---------|-------------|
| `groupPolicy` | `"open"` | `"open"` | Respond to all group messages (backward compatible) |
| | `"mention"` | | Only respond when @mentioned |
> [!NOTE]
> - `"open"`: Respond to all messages in all groups
> - `"mention"`: Only respond when @mentioned in any group
> - Private chats are unaffected (always respond)
**3. Run** **3. Run**

View File

@@ -352,73 +352,26 @@ class FeishuChannel(BaseChannel):
self._running = False self._running = False
logger.info("Feishu bot stopped") logger.info("Feishu bot stopped")
def _get_bot_open_id_sync(self) -> str | None: def _is_bot_mentioned(self, message: Any) -> bool:
"""Get bot's own open_id for mention detection. """Check if the bot is @mentioned in the message."""
飞书 SDK 没有直接的 bot info API从配置或缓存获取。
"""
# 尝试从配置获取 open_id用户可以在配置中指定
if hasattr(self.config, 'open_id') and self.config.open_id:
return self.config.open_id
return None
def _is_bot_mentioned(self, message: Any, bot_open_id: str | None) -> bool:
"""Check if bot is mentioned in the message.
飞书 mentions 数组包含被@的对象。匹配策略:
1. 如果配置了 bot_open_id则匹配 open_id
2. 否则,检查 mentions 中是否有空的 user_idbot 的特征)
Handles:
- Direct mentions in message.mentions
- @all mentions
"""
# Check @all
raw_content = message.content or "" raw_content = message.content or ""
if "@_all" in raw_content: if "@_all" in raw_content:
logger.debug("Feishu: @_all mention detected")
return True return True
# Check mentions array for mention in getattr(message, "mentions", None) or []:
mentions = message.mentions if hasattr(message, 'mentions') and message.mentions else [] mid = getattr(mention, "id", None)
if mentions: if not mid:
if bot_open_id: continue
# 策略 1: 匹配配置的 open_id # Bot mentions have an empty user_id with a valid open_id
for mention in mentions: if getattr(mid, "user_id", None) == "" and (getattr(mid, "open_id", None) or "").startswith("ou_"):
if mention.id: return True
open_id = getattr(mention.id, 'open_id', None)
if open_id == bot_open_id:
logger.debug("Feishu: bot mention matched")
return True
else:
# 策略 2: 检查 bot 特征 - user_id 为空且 open_id 存在
for mention in mentions:
if mention.id:
user_id = getattr(mention.id, 'user_id', None)
open_id = getattr(mention.id, 'open_id', None)
# Bot 的特征user_id 为空字符串open_id 存在
if user_id == '' and open_id and open_id.startswith('ou_'):
logger.debug("Feishu: bot mention matched")
return True
return False return False
def _should_respond_in_group( def _is_group_message_for_bot(self, message: Any) -> bool:
self, """Allow group messages when policy is open or bot is @mentioned."""
chat_id: str, if self.config.group_policy == "open":
mentioned: bool return True
) -> tuple[bool, str]: return self._is_bot_mentioned(message)
"""Determine if bot should respond in a group chat.
Returns:
(should_respond, reason)
"""
# Check mention requirement
if self.config.group_policy == "mention" and not mentioned:
return False, "not mentioned in group"
return True, ""
def _add_reaction_sync(self, message_id: str, emoji_type: str) -> None: def _add_reaction_sync(self, message_id: str, emoji_type: str) -> None:
"""Sync helper for adding reaction (runs in thread pool).""" """Sync helper for adding reaction (runs in thread pool)."""
@@ -961,15 +914,9 @@ class FeishuChannel(BaseChannel):
chat_type = message.chat_type chat_type = message.chat_type
msg_type = message.message_type msg_type = message.message_type
# Check group policy and mention requirement if chat_type == "group" and not self._is_group_message_for_bot(message):
if chat_type == "group": logger.debug("Feishu: skipping group message (not mentioned)")
bot_open_id = self._get_bot_open_id_sync() return
mentioned = self._is_bot_mentioned(message, bot_open_id)
should_respond, reason = self._should_respond_in_group(chat_id, mentioned)
if not should_respond:
logger.debug("Feishu: ignoring group message - {}", reason)
return
# Add reaction # Add reaction
await self._add_reaction(message_id, self.config.react_emoji) await self._add_reaction(message_id, self.config.react_emoji)

View File

@@ -48,8 +48,7 @@ class FeishuConfig(Base):
react_emoji: str = ( react_emoji: str = (
"THUMBSUP" # Emoji type for message reactions (e.g. THUMBSUP, OK, DONE, SMILE) "THUMBSUP" # Emoji type for message reactions (e.g. THUMBSUP, OK, DONE, SMILE)
) )
# Group chat settings group_policy: Literal["open", "mention"] = "mention" # "mention" responds when @mentioned, "open" responds to all
group_policy: Literal["open", "mention"] = "open" # Group response policy (default: open for backward compatibility)
class DingTalkConfig(Base): class DingTalkConfig(Base):