1use super::BotCmdResult;
2use super::ErrorKind;
3use super::Module;
4use super::MsgMetadata;
5use super::Result;
6use super::State;
7use super::TriggerHandler;
8use rando::Rando;
9use regex::Regex;
10use std::borrow::Cow;
11use std::ops::DerefMut;
12use std::sync::Arc;
13use std::sync::RwLock;
14use std::sync::RwLockReadGuard;
15use util;
16use uuid::Uuid;
17
18pub struct Trigger {
19 pub name: Cow<'static, str>,
20 pub provider: Arc<Module>,
21 pub regex: Arc<RwLock<Regex>>,
22 pub priority: TriggerPriority,
23 pub(super) handler: Arc<TriggerHandler>,
24 pub help_msg: Cow<'static, str>,
25 pub uuid: Uuid,
26}
27
28pub(super) struct TemporaryTrigger {
29 pub(super) inner: Trigger,
30 pub(super) activation_limit: u16,
31}
32
33#[derive(Debug)]
34pub enum TriggerAttr {
35 AlwaysWatching,
40}
41
42#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
43pub enum TriggerPriority {
44 Minimum,
46
47 Low,
51
52 Medium,
54
55 High,
58
59 Maximum,
61}
62
63impl Trigger {
64 fn read_regex(&self) -> Result<RwLockReadGuard<Regex>> {
65 self.regex.read().map_err(|_| {
66 ErrorKind::LockPoisoned(
67 format!(
68 "the regex for trigger {uuid} ({name:?})",
69 name = self.name,
70 uuid = self.uuid.hyphenated()
71 ).into(),
72 ).into()
73 })
74 }
75}
76
77pub(super) fn run_any_matching(
79 state: &State,
80 text: &str,
81 msg_metadata: &MsgMetadata,
82) -> Result<Option<BotCmdResult>> {
83 let mut trigger = None;
84
85 for (_priority, triggers) in state.triggers.iter().rev() {
86 if triggers.is_empty() {
87 continue;
88 }
89
90 if let Some(t) = triggers
91 .rand_iter()
92 .with_rng(state.rng()?.deref_mut())
93 .filter(|t| t.read_regex().map(|rx| rx.is_match(text)).unwrap_or(false))
94 .next()
95 {
96 trigger = Some(t);
97 break;
98 }
99 }
100
101 let trigger = match trigger {
102 Some(t) => t,
103 None => return Ok(None),
104 };
105
106 let args = trigger.read_regex()?.captures(text).expect(
107 "We shouldn't have reached this point if the \
108 trigger didn't match!",
109 );
110
111 Ok(Some(util::run_handler(
112 "trigger",
113 trigger.name.clone(),
114 || trigger.handler.run(state, msg_metadata, args),
115 )?))
116}