fix: address bot identity review regressions

This commit is contained in:
晴猫
2026-03-15 07:51:31 +09:00
parent c8dc9ddb60
commit 4f8ab0abb1
8 changed files with 77 additions and 17 deletions

View File

@@ -157,6 +157,14 @@ def test_unknown_platform_no_longer_falls_back_to_qq(monkeypatch):
assert "unknown_platform" in logger.warning_messages[-1] assert "unknown_platform" in logger.warning_messages[-1]
def test_unknown_platform_warns_only_once(monkeypatch):
utils_module, logger = load_utils_module(monkeypatch, qq_account=123456, platforms=[])
assert utils_module.is_bot_self("unknown_platform", "first") is False
assert utils_module.is_bot_self(" unknown_platform ", "second") is False
assert len(logger.warning_messages) == 1
def test_unconfigured_qq_account_disables_qq_and_webui_identity(monkeypatch): def test_unconfigured_qq_account_disables_qq_and_webui_identity(monkeypatch):
utils_module, _logger = load_utils_module(monkeypatch, qq_account=0, platforms=["telegram:tg_bot"]) utils_module, _logger = load_utils_module(monkeypatch, qq_account=0, platforms=["telegram:tg_bot"])
@@ -185,3 +193,24 @@ def test_is_mentioned_bot_in_message_uses_platform_account(monkeypatch):
assert is_mentioned is True assert is_mentioned is True
assert is_at is True assert is_at is True
assert reply_probability == 1.0 assert reply_probability == 1.0
def test_is_mentioned_bot_in_message_normalizes_qq_platform(monkeypatch):
utils_module, _logger = load_utils_module(monkeypatch, qq_account=123456, platforms=[])
message = SimpleNamespace(
processed_plain_text="@<MaiBot:123456> 你好",
platform=" QQ ",
is_mentioned=False,
message_segment=None,
message_info=SimpleNamespace(
additional_config={},
user_info=SimpleNamespace(user_id="user_1"),
),
)
is_mentioned, is_at, reply_probability = utils_module.is_mentioned_bot_in_message(message)
assert is_mentioned is True
assert is_at is True
assert reply_probability == 1.0

View File

@@ -42,8 +42,12 @@ class DirectMessageSender:
segments = Seg(type="seglist", data=[Seg(type="text", data=content)]) segments = Seg(type="seglist", data=[Seg(type="text", data=content)])
# 获取麦麦的信息 # 获取麦麦的信息
bot_user_id = get_bot_account(chat_stream.platform)
if not bot_user_id:
logger.warning(f"[私聊][{self.private_name}]平台 {chat_stream.platform} 未配置机器人账号,发送消息时回退到 QQ 账号")
bot_user_id = str(getattr(global_config.bot, "qq_account", "")).strip()
bot_user_info = UserInfo( bot_user_info = UserInfo(
user_id=get_bot_account(chat_stream.platform), user_id=bot_user_id,
user_nickname=global_config.bot.nickname, user_nickname=global_config.bot.nickname,
) )

View File

@@ -1115,6 +1115,10 @@ class DefaultReplyer:
anchor_message: Optional[MaiMessage] = None, anchor_message: Optional[MaiMessage] = None,
) -> SessionMessage: ) -> SessionMessage:
"""构建单个发送消息""" """构建单个发送消息"""
bot_user_id = get_bot_account(self.chat_stream.platform)
if not bot_user_id:
logger.warning(f"平台 {self.chat_stream.platform} 未配置机器人账号,发送消息时回退到 QQ 账号")
bot_user_id = str(getattr(global_config.bot, "qq_account", "")).strip()
maim_message = MessageBase( maim_message = MessageBase(
message_info=BaseMessageInfo( message_info=BaseMessageInfo(
@@ -1122,7 +1126,7 @@ class DefaultReplyer:
message_id=message_id, message_id=message_id,
time=thinking_start_time, time=thinking_start_time,
user_info=MaimUserInfo( user_info=MaimUserInfo(
user_id=get_bot_account(self.chat_stream.platform), user_id=bot_user_id,
user_nickname=global_config.bot.nickname, user_nickname=global_config.bot.nickname,
), ),
additional_config={}, additional_config={},

View File

@@ -955,6 +955,10 @@ class PrivateReplyer:
anchor_message: Optional[MaiMessage] = None, anchor_message: Optional[MaiMessage] = None,
) -> SessionMessage: ) -> SessionMessage:
"""构建单个发送消息""" """构建单个发送消息"""
bot_user_id = get_bot_account(self.chat_stream.platform)
if not bot_user_id:
logger.warning(f"平台 {self.chat_stream.platform} 未配置机器人账号,发送消息时回退到 QQ 账号")
bot_user_id = str(getattr(global_config.bot, "qq_account", "")).strip()
maim_message = MessageBase( maim_message = MessageBase(
message_info=BaseMessageInfo( message_info=BaseMessageInfo(
@@ -962,7 +966,7 @@ class PrivateReplyer:
message_id=message_id, message_id=message_id,
time=thinking_start_time, time=thinking_start_time,
user_info=MaimUserInfo( user_info=MaimUserInfo(
user_id=get_bot_account(self.chat_stream.platform), user_id=bot_user_id,
user_nickname=global_config.bot.nickname, user_nickname=global_config.bot.nickname,
), ),
group_info=None, group_info=None,

View File

@@ -1,11 +1,12 @@
from datetime import datetime
from typing import TYPE_CHECKING, List, Optional, Tuple
import ast import ast
import json import json
import os import os
import random import random
import re import re
import time import time
from datetime import datetime
from typing import Optional, Tuple, List, TYPE_CHECKING
import jieba import jieba
@@ -15,12 +16,14 @@ from src.common.logger import get_logger
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.person_info.person_info import Person from src.person_info.person_info import Person
from .typo_generator import ChineseTypoGenerator from .typo_generator import ChineseTypoGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from src.common.data_models.info_data_model import TargetPersonInfo from src.common.data_models.info_data_model import TargetPersonInfo
logger = get_logger("chat_utils") logger = get_logger("chat_utils")
_warned_unconfigured_platforms: set[str] = set()
def is_english_letter(char: str) -> bool: def is_english_letter(char: str) -> bool:
@@ -122,6 +125,8 @@ def is_bot_self(platform: str, user_id: str) -> bool:
if bot_account: if bot_account:
return user_id_str == bot_account return user_id_str == bot_account
if normalized_platform not in _warned_unconfigured_platforms:
_warned_unconfigured_platforms.add(normalized_platform)
logger.warning(f"平台 {normalized_platform} 未配置机器人账号,无法判断用户 {user_id_str} 是否为机器人自己") logger.warning(f"平台 {normalized_platform} 未配置机器人账号,无法判断用户 {user_id_str} 是否为机器人自己")
return False return False
@@ -129,7 +134,7 @@ def is_bot_self(platform: str, user_id: str) -> bool:
def is_mentioned_bot_in_message(message: SessionMessage) -> tuple[bool, bool, float]: def is_mentioned_bot_in_message(message: SessionMessage) -> tuple[bool, bool, float]:
"""检查消息是否提到了机器人(统一多平台实现)""" """检查消息是否提到了机器人(统一多平台实现)"""
text = message.processed_plain_text or "" text = message.processed_plain_text or ""
platform = message.platform or "" platform = str(message.platform or "").strip().lower()
# 获取当前平台对应的账号 # 获取当前平台对应的账号
current_account = get_bot_account(platform) current_account = get_bot_account(platform)

View File

@@ -7,9 +7,9 @@ import traceback
from sqlalchemy import and_, func, not_, or_ from sqlalchemy import and_, func, not_, or_
from sqlmodel import col, select from sqlmodel import col, select
from src.chat.message_receive.message import SessionMessage
from src.common.database.database import get_db_session from src.common.database.database import get_db_session
from src.common.database.database_model import Messages from src.common.database.database_model import Messages
from src.chat.message_receive.message import SessionMessage
from src.common.logger import get_logger from src.common.logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -162,17 +162,26 @@ def find_messages(
after_time=after_time, after_time=after_time,
) )
if filter_bot: if filter_bot:
from src.chat.utils.utils import get_all_bot_accounts from src.chat.utils.utils import _get_configured_qq_account, get_all_bot_accounts
bot_accounts = get_all_bot_accounts() bot_accounts = get_all_bot_accounts()
exclusion_conditions: list[Any] = []
if bot_accounts: if bot_accounts:
bot_identity_predicate = or_( exclusion_conditions.append(
or_(
*[ *[
and_(Messages.platform == platform_name, Messages.user_id == account) and_(Messages.platform == platform_name, Messages.user_id == account)
for platform_name, account in bot_accounts.items() for platform_name, account in bot_accounts.items()
] ]
) )
conditions.append(not_(bot_identity_predicate)) )
# 兼容旧数据:历史机器人消息在所有平台上都使用 QQ 账号进行存储。
if qq_fallback := _get_configured_qq_account():
exclusion_conditions.append(Messages.user_id == qq_fallback)
if exclusion_conditions:
conditions.append(not_(or_(*exclusion_conditions)))
if filter_command: if filter_command:
conditions.append(Messages.is_command == False) # noqa: E712 conditions.append(Messages.is_command == False) # noqa: E712

View File

@@ -1,4 +1,5 @@
# TODO: 这个兼容包装层后续可以删除,统一直接使用 src.chat.utils.utils.is_bot_self # TODO: 这个包装层后续可以删除,统一直接使用 src.chat.utils.utils.is_bot_self
# 注意:参数顺序已从旧版 (user_id, platform) 变更为 (platform, user_id),与统一接口一致
def is_bot_self(platform: str, user_id: str) -> bool: def is_bot_self(platform: str, user_id: str) -> bool:
""" """
判断用户 ID 是否是机器人自己。 判断用户 ID 是否是机器人自己。

View File

@@ -83,6 +83,10 @@ async def _send_to_target(
additional_config: dict[str, object] = {} additional_config: dict[str, object] = {}
if selected_expressions is not None: if selected_expressions is not None:
additional_config["selected_expressions"] = selected_expressions additional_config["selected_expressions"] = selected_expressions
bot_user_id = get_bot_account(target_stream.platform)
if not bot_user_id:
logger.warning(f"[SendService] 平台 {target_stream.platform} 未配置机器人账号,发送消息时回退到 QQ 账号")
bot_user_id = str(getattr(global_config.bot, "qq_account", "")).strip()
maim_message = MessageBase( maim_message = MessageBase(
message_info=BaseMessageInfo( message_info=BaseMessageInfo(
@@ -90,7 +94,7 @@ async def _send_to_target(
message_id=message_id, message_id=message_id,
time=current_time, time=current_time,
user_info=MaimUserInfo( user_info=MaimUserInfo(
user_id=get_bot_account(target_stream.platform), user_id=bot_user_id,
user_nickname=global_config.bot.nickname, user_nickname=global_config.bot.nickname,
platform=target_stream.platform, platform=target_stream.platform,
), ),