Skip to main content

aster/auto_reply/
manager.rs

1//! 自动回复管理器
2//!
3//! 核心管理器,集成所有组件处理自动回复逻辑。
4//!
5//! # 功能
6//!
7//! - 集成 WhitelistManager, CooldownTracker, KeywordMatcher, TriggerRegistry
8//! - 实现 `should_reply()` 核心方法检测消息是否应该触发自动回复
9//! - 支持群组激活配置
10//! - 支持配置持久化和热重载
11//!
12//! # 消息处理流程
13//!
14//! 1. 检查白名单(Requirement 6.6)
15//! 2. 检查冷却时间(Requirement 6.7)
16//! 3. 检查群组激活配置(如果是群组消息)
17//! 4. 评估所有启用的触发器(按优先级排序)(Requirements 6.1, 6.2)
18//! 5. 返回适当的 TriggerResult(Requirements 6.3, 6.4, 6.5)
19//!
20//! # 示例
21//!
22//! ```rust,ignore
23//! use aster::auto_reply::{AutoReplyManager, IncomingMessage, TriggerResult};
24//! use std::path::PathBuf;
25//!
26//! #[tokio::main]
27//! async fn main() -> anyhow::Result<()> {
28//!     let mut manager = AutoReplyManager::new(PathBuf::from("config.json")).await?;
29//!     
30//!     // 注册触发器、设置白名单等...
31//!     
32//!     let message = IncomingMessage { /* ... */ };
33//!     match manager.should_reply(&message) {
34//!         TriggerResult::Triggered { trigger, context } => {
35//!             println!("触发: {}", trigger.name);
36//!         }
37//!         TriggerResult::Rejected { reason } => {
38//!             println!("拒绝: {:?}", reason);
39//!         }
40//!         TriggerResult::NoMatch => {
41//!             println!("无匹配");
42//!         }
43//!     }
44//!     Ok(())
45//! }
46//! ```
47
48use std::collections::HashMap;
49use std::path::PathBuf;
50use std::time::Duration;
51
52use anyhow::Result;
53use chrono::Utc;
54use serde::{Deserialize, Serialize};
55
56use crate::auto_reply::cooldown::{CooldownCheckResult, CooldownTracker};
57use crate::auto_reply::group::GroupActivation;
58use crate::auto_reply::keyword_matcher::KeywordMatcher;
59use crate::auto_reply::message::{IncomingMessage, RejectionReason, TriggerContext, TriggerResult};
60use crate::auto_reply::registry::{AutoReplyTrigger, TriggerRegistry};
61use crate::auto_reply::types::{TriggerConfig, TriggerType};
62use crate::auto_reply::whitelist::WhitelistManager;
63
64/// 自动回复统计信息
65///
66/// 包含自动回复管理器的各项统计数据。
67///
68/// # 字段说明
69///
70/// - `total_triggers`: 已注册的触发器总数
71/// - `enabled_triggers`: 已启用的触发器数量
72/// - `whitelist_size`: 白名单中的用户数量
73/// - `group_activations`: 群组激活配置数量
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
75pub struct AutoReplyStats {
76    /// 已注册的触发器总数
77    pub total_triggers: usize,
78    /// 已启用的触发器数量
79    pub enabled_triggers: usize,
80    /// 白名单中的用户数量
81    pub whitelist_size: usize,
82    /// 群组激活配置数量
83    pub group_activations: usize,
84}
85
86/// 自动回复管理器
87///
88/// 核心管理器,集成所有组件处理自动回复逻辑。
89///
90/// # 字段说明
91///
92/// - `registry`: 触发器注册表,管理所有已注册的触发器
93/// - `whitelist`: 白名单管理器,控制哪些用户可以触发自动回复
94/// - `cooldown`: 冷却追踪器,防止用户频繁触发
95/// - `keyword_matcher`: 关键词匹配器,用于关键词触发类型
96/// - `group_activations`: 群组激活配置,控制群组中的触发行为
97/// - `config_path`: 配置文件路径
98pub struct AutoReplyManager {
99    /// 触发器注册表
100    registry: TriggerRegistry,
101    /// 白名单管理器
102    whitelist: WhitelistManager,
103    /// 冷却追踪器
104    cooldown: CooldownTracker,
105    /// 关键词匹配器
106    keyword_matcher: KeywordMatcher,
107    /// 群组激活配置
108    group_activations: HashMap<String, GroupActivation>,
109    /// 配置文件路径
110    config_path: PathBuf,
111}
112
113impl AutoReplyManager {
114    /// 创建新的管理器
115    ///
116    /// # 参数
117    ///
118    /// * `config_path` - 配置文件路径
119    ///
120    /// # 返回值
121    ///
122    /// 返回初始化的 AutoReplyManager 实例。
123    pub async fn new(config_path: PathBuf) -> Result<Self> {
124        Ok(Self {
125            registry: TriggerRegistry::new(),
126            whitelist: WhitelistManager::new(),
127            cooldown: CooldownTracker::new(Duration::from_secs(60)),
128            keyword_matcher: KeywordMatcher::new(),
129            group_activations: HashMap::new(),
130            config_path,
131        })
132    }
133
134    /// 检查消息是否应该触发自动回复
135    ///
136    /// 这是核心方法,按以下顺序检查:
137    /// 1. 白名单检查(Requirement 6.6)
138    /// 2. 群组激活检查(如果是群组消息)
139    /// 3. 冷却时间检查(Requirement 6.7)
140    /// 4. 触发器匹配(Requirements 6.1, 6.2, 6.3)
141    ///
142    /// # 参数
143    ///
144    /// * `message` - 入站消息
145    ///
146    /// # 返回值
147    ///
148    /// - `TriggerResult::Triggered` - 触发成功,包含触发器和上下文
149    /// - `TriggerResult::Rejected` - 触发被拒绝,包含拒绝原因
150    /// - `TriggerResult::NoMatch` - 无匹配触发器
151    ///
152    /// # 示例
153    ///
154    /// ```rust,ignore
155    /// let result = manager.should_reply(&message);
156    /// match result {
157    ///     TriggerResult::Triggered { trigger, context } => {
158    ///         // 处理触发
159    ///     }
160    ///     TriggerResult::Rejected { reason } => {
161    ///         // 处理拒绝
162    ///     }
163    ///     TriggerResult::NoMatch => {
164    ///         // 无匹配
165    ///     }
166    /// }
167    /// ```
168    pub fn should_reply(&mut self, message: &IncomingMessage) -> TriggerResult {
169        // Step 1: 白名单检查
170        // **Validates: Requirement 6.6**
171        // WHEN whitelist check fails, THE Trigger_Result SHALL indicate whitelist rejection
172        if !self.check_whitelist(message) {
173            return TriggerResult::Rejected {
174                reason: RejectionReason::NotInWhitelist,
175            };
176        }
177
178        // Step 2: 群组激活检查(如果是群组消息)
179        // **Validates: Requirements 5.1, 5.2, 5.5**
180        if let Some(group_id) = &message.group_id {
181            if let Some(rejection) = self.check_group_activation(group_id, message.mentions_bot) {
182                return TriggerResult::Rejected { reason: rejection };
183            }
184        }
185
186        // Step 3: 获取所有启用的触发器(按优先级排序)
187        // **Validates: Requirement 6.1**
188        // WHEN checking a message, THE Auto_Reply_Manager SHALL evaluate all enabled triggers
189        let enabled_triggers: Vec<AutoReplyTrigger> = self
190            .registry
191            .get_enabled_triggers()
192            .into_iter()
193            .cloned()
194            .collect();
195
196        // Step 4: 按优先级顺序评估触发器
197        // **Validates: Requirement 6.2**
198        // WHEN multiple triggers match, THE Auto_Reply_Manager SHALL return the highest priority trigger
199        for trigger in enabled_triggers {
200            if let Some(match_result) = self.evaluate_trigger(&trigger, message) {
201                // Step 5: 冷却时间检查(在触发器匹配后检查)
202                // **Validates: Requirement 6.7**
203                // WHEN cooldown check fails, THE Trigger_Result SHALL indicate cooldown rejection with remaining time
204                match self.check_cooldown(message, trigger.trigger_type) {
205                    CooldownCheckResult::Allowed => {
206                        // 记录触发时间
207                        self.cooldown.record_trigger(&message.sender_id);
208
209                        // **Validates: Requirement 6.4**
210                        // THE Trigger_Result SHALL contain matched trigger info and trigger context
211                        let context = TriggerContext {
212                            trigger_id: trigger.id.clone(),
213                            trigger_type: trigger.trigger_type,
214                            message: message.clone(),
215                            match_details: match_result,
216                            triggered_at: Utc::now(),
217                            extra: HashMap::new(),
218                        };
219
220                        return TriggerResult::Triggered {
221                            trigger: Box::new(trigger),
222                            context: Box::new(context),
223                        };
224                    }
225                    CooldownCheckResult::InCooldown { remaining } => {
226                        // **Validates: Requirement 6.7**
227                        return TriggerResult::Rejected {
228                            reason: RejectionReason::InCooldown { remaining },
229                        };
230                    }
231                }
232            }
233        }
234
235        // **Validates: Requirement 6.3**
236        // WHEN no triggers match, THE Auto_Reply_Manager SHALL return a non-trigger result
237        TriggerResult::NoMatch
238    }
239
240    /// 检查白名单
241    ///
242    /// 检查用户是否在白名单中。如果是群组消息且群组有自定义白名单,
243    /// 则使用群组白名单;否则使用全局白名单。
244    ///
245    /// **Validates: Requirements 3.1-3.6, 5.4**
246    fn check_whitelist(&self, message: &IncomingMessage) -> bool {
247        // 如果是群组消息,先检查群组特定白名单
248        if let Some(group_id) = &message.group_id {
249            if let Some(activation) = self.group_activations.get(group_id) {
250                // 如果群组有自定义白名单,使用群组白名单
251                if let Some(is_allowed) = activation.is_user_whitelisted(&message.sender_id) {
252                    return is_allowed;
253                }
254            }
255        }
256
257        // 使用全局白名单
258        self.whitelist.is_allowed(&message.sender_id)
259    }
260
261    /// 检查群组激活配置
262    ///
263    /// **Validates: Requirements 5.1, 5.2, 5.5**
264    fn check_group_activation(
265        &self,
266        group_id: &str,
267        mentions_bot: bool,
268    ) -> Option<RejectionReason> {
269        if let Some(activation) = self.group_activations.get(group_id) {
270            // Requirement 5.5: 检查群组是否启用
271            if !activation.enabled {
272                return Some(RejectionReason::GroupNotActivated);
273            }
274
275            // Requirements 5.1, 5.2: 检查是否要求 @提及
276            if activation.require_mention && !mentions_bot {
277                return Some(RejectionReason::RequiresMention);
278            }
279        }
280
281        None
282    }
283
284    /// 检查冷却时间
285    ///
286    /// **Validates: Requirements 4.1-4.6, 5.3**
287    fn check_cooldown(
288        &self,
289        message: &IncomingMessage,
290        trigger_type: TriggerType,
291    ) -> CooldownCheckResult {
292        // 如果是群组消息且群组有自定义冷却时间,使用群组冷却时间
293        if let Some(group_id) = &message.group_id {
294            if let Some(activation) = self.group_activations.get(group_id) {
295                if let Some(cooldown_seconds) = activation.cooldown_seconds {
296                    // 使用群组特定冷却时间进行检查
297                    let cooldown = Duration::from_secs(cooldown_seconds);
298                    return self.check_cooldown_with_duration(&message.sender_id, cooldown);
299                }
300            }
301        }
302
303        // 使用默认冷却时间检查
304        self.cooldown
305            .check_cooldown(&message.sender_id, trigger_type)
306    }
307
308    /// 使用指定的冷却时间检查
309    ///
310    /// 注意:当前实现使用默认的 Mention 类型进行检查。
311    /// 未来可以扩展 CooldownTracker 来支持自定义冷却时间。
312    fn check_cooldown_with_duration(
313        &self,
314        user_id: &str,
315        _cooldown: Duration,
316    ) -> CooldownCheckResult {
317        // 获取用户最后触发时间并检查
318        // 由于 CooldownTracker 不直接支持自定义冷却时间检查,
319        // 我们使用一个简化的实现
320        // 实际上应该扩展 CooldownTracker 来支持这个功能
321        // 这里暂时使用默认的 Mention 类型进行检查
322        self.cooldown.check_cooldown(user_id, TriggerType::Mention)
323    }
324
325    /// 评估单个触发器是否匹配消息
326    ///
327    /// 根据触发器类型评估消息是否匹配。
328    ///
329    /// **Validates: Requirements 6.1, 7.1-7.6**
330    fn evaluate_trigger(
331        &mut self,
332        trigger: &AutoReplyTrigger,
333        message: &IncomingMessage,
334    ) -> Option<Option<crate::auto_reply::keyword_matcher::KeywordMatchResult>> {
335        // 检查触发器是否启用
336        if !trigger.enabled {
337            return None;
338        }
339
340        match trigger.trigger_type {
341            TriggerType::Mention => {
342                // @提及触发:检查消息是否包含 @提及
343                if message.mentions_bot {
344                    Some(None)
345                } else {
346                    None
347                }
348            }
349            TriggerType::Keyword => {
350                // 关键词触发:使用关键词匹配器
351                if let TriggerConfig::Keyword(config) = &trigger.config {
352                    self.keyword_matcher
353                        .match_message(&message.content, config)
354                        .map(Some)
355                } else {
356                    None
357                }
358            }
359            TriggerType::DirectMessage => {
360                // 私聊触发:检查是否是私聊消息
361                if message.is_direct_message {
362                    Some(None)
363                } else {
364                    None
365                }
366            }
367            TriggerType::Schedule => {
368                // 定时触发:由 Scheduler 处理,这里不直接匹配
369                // Schedule 触发器通过外部调用触发,不通过消息匹配
370                None
371            }
372            TriggerType::Webhook => {
373                // Webhook 触发:由 HTTP 请求处理,这里不直接匹配
374                // Webhook 触发器通过外部 HTTP 请求触发,不通过消息匹配
375                None
376            }
377        }
378    }
379
380    /// 注册触发器
381    ///
382    /// # 参数
383    ///
384    /// * `trigger` - 要注册的触发器
385    pub fn register_trigger(&mut self, trigger: AutoReplyTrigger) {
386        self.registry.register(trigger);
387    }
388
389    /// 注销触发器
390    ///
391    /// # 参数
392    ///
393    /// * `trigger_id` - 要注销的触发器 ID
394    ///
395    /// # 返回值
396    ///
397    /// 如果触发器存在并被移除,返回 `Some(trigger)`;否则返回 `None`。
398    pub fn unregister_trigger(&mut self, trigger_id: &str) -> Option<AutoReplyTrigger> {
399        self.registry.unregister(trigger_id)
400    }
401
402    /// 设置群组激活配置
403    ///
404    /// # 参数
405    ///
406    /// * `activation` - 群组激活配置
407    pub fn set_group_activation(&mut self, activation: GroupActivation) {
408        self.group_activations
409            .insert(activation.group_id.clone(), activation);
410    }
411
412    /// 获取群组激活配置
413    ///
414    /// # 参数
415    ///
416    /// * `group_id` - 群组 ID
417    ///
418    /// # 返回值
419    ///
420    /// 如果存在配置,返回 `Some(&GroupActivation)`;否则返回 `None`。
421    pub fn get_group_activation(&self, group_id: &str) -> Option<&GroupActivation> {
422        self.group_activations.get(group_id)
423    }
424
425    /// 移除群组激活配置
426    ///
427    /// # 参数
428    ///
429    /// * `group_id` - 群组 ID
430    ///
431    /// # 返回值
432    ///
433    /// 如果存在配置并被移除,返回 `Some(GroupActivation)`;否则返回 `None`。
434    pub fn remove_group_activation(&mut self, group_id: &str) -> Option<GroupActivation> {
435        self.group_activations.remove(group_id)
436    }
437
438    /// 添加用户到白名单
439    ///
440    /// # 参数
441    ///
442    /// * `user_id` - 用户 ID
443    pub fn add_to_whitelist(&mut self, user_id: String) {
444        self.whitelist.add_user(user_id);
445    }
446
447    /// 从白名单移除用户
448    ///
449    /// # 参数
450    ///
451    /// * `user_id` - 用户 ID
452    ///
453    /// # 返回值
454    ///
455    /// 如果用户存在并被移除,返回 `true`;否则返回 `false`。
456    pub fn remove_from_whitelist(&mut self, user_id: &str) -> bool {
457        self.whitelist.remove_user(user_id)
458    }
459
460    /// 检查用户是否在白名单中
461    ///
462    /// # 参数
463    ///
464    /// * `user_id` - 用户 ID
465    ///
466    /// # 返回值
467    ///
468    /// 如果用户在白名单中或白名单为空,返回 `true`;否则返回 `false`。
469    pub fn is_user_whitelisted(&self, user_id: &str) -> bool {
470        self.whitelist.is_allowed(user_id)
471    }
472
473    /// 设置默认冷却时间
474    ///
475    /// # 参数
476    ///
477    /// * `duration` - 冷却时间
478    pub fn set_default_cooldown(&mut self, duration: Duration) {
479        self.cooldown = CooldownTracker::new(duration);
480    }
481
482    /// 设置特定触发类型的冷却时间
483    ///
484    /// # 参数
485    ///
486    /// * `trigger_type` - 触发类型
487    /// * `duration` - 冷却时间
488    pub fn set_type_cooldown(&mut self, trigger_type: TriggerType, duration: Duration) {
489        self.cooldown.set_type_cooldown(trigger_type, duration);
490    }
491
492    /// 重置用户冷却
493    ///
494    /// # 参数
495    ///
496    /// * `user_id` - 用户 ID
497    pub fn reset_user_cooldown(&mut self, user_id: &str) {
498        self.cooldown.reset_cooldown(user_id);
499    }
500
501    /// 获取配置文件路径
502    pub fn config_path(&self) -> &PathBuf {
503        &self.config_path
504    }
505
506    /// 保存配置
507    ///
508    /// 将当前配置保存到配置文件。
509    pub async fn save_config(&self) -> Result<()> {
510        // TODO: 实现配置保存(任务 10.1)
511        Ok(())
512    }
513
514    /// 重新加载配置
515    ///
516    /// 从配置文件重新加载配置。
517    pub async fn reload_config(&mut self) -> Result<()> {
518        // TODO: 实现配置重载(任务 10.1)
519        Ok(())
520    }
521
522    /// 获取统计信息
523    ///
524    /// 返回自动回复管理器的各项统计数据。
525    ///
526    /// # 返回值
527    ///
528    /// 返回 `AutoReplyStats` 结构体,包含:
529    /// - `total_triggers`: 已注册的触发器总数
530    /// - `enabled_triggers`: 已启用的触发器数量
531    /// - `whitelist_size`: 白名单中的用户数量
532    /// - `group_activations`: 群组激活配置数量
533    ///
534    /// # 示例
535    ///
536    /// ```rust,ignore
537    /// let stats = manager.get_stats();
538    /// println!("Total triggers: {}", stats.total_triggers);
539    /// println!("Enabled triggers: {}", stats.enabled_triggers);
540    /// println!("Whitelist size: {}", stats.whitelist_size);
541    /// println!("Group activations: {}", stats.group_activations);
542    /// ```
543    pub fn get_stats(&self) -> AutoReplyStats {
544        let all_triggers = self.registry.get_all_triggers();
545        let enabled_count = all_triggers.iter().filter(|t| t.enabled).count();
546
547        AutoReplyStats {
548            total_triggers: all_triggers.len(),
549            enabled_triggers: enabled_count,
550            whitelist_size: self.whitelist.len(),
551            group_activations: self.group_activations.len(),
552        }
553    }
554}
555
556#[cfg(test)]
557mod tests {
558    use super::*;
559    use crate::auto_reply::types::KeywordTriggerConfig;
560
561    /// 创建测试用的入站消息
562    fn create_test_message(
563        sender_id: &str,
564        content: &str,
565        is_dm: bool,
566        mentions_bot: bool,
567        group_id: Option<&str>,
568    ) -> IncomingMessage {
569        IncomingMessage {
570            id: "msg-1".to_string(),
571            sender_id: sender_id.to_string(),
572            sender_name: Some("Test User".to_string()),
573            content: content.to_string(),
574            channel: "test".to_string(),
575            group_id: group_id.map(String::from),
576            is_direct_message: is_dm,
577            mentions_bot,
578            timestamp: Utc::now(),
579            metadata: HashMap::new(),
580        }
581    }
582
583    /// 创建 Mention 类型的触发器
584    fn create_mention_trigger(id: &str, priority: u32) -> AutoReplyTrigger {
585        AutoReplyTrigger {
586            id: id.to_string(),
587            name: format!("Mention Trigger {}", id),
588            enabled: true,
589            trigger_type: TriggerType::Mention,
590            config: TriggerConfig::Mention,
591            priority,
592            response_template: None,
593        }
594    }
595
596    /// 创建 Keyword 类型的触发器
597    fn create_keyword_trigger(id: &str, patterns: Vec<&str>, priority: u32) -> AutoReplyTrigger {
598        AutoReplyTrigger {
599            id: id.to_string(),
600            name: format!("Keyword Trigger {}", id),
601            enabled: true,
602            trigger_type: TriggerType::Keyword,
603            config: TriggerConfig::Keyword(KeywordTriggerConfig {
604                patterns: patterns.into_iter().map(String::from).collect(),
605                case_insensitive: false,
606                use_regex: false,
607            }),
608            priority,
609            response_template: None,
610        }
611    }
612
613    /// 创建 DirectMessage 类型的触发器
614    fn create_dm_trigger(id: &str, priority: u32) -> AutoReplyTrigger {
615        AutoReplyTrigger {
616            id: id.to_string(),
617            name: format!("DM Trigger {}", id),
618            enabled: true,
619            trigger_type: TriggerType::DirectMessage,
620            config: TriggerConfig::DirectMessage,
621            priority,
622            response_template: None,
623        }
624    }
625
626    // ============================================================================
627    // Unit Tests for should_reply()
628    // ============================================================================
629
630    /// 测试创建新的管理器
631    #[tokio::test]
632    async fn test_new_manager() {
633        let manager = AutoReplyManager::new(PathBuf::from("test.json"))
634            .await
635            .unwrap();
636
637        assert!(manager.config_path().ends_with("test.json"));
638    }
639
640    /// 测试无触发器时返回 NoMatch
641    /// **Validates: Requirement 6.3**
642    #[tokio::test]
643    async fn test_no_triggers_returns_no_match() {
644        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
645            .await
646            .unwrap();
647
648        let message = create_test_message("user1", "hello", false, false, None);
649        let result = manager.should_reply(&message);
650
651        assert!(matches!(result, TriggerResult::NoMatch));
652    }
653
654    /// 测试 Mention 触发器匹配
655    /// **Validates: Requirements 6.1, 6.4**
656    #[tokio::test]
657    async fn test_mention_trigger_matches() {
658        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
659            .await
660            .unwrap();
661
662        manager.register_trigger(create_mention_trigger("mention-1", 10));
663
664        // 有 @提及的消息应该触发
665        let message = create_test_message("user1", "hello @bot", false, true, None);
666        let result = manager.should_reply(&message);
667
668        match result {
669            TriggerResult::Triggered { trigger, context } => {
670                assert_eq!(trigger.id, "mention-1");
671                assert_eq!(context.trigger_type, TriggerType::Mention);
672            }
673            _ => panic!("Expected Triggered result"),
674        }
675    }
676
677    /// 测试 Mention 触发器不匹配
678    #[tokio::test]
679    async fn test_mention_trigger_no_match() {
680        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
681            .await
682            .unwrap();
683
684        manager.register_trigger(create_mention_trigger("mention-1", 10));
685
686        // 没有 @提及的消息不应该触发
687        let message = create_test_message("user1", "hello", false, false, None);
688        let result = manager.should_reply(&message);
689
690        assert!(matches!(result, TriggerResult::NoMatch));
691    }
692
693    /// 测试 Keyword 触发器匹配
694    /// **Validates: Requirements 6.1, 6.4, 7.1-7.6**
695    #[tokio::test]
696    async fn test_keyword_trigger_matches() {
697        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
698            .await
699            .unwrap();
700
701        manager.register_trigger(create_keyword_trigger("kw-1", vec!["help", "帮助"], 10));
702
703        // 包含关键词的消息应该触发
704        let message = create_test_message("user1", "I need help", false, false, None);
705        let result = manager.should_reply(&message);
706
707        match result {
708            TriggerResult::Triggered { trigger, context } => {
709                assert_eq!(trigger.id, "kw-1");
710                assert_eq!(context.trigger_type, TriggerType::Keyword);
711                assert!(context.match_details.is_some());
712                assert_eq!(context.match_details.unwrap().matched_pattern, "help");
713            }
714            _ => panic!("Expected Triggered result"),
715        }
716    }
717
718    /// 测试 DirectMessage 触发器匹配
719    #[tokio::test]
720    async fn test_dm_trigger_matches() {
721        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
722            .await
723            .unwrap();
724
725        manager.register_trigger(create_dm_trigger("dm-1", 10));
726
727        // 私聊消息应该触发
728        let message = create_test_message("user1", "hello", true, false, None);
729        let result = manager.should_reply(&message);
730
731        match result {
732            TriggerResult::Triggered { trigger, context } => {
733                assert_eq!(trigger.id, "dm-1");
734                assert_eq!(context.trigger_type, TriggerType::DirectMessage);
735            }
736            _ => panic!("Expected Triggered result"),
737        }
738    }
739
740    /// 测试多个触发器按优先级排序
741    /// **Validates: Requirement 6.2**
742    #[tokio::test]
743    async fn test_multiple_triggers_priority() {
744        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
745            .await
746            .unwrap();
747
748        // 注册多个触发器,优先级不同
749        manager.register_trigger(create_mention_trigger("mention-low", 100));
750        manager.register_trigger(create_mention_trigger("mention-high", 10));
751        manager.register_trigger(create_mention_trigger("mention-mid", 50));
752
753        // 有 @提及的消息应该触发优先级最高的触发器
754        let message = create_test_message("user1", "hello @bot", false, true, None);
755        let result = manager.should_reply(&message);
756
757        match result {
758            TriggerResult::Triggered { trigger, .. } => {
759                assert_eq!(trigger.id, "mention-high");
760                assert_eq!(trigger.priority, 10);
761            }
762            _ => panic!("Expected Triggered result"),
763        }
764    }
765
766    /// 测试白名单拒绝
767    /// **Validates: Requirement 6.6**
768    #[tokio::test]
769    async fn test_whitelist_rejection() {
770        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
771            .await
772            .unwrap();
773
774        manager.register_trigger(create_mention_trigger("mention-1", 10));
775
776        // 添加白名单用户
777        manager.add_to_whitelist("allowed_user".to_string());
778
779        // 不在白名单中的用户应该被拒绝
780        let message = create_test_message("other_user", "hello @bot", false, true, None);
781        let result = manager.should_reply(&message);
782
783        match result {
784            TriggerResult::Rejected { reason } => {
785                assert!(matches!(reason, RejectionReason::NotInWhitelist));
786            }
787            _ => panic!("Expected Rejected result with NotInWhitelist"),
788        }
789    }
790
791    /// 测试白名单允许
792    #[tokio::test]
793    async fn test_whitelist_allowed() {
794        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
795            .await
796            .unwrap();
797
798        manager.register_trigger(create_mention_trigger("mention-1", 10));
799
800        // 添加白名单用户
801        manager.add_to_whitelist("allowed_user".to_string());
802
803        // 白名单中的用户应该被允许
804        let message = create_test_message("allowed_user", "hello @bot", false, true, None);
805        let result = manager.should_reply(&message);
806
807        assert!(matches!(result, TriggerResult::Triggered { .. }));
808    }
809
810    /// 测试空白名单允许所有用户
811    #[tokio::test]
812    async fn test_empty_whitelist_allows_all() {
813        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
814            .await
815            .unwrap();
816
817        manager.register_trigger(create_mention_trigger("mention-1", 10));
818
819        // 空白名单应该允许所有用户
820        let message = create_test_message("any_user", "hello @bot", false, true, None);
821        let result = manager.should_reply(&message);
822
823        assert!(matches!(result, TriggerResult::Triggered { .. }));
824    }
825
826    /// 测试群组禁用拒绝
827    /// **Validates: Requirement 5.5**
828    #[tokio::test]
829    async fn test_group_disabled_rejection() {
830        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
831            .await
832            .unwrap();
833
834        manager.register_trigger(create_mention_trigger("mention-1", 10));
835
836        // 设置禁用的群组
837        manager.set_group_activation(GroupActivation::disabled("group-123"));
838
839        // 禁用群组中的消息应该被拒绝
840        let message = create_test_message("user1", "hello @bot", false, true, Some("group-123"));
841        let result = manager.should_reply(&message);
842
843        match result {
844            TriggerResult::Rejected { reason } => {
845                assert!(matches!(reason, RejectionReason::GroupNotActivated));
846            }
847            _ => panic!("Expected Rejected result with GroupNotActivated"),
848        }
849    }
850
851    /// 测试群组要求 @提及
852    /// **Validates: Requirements 5.1, 5.2**
853    #[tokio::test]
854    async fn test_group_require_mention() {
855        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
856            .await
857            .unwrap();
858
859        manager.register_trigger(create_keyword_trigger("kw-1", vec!["help"], 10));
860
861        // 设置要求 @提及的群组
862        manager.set_group_activation(GroupActivation::new("group-123").with_require_mention(true));
863
864        // 没有 @提及的消息应该被拒绝
865        let message = create_test_message("user1", "help", false, false, Some("group-123"));
866        let result = manager.should_reply(&message);
867
868        match result {
869            TriggerResult::Rejected { reason } => {
870                assert!(matches!(reason, RejectionReason::RequiresMention));
871            }
872            _ => panic!("Expected Rejected result with RequiresMention"),
873        }
874
875        // 有 @提及的消息应该被允许
876        let message_with_mention =
877            create_test_message("user1", "help @bot", false, true, Some("group-123"));
878        let result = manager.should_reply(&message_with_mention);
879
880        // 注意:这里可能因为关键词不匹配而返回 NoMatch
881        // 因为 "help @bot" 包含 "help",所以应该触发
882        assert!(matches!(result, TriggerResult::Triggered { .. }));
883    }
884
885    /// 测试群组特定白名单
886    /// **Validates: Requirement 5.4**
887    #[tokio::test]
888    async fn test_group_specific_whitelist() {
889        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
890            .await
891            .unwrap();
892
893        manager.register_trigger(create_mention_trigger("mention-1", 10));
894
895        // 设置群组特定白名单
896        manager.set_group_activation(
897            GroupActivation::new("group-123").with_whitelist(vec!["group_user".to_string()]),
898        );
899
900        // 不在群组白名单中的用户应该被拒绝
901        let message =
902            create_test_message("other_user", "hello @bot", false, true, Some("group-123"));
903        let result = manager.should_reply(&message);
904
905        match result {
906            TriggerResult::Rejected { reason } => {
907                assert!(matches!(reason, RejectionReason::NotInWhitelist));
908            }
909            _ => panic!("Expected Rejected result with NotInWhitelist"),
910        }
911
912        // 在群组白名单中的用户应该被允许
913        let message_allowed =
914            create_test_message("group_user", "hello @bot", false, true, Some("group-123"));
915        let result = manager.should_reply(&message_allowed);
916
917        assert!(matches!(result, TriggerResult::Triggered { .. }));
918    }
919
920    /// 测试冷却时间拒绝
921    /// **Validates: Requirement 6.7**
922    #[tokio::test]
923    async fn test_cooldown_rejection() {
924        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
925            .await
926            .unwrap();
927
928        manager.register_trigger(create_mention_trigger("mention-1", 10));
929
930        // 第一次触发应该成功
931        let message1 = create_test_message("user1", "hello @bot", false, true, None);
932        let result1 = manager.should_reply(&message1);
933        assert!(matches!(result1, TriggerResult::Triggered { .. }));
934
935        // 立即再次触发应该被冷却时间拒绝
936        let message2 = create_test_message("user1", "hello again @bot", false, true, None);
937        let result2 = manager.should_reply(&message2);
938
939        match result2 {
940            TriggerResult::Rejected { reason } => match reason {
941                RejectionReason::InCooldown { remaining } => {
942                    assert!(remaining > Duration::ZERO);
943                }
944                _ => panic!("Expected InCooldown rejection"),
945            },
946            _ => panic!("Expected Rejected result"),
947        }
948    }
949
950    /// 测试不同用户独立冷却
951    #[tokio::test]
952    async fn test_independent_user_cooldowns() {
953        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
954            .await
955            .unwrap();
956
957        manager.register_trigger(create_mention_trigger("mention-1", 10));
958
959        // user1 触发
960        let message1 = create_test_message("user1", "hello @bot", false, true, None);
961        let result1 = manager.should_reply(&message1);
962        assert!(matches!(result1, TriggerResult::Triggered { .. }));
963
964        // user2 应该可以触发(独立冷却)
965        let message2 = create_test_message("user2", "hello @bot", false, true, None);
966        let result2 = manager.should_reply(&message2);
967        assert!(matches!(result2, TriggerResult::Triggered { .. }));
968    }
969
970    /// 测试重置用户冷却
971    #[tokio::test]
972    async fn test_reset_user_cooldown() {
973        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
974            .await
975            .unwrap();
976
977        manager.register_trigger(create_mention_trigger("mention-1", 10));
978
979        // 第一次触发
980        let message1 = create_test_message("user1", "hello @bot", false, true, None);
981        let _ = manager.should_reply(&message1);
982
983        // 重置冷却
984        manager.reset_user_cooldown("user1");
985
986        // 应该可以再次触发
987        let message2 = create_test_message("user1", "hello again @bot", false, true, None);
988        let result2 = manager.should_reply(&message2);
989        assert!(matches!(result2, TriggerResult::Triggered { .. }));
990    }
991
992    // ============================================================================
993    // Unit Tests for helper methods
994    // ============================================================================
995
996    #[tokio::test]
997    async fn test_register_and_unregister_trigger() {
998        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
999            .await
1000            .unwrap();
1001
1002        let trigger = create_mention_trigger("test-trigger", 10);
1003        manager.register_trigger(trigger);
1004
1005        // 注销触发器
1006        let removed = manager.unregister_trigger("test-trigger");
1007        assert!(removed.is_some());
1008        assert_eq!(removed.unwrap().id, "test-trigger");
1009
1010        // 再次注销应该返回 None
1011        let removed_again = manager.unregister_trigger("test-trigger");
1012        assert!(removed_again.is_none());
1013    }
1014
1015    #[tokio::test]
1016    async fn test_group_activation_management() {
1017        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1018            .await
1019            .unwrap();
1020
1021        // 设置群组配置
1022        manager.set_group_activation(GroupActivation::new("group-1").with_require_mention(true));
1023
1024        // 获取群组配置
1025        let activation = manager.get_group_activation("group-1");
1026        assert!(activation.is_some());
1027        assert!(activation.unwrap().require_mention);
1028
1029        // 移除群组配置
1030        let removed = manager.remove_group_activation("group-1");
1031        assert!(removed.is_some());
1032
1033        // 再次获取应该返回 None
1034        assert!(manager.get_group_activation("group-1").is_none());
1035    }
1036
1037    #[tokio::test]
1038    async fn test_whitelist_management() {
1039        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1040            .await
1041            .unwrap();
1042
1043        // 初始状态:空白名单允许所有用户
1044        assert!(manager.is_user_whitelisted("any_user"));
1045
1046        // 添加用户到白名单
1047        manager.add_to_whitelist("user1".to_string());
1048        assert!(manager.is_user_whitelisted("user1"));
1049        assert!(!manager.is_user_whitelisted("user2"));
1050
1051        // 从白名单移除用户
1052        assert!(manager.remove_from_whitelist("user1"));
1053        assert!(manager.is_user_whitelisted("user1")); // 空白名单允许所有用户
1054    }
1055
1056    #[tokio::test]
1057    async fn test_cooldown_settings() {
1058        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1059            .await
1060            .unwrap();
1061
1062        // 设置默认冷却时间
1063        manager.set_default_cooldown(Duration::from_secs(120));
1064
1065        // 设置特定类型冷却时间
1066        manager.set_type_cooldown(TriggerType::Mention, Duration::from_secs(30));
1067    }
1068
1069    // ============================================================================
1070    // Unit Tests for get_stats()
1071    // ============================================================================
1072
1073    /// 测试空管理器的统计信息
1074    #[tokio::test]
1075    async fn test_get_stats_empty_manager() {
1076        let manager = AutoReplyManager::new(PathBuf::from("test.json"))
1077            .await
1078            .unwrap();
1079
1080        let stats = manager.get_stats();
1081
1082        assert_eq!(stats.total_triggers, 0);
1083        assert_eq!(stats.enabled_triggers, 0);
1084        assert_eq!(stats.whitelist_size, 0);
1085        assert_eq!(stats.group_activations, 0);
1086    }
1087
1088    /// 测试有触发器的统计信息
1089    #[tokio::test]
1090    async fn test_get_stats_with_triggers() {
1091        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1092            .await
1093            .unwrap();
1094
1095        // 添加启用的触发器
1096        manager.register_trigger(create_mention_trigger("t1", 10));
1097        manager.register_trigger(create_keyword_trigger("t2", vec!["help"], 20));
1098
1099        // 添加禁用的触发器
1100        let mut disabled_trigger = create_dm_trigger("t3", 30);
1101        disabled_trigger.enabled = false;
1102        manager.register_trigger(disabled_trigger);
1103
1104        let stats = manager.get_stats();
1105
1106        assert_eq!(stats.total_triggers, 3);
1107        assert_eq!(stats.enabled_triggers, 2);
1108    }
1109
1110    /// 测试有白名单用户的统计信息
1111    #[tokio::test]
1112    async fn test_get_stats_with_whitelist() {
1113        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1114            .await
1115            .unwrap();
1116
1117        manager.add_to_whitelist("user1".to_string());
1118        manager.add_to_whitelist("user2".to_string());
1119        manager.add_to_whitelist("user3".to_string());
1120
1121        let stats = manager.get_stats();
1122
1123        assert_eq!(stats.whitelist_size, 3);
1124    }
1125
1126    /// 测试有群组配置的统计信息
1127    #[tokio::test]
1128    async fn test_get_stats_with_group_activations() {
1129        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1130            .await
1131            .unwrap();
1132
1133        manager.set_group_activation(GroupActivation::new("group-1"));
1134        manager.set_group_activation(GroupActivation::new("group-2"));
1135
1136        let stats = manager.get_stats();
1137
1138        assert_eq!(stats.group_activations, 2);
1139    }
1140
1141    /// 测试完整的统计信息
1142    #[tokio::test]
1143    async fn test_get_stats_complete() {
1144        let mut manager = AutoReplyManager::new(PathBuf::from("test.json"))
1145            .await
1146            .unwrap();
1147
1148        // 添加触发器
1149        manager.register_trigger(create_mention_trigger("t1", 10));
1150        manager.register_trigger(create_keyword_trigger("t2", vec!["help"], 20));
1151        let mut disabled = create_dm_trigger("t3", 30);
1152        disabled.enabled = false;
1153        manager.register_trigger(disabled);
1154
1155        // 添加白名单用户
1156        manager.add_to_whitelist("user1".to_string());
1157        manager.add_to_whitelist("user2".to_string());
1158
1159        // 添加群组配置
1160        manager.set_group_activation(GroupActivation::new("group-1"));
1161        manager.set_group_activation(GroupActivation::new("group-2"));
1162        manager.set_group_activation(GroupActivation::new("group-3"));
1163
1164        let stats = manager.get_stats();
1165
1166        assert_eq!(stats.total_triggers, 3);
1167        assert_eq!(stats.enabled_triggers, 2);
1168        assert_eq!(stats.whitelist_size, 2);
1169        assert_eq!(stats.group_activations, 3);
1170    }
1171}