fix(pipeline): enforce group response rules after routing

Stop treating an explicit Pipeline route as permission to bypass group
response rules. Routed group messages must still satisfy the configured
mention or prefix matchers.

Preserve the bypass for verified interaction submissions that resume an
existing run. Cover ordinary messages, mentions, prefixes, and interaction
callbacks with regression tests.
This commit is contained in:
BiFangKNT
2026-10-17 19:48:15 +08:00
parent 4d90358da0
commit 2ca9c2c7fb
2 changed files with 43 additions and 2 deletions
@@ -37,8 +37,10 @@ class GroupRespondRuleCheckStage(stage.PipelineStage):
if query.launcher_type.value != 'group': # 只处理群消息
return entities.StageProcessResult(result_type=entities.ResultType.CONTINUE, new_query=query)
# 通过路由规则明确指定的流水线,跳过群响应规则检查
if query.variables and query.variables.get('_routed_by_rule', False):
# Verified interaction callbacks resume the original run without a new
# mention or prefix. Routing ordinary messages only selects a pipeline;
# it must not bypass that pipeline's group response rules.
if query.variables and query.variables.get('_interaction_submission'):
return entities.StageProcessResult(result_type=entities.ResultType.CONTINUE, new_query=query)
rules = query.pipeline_config['trigger']['group-respond-rules']
@@ -138,3 +138,42 @@ async def test_atbot_rule_no_match(mock_app, sample_query):
result = await atbot_rule.match(str(message_chain), message_chain, {}, sample_query)
assert result.matching is False
@pytest.mark.asyncio
@pytest.mark.parametrize('routed', [False, True])
@pytest.mark.parametrize('kind', ['plain', 'mention', 'prefix'])
async def test_routed_group_messages_still_obey_response_rules(mock_app, sample_query, routed, kind):
resprule, entities, _, _ = get_modules()
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.adapter.bot_account_id = '999'
sample_query.variables = {'_routed_by_rule': routed}
sample_query.pipeline_config = {
'trigger': {'group-respond-rules': {'at': True, 'prefix': ['!bot '], 'regexp': [], 'random': 0}}
}
parts = [platform_message.Plain(text='!bot hello' if kind == 'prefix' else 'hello')]
if kind == 'mention':
parts.insert(0, platform_message.At(target='999'))
sample_query.message_chain = platform_message.MessageChain(parts)
stage = resprule.GroupRespondRuleCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
result = await stage.process(sample_query, 'GroupRespondRuleCheckStage')
expected = entities.ResultType.INTERRUPT if kind == 'plain' else entities.ResultType.CONTINUE
assert result.result_type == expected
if kind != 'plain':
assert str(sample_query.message_chain) == 'hello'
@pytest.mark.asyncio
async def test_interaction_submission_does_not_require_another_mention(mock_app, sample_query):
resprule, entities, _, _ = get_modules()
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.variables = {'_routed_by_rule': True, '_interaction_submission': {'interaction_id': 'form-1'}}
stage = resprule.GroupRespondRuleCheckStage(mock_app)
stage.rule_matchers = []
result = await stage.process(sample_query, 'GroupRespondRuleCheckStage')
assert result.result_type == entities.ResultType.CONTINUE