From ca40cbc37d9e60ad4296e4715172c63b513b78a8 Mon Sep 17 00:00:00 2001 From: Luna-channel Date: Thu, 19 Feb 2026 23:59:20 +0800 Subject: [PATCH 1/7] feat: add scope option for reply_with_quote setting --- astrbot/core/config/default.py | 19 +++++++++++++++++++ astrbot/core/pipeline/respond/stage.py | 3 +++ .../core/pipeline/result_decorate/stage.py | 11 ++++++++++- .../en-US/features/config-metadata.json | 8 ++++++++ .../zh-CN/features/config-metadata.json | 7 +++++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index b50bcd8dea..c7183ab3dd 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -37,6 +37,7 @@ "wl_ignore_admin_on_friend": True, "reply_with_mention": False, "reply_with_quote": False, + "reply_with_quote_scope": "all", # all | group_only | private_only "path_mapping": [], "segmented_reply": { "enable": False, @@ -883,6 +884,17 @@ class ChatProviderTemplate(TypedDict): "type": "bool", "hint": "启用后,机器人回复消息时会引用原消息。实际效果以具体的平台适配器为准。", }, + "reply_with_quote_scope": { + "type": "string", + "options": ["all", "group_only", "private_only"], + "labels": [ + "全部开启", + "仅群聊", + "仅私聊", + ], + "hint": "选择引用回复的生效范围。", + "condition": {"reply_with_quote": True}, + }, "path_mapping": { "type": "list", "items": {"type": "string"}, @@ -3106,6 +3118,13 @@ class ChatProviderTemplate(TypedDict): "description": "回复时引用发送人消息", "type": "bool", }, + "platform_settings.reply_with_quote_scope": { + "description": "引用回复范围", + "type": "string", + "options": ["all", "group_only", "private_only"], + "labels": ["全部开启", "仅群聊", "仅私聊"], + "condition": {"platform_settings.reply_with_quote": True}, + }, "platform_settings.forward_threshold": { "description": "转发消息的字数阈值", "type": "int", diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index 72e853ffcc..c7965e3642 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -61,6 +61,9 @@ async def initialize(self, ctx: PipelineContext) -> None: self.reply_with_quote = ctx.astrbot_config["platform_settings"][ "reply_with_quote" ] + self.reply_with_quote_scope = ctx.astrbot_config["platform_settings"].get( + "reply_with_quote_scope", "all" + ) # 分段回复 self.enable_seg: bool = ctx.astrbot_config["platform_settings"][ diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 15d68fb22e..c6595899e3 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -29,6 +29,9 @@ async def initialize(self, ctx: PipelineContext) -> None: self.reply_with_quote = ctx.astrbot_config["platform_settings"][ "reply_with_quote" ] + self.reply_with_quote_scope = ctx.astrbot_config["platform_settings"].get( + "reply_with_quote_scope", "all" + ) self.t2i_word_threshold = ctx.astrbot_config["t2i_word_threshold"] try: self.t2i_word_threshold = int(self.t2i_word_threshold) @@ -399,5 +402,11 @@ async def process( # 引用回复 if self.reply_with_quote: - if not any(isinstance(item, File) for item in result.chain): + is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE + should_quote = ( + self.reply_with_quote_scope == "all" + or (self.reply_with_quote_scope == "private_only" and is_private) + or (self.reply_with_quote_scope == "group_only" and not is_private) + ) + if should_quote and not any(isinstance(item, File) for item in result.chain): result.chain.insert(0, Reply(id=event.message_obj.message_id)) diff --git a/dashboard/src/i18n/locales/en-US/features/config-metadata.json b/dashboard/src/i18n/locales/en-US/features/config-metadata.json index 639be65781..5480c43b97 100644 --- a/dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ b/dashboard/src/i18n/locales/en-US/features/config-metadata.json @@ -583,6 +583,14 @@ "reply_with_quote": { "description": "Quote Sender's Message in Reply" }, + "reply_with_quote_scope": { + "description": "Quote Reply Scope", + "labels": [ + "All", + "Group Only", + "Private Only" + ] + }, "forward_threshold": { "description": "Forward Message Word Count Threshold" }, diff --git a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json index c0838b5eea..2b29214d50 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ b/dashboard/src/i18n/locales/zh-CN/features/config-metadata.json @@ -586,6 +586,13 @@ "reply_with_quote": { "description": "回复时引用发送人消息" }, + "reply_with_quote_scope": { + "description": "引用回复范围", + "labels": [ + "全部开启", + "仅群聊", + "仅私聊"] + }, "forward_threshold": { "description": "转发消息的字数阈值" }, From be1f9d733a71819236b009902e45c13fd89af1ab Mon Sep 17 00:00:00 2001 From: Luna-channel Date: Fri, 20 Feb 2026 00:33:30 +0800 Subject: [PATCH 2/7] fix: add description field for reply_with_quote_scope in CONFIG_METADATA_3 --- astrbot/core/config/default.py | 1 + 1 file changed, 1 insertion(+) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index c7183ab3dd..0156f0be97 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -885,6 +885,7 @@ class ChatProviderTemplate(TypedDict): "hint": "启用后,机器人回复消息时会引用原消息。实际效果以具体的平台适配器为准。", }, "reply_with_quote_scope": { + "description": "引用回复范围", "type": "string", "options": ["all", "group_only", "private_only"], "labels": [ From 919f1ddde4eac41ece52cd6e9d16c69fef9dc7e1 Mon Sep 17 00:00:00 2001 From: Luna_Dol <86590429+Luna-channel@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:06:49 +0800 Subject: [PATCH 3/7] Update stage.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复缩进问题 --- .../core/pipeline/result_decorate/stage.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index c6595899e3..d7cc045a08 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -399,14 +399,16 @@ async def process( ) if len(result.chain) > 1 and isinstance(result.chain[1], Plain): result.chain[1].text = "\n" + result.chain[1].text - - # 引用回复 - if self.reply_with_quote: - is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE - should_quote = ( - self.reply_with_quote_scope == "all" - or (self.reply_with_quote_scope == "private_only" and is_private) - or (self.reply_with_quote_scope == "group_only" and not is_private) - ) - if should_quote and not any(isinstance(item, File) for item in result.chain): - result.chain.insert(0, Reply(id=event.message_obj.message_id)) + + # 引用回复 + if self.reply_with_quote: + is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE + should_quote = ( + self.reply_with_quote_scope == "all" + or (self.reply_with_quote_scope == "private_only" and is_private) + or (self.reply_with_quote_scope == "group_only" and not is_private) + ) + if should_quote and not any( + isinstance(item, File) for item in result.chain + ): + result.chain.insert(0, Reply(id=event.message_obj.message_id)) From 36d356e2c0acea72dca685381770c7d5f58da7d1 Mon Sep 17 00:00:00 2001 From: Luna_Dol <86590429+Luna-channel@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:10:47 +0800 Subject: [PATCH 4/7] Update stage.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复排版问题2 --- astrbot/core/pipeline/result_decorate/stage.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index d7cc045a08..2550cb4f74 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -411,4 +411,6 @@ async def process( if should_quote and not any( isinstance(item, File) for item in result.chain ): - result.chain.insert(0, Reply(id=event.message_obj.message_id)) + result.chain.insert( + 0, Reply(id=event.message_obj.message_id) + ) From 4d92ac1981ab15d101eaa567e4b29a5e5e38fed5 Mon Sep 17 00:00:00 2001 From: Luna_Dol <86590429+Luna-channel@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:17:21 +0800 Subject: [PATCH 5/7] Update stage.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复排版问题3 --- astrbot/core/pipeline/result_decorate/stage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 2550cb4f74..3c2a89603f 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -30,7 +30,8 @@ async def initialize(self, ctx: PipelineContext) -> None: "reply_with_quote" ] self.reply_with_quote_scope = ctx.astrbot_config["platform_settings"].get( - "reply_with_quote_scope", "all" + "reply_with_quote_scope", + "all", ) self.t2i_word_threshold = ctx.astrbot_config["t2i_word_threshold"] try: @@ -411,6 +412,5 @@ async def process( if should_quote and not any( isinstance(item, File) for item in result.chain ): - result.chain.insert( - 0, Reply(id=event.message_obj.message_id) - ) + result.chain.insert(0, Reply(id=event.message_obj.message_id)) + From 5f837cee0c205529d6267357b97b1e27a6e8ccec Mon Sep 17 00:00:00 2001 From: Luna-channel Date: Fri, 20 Feb 2026 03:24:36 +0800 Subject: [PATCH 6/7] fix: correct indentation and remove trailing whitespace in stage.py --- .../core/pipeline/result_decorate/stage.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 3c2a89603f..457774533e 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -400,17 +400,17 @@ async def process( ) if len(result.chain) > 1 and isinstance(result.chain[1], Plain): result.chain[1].text = "\n" + result.chain[1].text - - # 引用回复 - if self.reply_with_quote: - is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE - should_quote = ( - self.reply_with_quote_scope == "all" - or (self.reply_with_quote_scope == "private_only" and is_private) - or (self.reply_with_quote_scope == "group_only" and not is_private) - ) - if should_quote and not any( - isinstance(item, File) for item in result.chain - ): - result.chain.insert(0, Reply(id=event.message_obj.message_id)) + + # 引用回复 + if self.reply_with_quote: + is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE + should_quote = ( + self.reply_with_quote_scope == "all" + or (self.reply_with_quote_scope == "private_only" and is_private) + or (self.reply_with_quote_scope == "group_only" and not is_private) + ) + if should_quote and not any( + isinstance(item, File) for item in result.chain + ): + result.chain.insert(0, Reply(id=event.message_obj.message_id)) From 93f01d9125b6ec53608fa7d4a89c1ccb41ea8fce Mon Sep 17 00:00:00 2001 From: Luna-channel Date: Fri, 20 Feb 2026 03:26:20 +0800 Subject: [PATCH 7/7] fix: ruff format - break long or expressions --- astrbot/core/pipeline/result_decorate/stage.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index 457774533e..53109232b7 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -406,11 +406,15 @@ async def process( is_private = event.get_message_type() == MessageType.FRIEND_MESSAGE should_quote = ( self.reply_with_quote_scope == "all" - or (self.reply_with_quote_scope == "private_only" and is_private) - or (self.reply_with_quote_scope == "group_only" and not is_private) + or ( + self.reply_with_quote_scope == "private_only" and is_private + ) + or ( + self.reply_with_quote_scope == "group_only" + and not is_private + ) ) if should_quote and not any( isinstance(item, File) for item in result.chain ): result.chain.insert(0, Reply(id=event.message_obj.message_id)) -