mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-08-30 17:33:24 +08:00
fix: check quoted text for content safety (#9232)
* fix: check quoted text for content safety * fix: initialize content safety result * fix: combine content safety check text --------- Co-authored-by: JIANZHOU <jzhou2409324124@gmail.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from astrbot.core import logger
|
||||
from astrbot.core.message.components import Reply
|
||||
from astrbot.core.message.message_event_result import MessageEventResult
|
||||
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
||||
from astrbot.core.utils.quoted_message.chain_parser import ReplyChainParser
|
||||
|
||||
from ..context import PipelineContext
|
||||
from ..stage import Stage, register_stage
|
||||
@@ -26,8 +28,20 @@ class ContentSafetyCheckStage(Stage):
|
||||
check_text: str | None = None,
|
||||
) -> AsyncGenerator[None, None]:
|
||||
"""检查内容安全"""
|
||||
text = check_text if check_text else event.get_message_str()
|
||||
ok, info = self.strategy_selector.check(text)
|
||||
if check_text is None:
|
||||
texts = [event.get_message_str()]
|
||||
reply_parser = ReplyChainParser()
|
||||
for component in event.get_messages():
|
||||
if isinstance(component, Reply) and (
|
||||
quoted_text := reply_parser.extract_text_from_reply_component(
|
||||
component
|
||||
)
|
||||
):
|
||||
texts.append(quoted_text)
|
||||
else:
|
||||
texts = [check_text]
|
||||
|
||||
ok, info = self.strategy_selector.check("\n".join(texts))
|
||||
if not ok:
|
||||
if event.is_at_or_wake_command:
|
||||
event.set_result(
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from astrbot.core.message.components import Plain, Reply
|
||||
from astrbot.core.pipeline.content_safety_check.stage import ContentSafetyCheckStage
|
||||
from astrbot.core.pipeline.content_safety_check.strategies.strategy import (
|
||||
StrategySelector,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_content_safety_checks_combined_message_text_once():
|
||||
event = SimpleNamespace(
|
||||
is_at_or_wake_command=False,
|
||||
get_message_str=lambda: "current message",
|
||||
get_messages=lambda: [Reply(id="1", message_str="quoted message")],
|
||||
stop_event=Mock(),
|
||||
)
|
||||
stage = ContentSafetyCheckStage()
|
||||
stage.strategy_selector = SimpleNamespace(check=Mock(return_value=(True, "")))
|
||||
|
||||
async for _ in stage.process(event):
|
||||
pass
|
||||
|
||||
stage.strategy_selector.check.assert_called_once_with(
|
||||
"current message\nquoted message"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
("reply", "keyword", "check_text", "expected_stopped"),
|
||||
[
|
||||
(
|
||||
Reply(id="1", message_str="引用中包含淀粉砖"),
|
||||
"淀粉砖",
|
||||
None,
|
||||
True,
|
||||
),
|
||||
(
|
||||
Reply(id="1", message_str="", chain=[Plain("引用中包含淀粉砖")]),
|
||||
"淀粉砖",
|
||||
None,
|
||||
True,
|
||||
),
|
||||
(
|
||||
Reply(id="1", message_str="引用中包含淀粉砖"),
|
||||
"^你说呢\n引用中包含淀粉砖$",
|
||||
None,
|
||||
True,
|
||||
),
|
||||
(
|
||||
Reply(id="1", message_str="引用中包含淀粉砖"),
|
||||
"淀粉砖",
|
||||
"",
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_content_safety_checks_quoted_text_only_for_inbound_messages(
|
||||
reply: Reply,
|
||||
keyword: str,
|
||||
check_text: str | None,
|
||||
expected_stopped: bool,
|
||||
):
|
||||
stopped = False
|
||||
|
||||
def stop_event() -> None:
|
||||
nonlocal stopped
|
||||
stopped = True
|
||||
|
||||
event = SimpleNamespace(
|
||||
is_at_or_wake_command=False,
|
||||
get_message_str=lambda: "你说呢",
|
||||
get_messages=lambda: [reply],
|
||||
stop_event=stop_event,
|
||||
)
|
||||
stage = ContentSafetyCheckStage()
|
||||
stage.strategy_selector = StrategySelector(
|
||||
{
|
||||
"internal_keywords": {
|
||||
"enable": True,
|
||||
"extra_keywords": [keyword],
|
||||
},
|
||||
"baidu_aip": {"enable": False},
|
||||
}
|
||||
)
|
||||
|
||||
async for _ in stage.process(event, check_text=check_text):
|
||||
pass
|
||||
|
||||
assert stopped is expected_stopped
|
||||
Reference in New Issue
Block a user