22import asyncio
33import math
44import traceback
5+ import astrbot .core .message .components as Comp
56from typing import Union , AsyncGenerator
67from ..stage import register_stage , Stage
78from ..context import PipelineContext
1112from astrbot .core .message .message_event_result import BaseMessageComponent
1213from astrbot .core .star .star_handler import star_handlers_registry , EventType
1314from astrbot .core .star .star import star_map
14- from astrbot .core .message .components import Plain , Reply , At
1515
1616
1717@register_stage
1818class RespondStage (Stage ):
19+ # 组件类型到其非空判断函数的映射
20+ _component_validators = {
21+ Comp .Plain : lambda comp : bool (comp .text and comp .text .strip ()), # 纯文本消息需要strip
22+ Comp .Face : lambda comp : comp .id is not None , # QQ表情
23+ Comp .Record : lambda comp : bool (comp .file ), # 语音
24+ Comp .Video : lambda comp : bool (comp .file ), # 视频
25+ Comp .At : lambda comp : bool (comp .qq ) or bool (comp .name ), # @
26+ Comp .AtAll : lambda comp : True , # @所有人
27+ Comp .RPS : lambda comp : True , # 不知道是啥(未完成)
28+ Comp .Dice : lambda comp : True , # 骰子(未完成)
29+ Comp .Shake : lambda comp : True , # 摇一摇(未完成)
30+ Comp .Anonymous : lambda comp : True , # 匿名(未完成)
31+ Comp .Share : lambda comp : bool (comp .url ) and bool (comp .title ), # 分享
32+ Comp .Contact : lambda comp : True , # 联系人(未完成)
33+ Comp .Location : lambda comp : bool (comp .lat and comp .lon ), # 位置
34+ Comp .Music : lambda comp : bool (comp ._type ) and bool (comp .url ) and bool (comp .audio ), # 音乐
35+ Comp .Image : lambda comp : bool (comp .file ), # 图片
36+ Comp .Reply : lambda comp : bool (comp .id ) and comp .sender_id is not None , # 回复
37+ Comp .RedBag : lambda comp : bool (comp .title ), # 红包
38+ Comp .Poke : lambda comp : comp .id != 0 and comp .qq != 0 , # 戳一戳
39+ Comp .Forward : lambda comp : bool (comp .id and comp .id .strip ()), # 转发
40+ Comp .Node : lambda comp : bool (comp .name ) and comp .uin != 0 and bool (comp .content ), # 一个转发节点
41+ Comp .Nodes : lambda comp : bool (comp .nodes ), # 多个转发节点
42+ Comp .Xml : lambda comp : bool (comp .data and comp .data .strip ()), # XML
43+ Comp .Json : lambda comp : bool (comp .data ), # JSON
44+ Comp .CardImage : lambda comp : bool (comp .file ), # 卡片图片
45+ Comp .TTS : lambda comp : bool (comp .text and comp .text .strip ()), # 语音合成
46+ Comp .Unknown : lambda comp : bool (comp .text and comp .text .strip ()), # 未知消息
47+ Comp .File : lambda comp : bool (comp .file ), # 文件
48+ Comp .WechatEmoji : lambda comp : bool (comp .md5 ), # 微信表情
49+ }
50+
1951 async def initialize (self , ctx : PipelineContext ):
2052 self .ctx = ctx
2153
@@ -62,7 +94,7 @@ async def _word_cnt(self, text: str) -> int:
6294 async def _calc_comp_interval (self , comp : BaseMessageComponent ) -> float :
6395 """分段回复 计算间隔时间"""
6496 if self .interval_method == "log" :
65- if isinstance (comp , Plain ):
97+ if isinstance (comp , Comp . Plain ):
6698 wc = await self ._word_cnt (comp .text )
6799 i = math .log (wc + 1 , self .log_base )
68100 return random .uniform (i , i + 0.5 )
@@ -72,6 +104,28 @@ async def _calc_comp_interval(self, comp: BaseMessageComponent) -> float:
72104 # random
73105 return random .uniform (self .interval [0 ], self .interval [1 ])
74106
107+ async def _is_empty_message_chain (self , chain : list [BaseMessageComponent ]):
108+ """检查消息链是否为空
109+
110+ Args:
111+ chain (list[BaseMessageComponent]): 包含消息对象的列表
112+ """
113+ if not chain :
114+ return True
115+
116+ for comp in chain :
117+ comp_type = type (comp )
118+
119+ # 检查组件类型是否在字典中
120+ if comp_type in self ._component_validators :
121+ if self ._component_validators [comp_type ](comp ):
122+ return False
123+ else :
124+ logger .info (f"空内容检查: 无法识别的组件类型: { comp_type .__name__ } " )
125+
126+ # 如果所有组件都为空
127+ return True
128+
75129 async def process (
76130 self , event : AstrMessageEvent
77131 ) -> Union [None , AsyncGenerator [None , None ]]:
@@ -82,20 +136,30 @@ async def process(
82136 if len (result .chain ) > 0 :
83137 await event ._pre_send ()
84138
139+ # 检查消息链是否为空
140+ try :
141+ if await self ._is_empty_message_chain (result .chain ):
142+ logger .info ("消息为空,跳过发送阶段" )
143+ event .clear_result ()
144+ event .stop_event ()
145+ return
146+ except Exception as e :
147+ logger .warning (f"空内容检查异常: { e } " )
148+
85149 if self .enable_seg and (
86150 (self .only_llm_result and result .is_llm_result ())
87151 or not self .only_llm_result
88152 ):
89153 decorated_comps = []
90154 if self .reply_with_mention :
91155 for comp in result .chain :
92- if isinstance (comp , At ):
156+ if isinstance (comp , Comp . At ):
93157 decorated_comps .append (comp )
94158 result .chain .remove (comp )
95159 break
96160 if self .reply_with_quote :
97161 for comp in result .chain :
98- if isinstance (comp , Reply ):
162+ if isinstance (comp , Comp . Reply ):
99163 decorated_comps .append (comp )
100164 result .chain .remove (comp )
101165 break
0 commit comments