Skip to main content

aster/auto_reply/
group.rs

1//! 群组激活配置
2//!
3//! 控制群组中的自动回复触发行为。
4//!
5//! # 功能
6//!
7//! - 支持 `require_mention` 选项,要求 @提及才触发(Requirement 5.1, 5.2)
8//! - 支持群组特定冷却时间覆盖(Requirement 5.3)
9//! - 支持群组特定白名单覆盖(Requirement 5.4)
10//! - 支持 `enabled` 标志禁用特定群组的自动回复(Requirement 5.5)
11//!
12//! # 示例
13//!
14//! ```rust
15//! use aster::auto_reply::GroupActivation;
16//!
17//! // 创建基本群组配置
18//! let activation = GroupActivation::new("group-123");
19//! assert!(activation.enabled);
20//! assert!(!activation.require_mention);
21//!
22//! // 使用 builder 模式创建配置
23//! let activation = GroupActivation::new("group-456")
24//!     .with_require_mention(true)
25//!     .with_cooldown(120)
26//!     .with_whitelist(vec!["user1".to_string(), "user2".to_string()]);
27//!
28//! assert!(activation.require_mention);
29//! assert_eq!(activation.cooldown_seconds, Some(120));
30//! ```
31
32use serde::{Deserialize, Serialize};
33
34/// 群组激活配置
35///
36/// 控制特定群组中的自动回复触发行为。
37///
38/// # 字段说明
39///
40/// - `group_id`: 群组的唯一标识符
41/// - `enabled`: 是否在该群组启用自动回复(Requirement 5.5)
42/// - `require_mention`: 是否要求 @提及才触发(Requirement 5.1, 5.2)
43/// - `cooldown_seconds`: 群组特定的冷却时间覆盖(Requirement 5.3)
44/// - `whitelist`: 群组特定的白名单覆盖(Requirement 5.4)
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46pub struct GroupActivation {
47    /// 群组 ID
48    pub group_id: String,
49    /// 是否启用自动回复
50    /// **Validates: Requirement 5.5**
51    #[serde(default = "default_true")]
52    pub enabled: bool,
53    /// 是否要求 @提及
54    /// **Validates: Requirements 5.1, 5.2**
55    #[serde(default)]
56    pub require_mention: bool,
57    /// 群组特定冷却时间(秒)
58    /// **Validates: Requirement 5.3**
59    #[serde(default)]
60    pub cooldown_seconds: Option<u64>,
61    /// 群组特定白名单
62    /// **Validates: Requirement 5.4**
63    #[serde(default)]
64    pub whitelist: Option<Vec<String>>,
65}
66
67fn default_true() -> bool {
68    true
69}
70
71impl GroupActivation {
72    /// 创建新的群组激活配置
73    ///
74    /// 默认启用自动回复,不要求 @提及。
75    ///
76    /// # 参数
77    ///
78    /// * `group_id` - 群组 ID
79    ///
80    /// # 示例
81    ///
82    /// ```rust
83    /// use aster::auto_reply::GroupActivation;
84    ///
85    /// let activation = GroupActivation::new("group-123");
86    /// assert!(activation.enabled);
87    /// assert!(!activation.require_mention);
88    /// assert!(activation.cooldown_seconds.is_none());
89    /// assert!(activation.whitelist.is_none());
90    /// ```
91    pub fn new(group_id: impl Into<String>) -> Self {
92        Self {
93            group_id: group_id.into(),
94            enabled: true,
95            require_mention: false,
96            cooldown_seconds: None,
97            whitelist: None,
98        }
99    }
100
101    /// 创建禁用的群组配置
102    ///
103    /// **Validates: Requirement 5.5**
104    ///
105    /// # 参数
106    ///
107    /// * `group_id` - 群组 ID
108    ///
109    /// # 示例
110    ///
111    /// ```rust
112    /// use aster::auto_reply::GroupActivation;
113    ///
114    /// let activation = GroupActivation::disabled("group-123");
115    /// assert!(!activation.enabled);
116    /// ```
117    pub fn disabled(group_id: impl Into<String>) -> Self {
118        Self {
119            group_id: group_id.into(),
120            enabled: false,
121            require_mention: false,
122            cooldown_seconds: None,
123            whitelist: None,
124        }
125    }
126
127    /// 设置是否启用自动回复
128    ///
129    /// **Validates: Requirement 5.5**
130    ///
131    /// # 参数
132    ///
133    /// * `enabled` - 是否启用
134    ///
135    /// # 示例
136    ///
137    /// ```rust
138    /// use aster::auto_reply::GroupActivation;
139    ///
140    /// let activation = GroupActivation::new("group-123").with_enabled(false);
141    /// assert!(!activation.enabled);
142    /// ```
143    pub fn with_enabled(mut self, enabled: bool) -> Self {
144        self.enabled = enabled;
145        self
146    }
147
148    /// 设置是否要求 @提及
149    ///
150    /// **Validates: Requirements 5.1, 5.2**
151    ///
152    /// # 参数
153    ///
154    /// * `require_mention` - 是否要求 @提及
155    ///
156    /// # 示例
157    ///
158    /// ```rust
159    /// use aster::auto_reply::GroupActivation;
160    ///
161    /// let activation = GroupActivation::new("group-123").with_require_mention(true);
162    /// assert!(activation.require_mention);
163    /// ```
164    pub fn with_require_mention(mut self, require_mention: bool) -> Self {
165        self.require_mention = require_mention;
166        self
167    }
168
169    /// 设置群组特定冷却时间
170    ///
171    /// **Validates: Requirement 5.3**
172    ///
173    /// # 参数
174    ///
175    /// * `seconds` - 冷却时间(秒)
176    ///
177    /// # 示例
178    ///
179    /// ```rust
180    /// use aster::auto_reply::GroupActivation;
181    ///
182    /// let activation = GroupActivation::new("group-123").with_cooldown(120);
183    /// assert_eq!(activation.cooldown_seconds, Some(120));
184    /// ```
185    pub fn with_cooldown(mut self, seconds: u64) -> Self {
186        self.cooldown_seconds = Some(seconds);
187        self
188    }
189
190    /// 设置群组特定白名单
191    ///
192    /// **Validates: Requirement 5.4**
193    ///
194    /// # 参数
195    ///
196    /// * `users` - 白名单用户列表
197    ///
198    /// # 示例
199    ///
200    /// ```rust
201    /// use aster::auto_reply::GroupActivation;
202    ///
203    /// let activation = GroupActivation::new("group-123")
204    ///     .with_whitelist(vec!["user1".to_string(), "user2".to_string()]);
205    /// assert!(activation.whitelist.is_some());
206    /// assert_eq!(activation.whitelist.as_ref().unwrap().len(), 2);
207    /// ```
208    pub fn with_whitelist(mut self, users: Vec<String>) -> Self {
209        self.whitelist = Some(users);
210        self
211    }
212
213    /// 检查消息是否应该触发自动回复
214    ///
215    /// 根据群组配置检查消息是否满足触发条件。
216    ///
217    /// **Validates: Requirements 5.1, 5.2, 5.5**
218    ///
219    /// # 参数
220    ///
221    /// * `mentions_bot` - 消息是否 @提及了机器人
222    ///
223    /// # 返回值
224    ///
225    /// 返回 `Ok(())` 如果应该触发,否则返回 `Err(GroupRejectionReason)`。
226    ///
227    /// # 示例
228    ///
229    /// ```rust
230    /// use aster::auto_reply::{GroupActivation, GroupRejectionReason};
231    ///
232    /// // 禁用的群组
233    /// let disabled = GroupActivation::disabled("group-123");
234    /// assert_eq!(disabled.should_trigger(true), Err(GroupRejectionReason::GroupDisabled));
235    ///
236    /// // 要求 @提及的群组
237    /// let require_mention = GroupActivation::new("group-456").with_require_mention(true);
238    /// assert_eq!(require_mention.should_trigger(false), Err(GroupRejectionReason::RequiresMention));
239    /// assert_eq!(require_mention.should_trigger(true), Ok(()));
240    /// ```
241    pub fn should_trigger(&self, mentions_bot: bool) -> Result<(), GroupRejectionReason> {
242        // Requirement 5.5: 检查群组是否启用
243        if !self.enabled {
244            return Err(GroupRejectionReason::GroupDisabled);
245        }
246
247        // Requirements 5.1, 5.2: 检查是否要求 @提及
248        if self.require_mention && !mentions_bot {
249            return Err(GroupRejectionReason::RequiresMention);
250        }
251
252        Ok(())
253    }
254
255    /// 检查用户是否在群组白名单中
256    ///
257    /// **Validates: Requirement 5.4**
258    ///
259    /// # 参数
260    ///
261    /// * `user_id` - 用户 ID
262    ///
263    /// # 返回值
264    ///
265    /// - 如果没有设置群组白名单,返回 `None`(使用全局白名单)
266    /// - 如果设置了群组白名单且用户在其中,返回 `Some(true)`
267    /// - 如果设置了群组白名单但用户不在其中,返回 `Some(false)`
268    ///
269    /// # 示例
270    ///
271    /// ```rust
272    /// use aster::auto_reply::GroupActivation;
273    ///
274    /// // 没有群组白名单
275    /// let no_whitelist = GroupActivation::new("group-123");
276    /// assert_eq!(no_whitelist.is_user_whitelisted("any_user"), None);
277    ///
278    /// // 有群组白名单
279    /// let with_whitelist = GroupActivation::new("group-456")
280    ///     .with_whitelist(vec!["user1".to_string()]);
281    /// assert_eq!(with_whitelist.is_user_whitelisted("user1"), Some(true));
282    /// assert_eq!(with_whitelist.is_user_whitelisted("user2"), Some(false));
283    /// ```
284    pub fn is_user_whitelisted(&self, user_id: &str) -> Option<bool> {
285        self.whitelist
286            .as_ref()
287            .map(|list| list.iter().any(|u| u == user_id))
288    }
289
290    /// 获取有效的冷却时间
291    ///
292    /// **Validates: Requirement 5.3**
293    ///
294    /// # 参数
295    ///
296    /// * `default_cooldown` - 默认冷却时间(秒)
297    ///
298    /// # 返回值
299    ///
300    /// 返回群组特定冷却时间,如果未设置则返回默认值。
301    ///
302    /// # 示例
303    ///
304    /// ```rust
305    /// use aster::auto_reply::GroupActivation;
306    ///
307    /// // 没有群组冷却时间
308    /// let no_cooldown = GroupActivation::new("group-123");
309    /// assert_eq!(no_cooldown.effective_cooldown(60), 60);
310    ///
311    /// // 有群组冷却时间
312    /// let with_cooldown = GroupActivation::new("group-456").with_cooldown(120);
313    /// assert_eq!(with_cooldown.effective_cooldown(60), 120);
314    /// ```
315    pub fn effective_cooldown(&self, default_cooldown: u64) -> u64 {
316        self.cooldown_seconds.unwrap_or(default_cooldown)
317    }
318
319    /// 获取群组 ID
320    pub fn group_id(&self) -> &str {
321        &self.group_id
322    }
323
324    /// 检查群组是否启用
325    ///
326    /// **Validates: Requirement 5.5**
327    pub fn is_enabled(&self) -> bool {
328        self.enabled
329    }
330
331    /// 检查是否要求 @提及
332    ///
333    /// **Validates: Requirement 5.1**
334    pub fn requires_mention(&self) -> bool {
335        self.require_mention
336    }
337
338    /// 检查是否有群组特定冷却时间
339    ///
340    /// **Validates: Requirement 5.3**
341    pub fn has_custom_cooldown(&self) -> bool {
342        self.cooldown_seconds.is_some()
343    }
344
345    /// 检查是否有群组特定白名单
346    ///
347    /// **Validates: Requirement 5.4**
348    pub fn has_custom_whitelist(&self) -> bool {
349        self.whitelist.is_some()
350    }
351}
352
353/// 群组拒绝原因
354///
355/// 表示群组配置检查失败的原因。
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub enum GroupRejectionReason {
358    /// 群组已禁用自动回复
359    /// **Validates: Requirement 5.5**
360    GroupDisabled,
361    /// 群组要求 @提及
362    /// **Validates: Requirements 5.1, 5.2**
363    RequiresMention,
364}
365
366impl std::fmt::Display for GroupRejectionReason {
367    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368        match self {
369            GroupRejectionReason::GroupDisabled => write!(f, "Group has auto-reply disabled"),
370            GroupRejectionReason::RequiresMention => {
371                write!(f, "Group requires @mention to trigger")
372            }
373        }
374    }
375}
376
377impl std::error::Error for GroupRejectionReason {}
378
379/// 群组激活配置管理器
380///
381/// 管理多个群组的激活配置。
382#[derive(Debug, Clone, Default)]
383pub struct GroupActivationManager {
384    /// 群组配置映射
385    activations: std::collections::HashMap<String, GroupActivation>,
386}
387
388impl GroupActivationManager {
389    /// 创建新的群组激活管理器
390    pub fn new() -> Self {
391        Self {
392            activations: std::collections::HashMap::new(),
393        }
394    }
395
396    /// 从配置列表创建管理器
397    pub fn from_activations(activations: Vec<GroupActivation>) -> Self {
398        let mut manager = Self::new();
399        for activation in activations {
400            manager.set(activation);
401        }
402        manager
403    }
404
405    /// 设置群组配置
406    pub fn set(&mut self, activation: GroupActivation) {
407        self.activations
408            .insert(activation.group_id.clone(), activation);
409    }
410
411    /// 获取群组配置
412    pub fn get(&self, group_id: &str) -> Option<&GroupActivation> {
413        self.activations.get(group_id)
414    }
415
416    /// 移除群组配置
417    pub fn remove(&mut self, group_id: &str) -> Option<GroupActivation> {
418        self.activations.remove(group_id)
419    }
420
421    /// 获取所有群组配置
422    pub fn list(&self) -> Vec<&GroupActivation> {
423        self.activations.values().collect()
424    }
425
426    /// 获取群组数量
427    pub fn len(&self) -> usize {
428        self.activations.len()
429    }
430
431    /// 检查是否为空
432    pub fn is_empty(&self) -> bool {
433        self.activations.is_empty()
434    }
435
436    /// 检查群组消息是否应该触发
437    ///
438    /// 如果群组没有配置,默认允许触发。
439    pub fn should_trigger(
440        &self,
441        group_id: &str,
442        mentions_bot: bool,
443    ) -> Result<(), GroupRejectionReason> {
444        match self.get(group_id) {
445            Some(activation) => activation.should_trigger(mentions_bot),
446            None => Ok(()), // 未配置的群组默认允许
447        }
448    }
449
450    /// 获取群组的有效冷却时间
451    ///
452    /// 如果群组没有配置或没有自定义冷却时间,返回默认值。
453    pub fn effective_cooldown(&self, group_id: &str, default_cooldown: u64) -> u64 {
454        self.get(group_id)
455            .map(|a| a.effective_cooldown(default_cooldown))
456            .unwrap_or(default_cooldown)
457    }
458
459    /// 检查用户是否在群组白名单中
460    ///
461    /// 如果群组没有配置或没有自定义白名单,返回 None。
462    pub fn is_user_whitelisted(&self, group_id: &str, user_id: &str) -> Option<bool> {
463        self.get(group_id)
464            .and_then(|a| a.is_user_whitelisted(user_id))
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471
472    // ============================================================================
473    // Unit Tests for GroupActivation
474    // ============================================================================
475
476    /// 测试创建新的群组配置
477    #[test]
478    fn test_new_group_activation() {
479        let activation = GroupActivation::new("group-123");
480
481        assert_eq!(activation.group_id, "group-123");
482        assert!(activation.enabled);
483        assert!(!activation.require_mention);
484        assert!(activation.cooldown_seconds.is_none());
485        assert!(activation.whitelist.is_none());
486    }
487
488    /// 测试创建禁用的群组配置
489    /// **Validates: Requirement 5.5**
490    #[test]
491    fn test_disabled_group_activation() {
492        let activation = GroupActivation::disabled("group-123");
493
494        assert_eq!(activation.group_id, "group-123");
495        assert!(!activation.enabled);
496    }
497
498    /// 测试 builder 模式
499    #[test]
500    fn test_builder_pattern() {
501        let activation = GroupActivation::new("group-123")
502            .with_enabled(true)
503            .with_require_mention(true)
504            .with_cooldown(120)
505            .with_whitelist(vec!["user1".to_string(), "user2".to_string()]);
506
507        assert!(activation.enabled);
508        assert!(activation.require_mention);
509        assert_eq!(activation.cooldown_seconds, Some(120));
510        assert_eq!(activation.whitelist.as_ref().unwrap().len(), 2);
511    }
512
513    /// 测试 should_trigger - 禁用的群组
514    /// **Validates: Requirement 5.5**
515    #[test]
516    fn test_should_trigger_disabled_group() {
517        let activation = GroupActivation::disabled("group-123");
518
519        // 禁用的群组应该拒绝所有触发
520        assert_eq!(
521            activation.should_trigger(true),
522            Err(GroupRejectionReason::GroupDisabled)
523        );
524        assert_eq!(
525            activation.should_trigger(false),
526            Err(GroupRejectionReason::GroupDisabled)
527        );
528    }
529
530    /// 测试 should_trigger - 要求 @提及
531    /// **Validates: Requirements 5.1, 5.2**
532    #[test]
533    fn test_should_trigger_require_mention() {
534        let activation = GroupActivation::new("group-123").with_require_mention(true);
535
536        // 没有 @提及应该被拒绝
537        assert_eq!(
538            activation.should_trigger(false),
539            Err(GroupRejectionReason::RequiresMention)
540        );
541
542        // 有 @提及应该允许
543        assert_eq!(activation.should_trigger(true), Ok(()));
544    }
545
546    /// 测试 should_trigger - 不要求 @提及
547    #[test]
548    fn test_should_trigger_no_require_mention() {
549        let activation = GroupActivation::new("group-123");
550
551        // 不要求 @提及时,两种情况都应该允许
552        assert_eq!(activation.should_trigger(false), Ok(()));
553        assert_eq!(activation.should_trigger(true), Ok(()));
554    }
555
556    /// 测试 is_user_whitelisted - 没有白名单
557    /// **Validates: Requirement 5.4**
558    #[test]
559    fn test_is_user_whitelisted_no_whitelist() {
560        let activation = GroupActivation::new("group-123");
561
562        // 没有白名单时返回 None
563        assert_eq!(activation.is_user_whitelisted("any_user"), None);
564    }
565
566    /// 测试 is_user_whitelisted - 有白名单
567    /// **Validates: Requirement 5.4**
568    #[test]
569    fn test_is_user_whitelisted_with_whitelist() {
570        let activation = GroupActivation::new("group-123")
571            .with_whitelist(vec!["user1".to_string(), "user2".to_string()]);
572
573        // 白名单中的用户
574        assert_eq!(activation.is_user_whitelisted("user1"), Some(true));
575        assert_eq!(activation.is_user_whitelisted("user2"), Some(true));
576
577        // 不在白名单中的用户
578        assert_eq!(activation.is_user_whitelisted("user3"), Some(false));
579    }
580
581    /// 测试 effective_cooldown
582    /// **Validates: Requirement 5.3**
583    #[test]
584    fn test_effective_cooldown() {
585        // 没有自定义冷却时间
586        let no_cooldown = GroupActivation::new("group-123");
587        assert_eq!(no_cooldown.effective_cooldown(60), 60);
588
589        // 有自定义冷却时间
590        let with_cooldown = GroupActivation::new("group-456").with_cooldown(120);
591        assert_eq!(with_cooldown.effective_cooldown(60), 120);
592    }
593
594    /// 测试辅助方法
595    #[test]
596    fn test_helper_methods() {
597        let activation = GroupActivation::new("group-123")
598            .with_require_mention(true)
599            .with_cooldown(120)
600            .with_whitelist(vec!["user1".to_string()]);
601
602        assert_eq!(activation.group_id(), "group-123");
603        assert!(activation.is_enabled());
604        assert!(activation.requires_mention());
605        assert!(activation.has_custom_cooldown());
606        assert!(activation.has_custom_whitelist());
607    }
608
609    /// 测试序列化和反序列化
610    #[test]
611    fn test_serialization_roundtrip() {
612        let activation = GroupActivation::new("group-123")
613            .with_require_mention(true)
614            .with_cooldown(120)
615            .with_whitelist(vec!["user1".to_string()]);
616
617        let json = serde_json::to_string(&activation).unwrap();
618        let parsed: GroupActivation = serde_json::from_str(&json).unwrap();
619
620        assert_eq!(activation, parsed);
621    }
622
623    /// 测试默认值反序列化
624    #[test]
625    fn test_deserialization_defaults() {
626        let json = r#"{"group_id": "group-123"}"#;
627        let activation: GroupActivation = serde_json::from_str(json).unwrap();
628
629        assert_eq!(activation.group_id, "group-123");
630        assert!(activation.enabled); // 默认 true
631        assert!(!activation.require_mention); // 默认 false
632        assert!(activation.cooldown_seconds.is_none());
633        assert!(activation.whitelist.is_none());
634    }
635
636    // ============================================================================
637    // Unit Tests for GroupRejectionReason
638    // ============================================================================
639
640    #[test]
641    fn test_rejection_reason_display() {
642        assert_eq!(
643            GroupRejectionReason::GroupDisabled.to_string(),
644            "Group has auto-reply disabled"
645        );
646        assert_eq!(
647            GroupRejectionReason::RequiresMention.to_string(),
648            "Group requires @mention to trigger"
649        );
650    }
651
652    // ============================================================================
653    // Unit Tests for GroupActivationManager
654    // ============================================================================
655
656    #[test]
657    fn test_manager_new() {
658        let manager = GroupActivationManager::new();
659        assert!(manager.is_empty());
660        assert_eq!(manager.len(), 0);
661    }
662
663    #[test]
664    fn test_manager_from_activations() {
665        let activations = vec![
666            GroupActivation::new("group-1"),
667            GroupActivation::new("group-2"),
668        ];
669        let manager = GroupActivationManager::from_activations(activations);
670
671        assert_eq!(manager.len(), 2);
672        assert!(manager.get("group-1").is_some());
673        assert!(manager.get("group-2").is_some());
674    }
675
676    #[test]
677    fn test_manager_set_and_get() {
678        let mut manager = GroupActivationManager::new();
679
680        manager.set(GroupActivation::new("group-123").with_require_mention(true));
681
682        let activation = manager.get("group-123").unwrap();
683        assert!(activation.require_mention);
684    }
685
686    #[test]
687    fn test_manager_remove() {
688        let mut manager = GroupActivationManager::new();
689        manager.set(GroupActivation::new("group-123"));
690
691        let removed = manager.remove("group-123");
692        assert!(removed.is_some());
693        assert!(manager.get("group-123").is_none());
694    }
695
696    #[test]
697    fn test_manager_should_trigger() {
698        let mut manager = GroupActivationManager::new();
699        manager.set(GroupActivation::disabled("disabled-group"));
700        manager.set(GroupActivation::new("mention-group").with_require_mention(true));
701
702        // 禁用的群组
703        assert_eq!(
704            manager.should_trigger("disabled-group", true),
705            Err(GroupRejectionReason::GroupDisabled)
706        );
707
708        // 要求 @提及的群组
709        assert_eq!(
710            manager.should_trigger("mention-group", false),
711            Err(GroupRejectionReason::RequiresMention)
712        );
713        assert_eq!(manager.should_trigger("mention-group", true), Ok(()));
714
715        // 未配置的群组默认允许
716        assert_eq!(manager.should_trigger("unknown-group", false), Ok(()));
717    }
718
719    #[test]
720    fn test_manager_effective_cooldown() {
721        let mut manager = GroupActivationManager::new();
722        manager.set(GroupActivation::new("group-123").with_cooldown(120));
723
724        // 有自定义冷却时间的群组
725        assert_eq!(manager.effective_cooldown("group-123", 60), 120);
726
727        // 未配置的群组使用默认值
728        assert_eq!(manager.effective_cooldown("unknown-group", 60), 60);
729    }
730
731    #[test]
732    fn test_manager_is_user_whitelisted() {
733        let mut manager = GroupActivationManager::new();
734        manager.set(GroupActivation::new("group-123").with_whitelist(vec!["user1".to_string()]));
735
736        // 有白名单的群组
737        assert_eq!(
738            manager.is_user_whitelisted("group-123", "user1"),
739            Some(true)
740        );
741        assert_eq!(
742            manager.is_user_whitelisted("group-123", "user2"),
743            Some(false)
744        );
745
746        // 未配置的群组
747        assert_eq!(
748            manager.is_user_whitelisted("unknown-group", "any_user"),
749            None
750        );
751    }
752}