1use crate::__private::{GenericHandler, HandlerError};
2use crate::models::{GetUpdates, Update, UpdateType};
3use crate::{Bot, BotState};
4use std::env::var;
5use std::fs::File;
6use std::io::Read;
7
8#[cfg(feature = "commands")]
9use {crate::__private::command::CommandHandler, crate::models::MessageEntityType, lazy_static::lazy_static, regex::Regex, std::collections::BTreeMap};
10#[cfg(feature = "daemons")]
11use {crate::__private::daemon::DaemonStruct, tokio::sync::Mutex};
12
13use log::{error, info, trace};
14use std::sync::Arc;
15use std::time::Duration;
16
17pub struct BotBuilder<T>
20where
21 T: Sync + Send + 'static,
22{
23 bot: Arc<Bot>,
24 state: Arc<Option<BotState<T>>>,
25
26 #[cfg(feature = "daemons")]
27 daemons: Vec<DaemonStruct<T>>,
28
29 #[cfg(feature = "commands")]
30 commands: Option<BTreeMap<String, Vec<CommandHandler<T>>>>,
31
32 handler: Vec<GenericHandler<T>>,
33
34 update_limit: Option<i64>,
35 allowed_updates: Option<Vec<UpdateType>>,
36 offset: Option<i64>,
37 timeout: Option<i64>,
38 interval: Option<Duration>,
39}
40
41impl BotBuilder<()> {
42 pub fn new() -> BotBuilder<()> {
44 BotBuilder {
45 bot: Arc::new(Bot::login(
46 var("BOT_TOKEN")
47 .ok()
48 .or(var("BOT_TOKEN_FILE").ok().and_then(|f| {
49 let mut s = String::new();
50 File::open(f).expect("Could not open token file").read_to_string(&mut s).expect("Could not read from token file");
51 Some(s)
52 }))
53 .expect("No token provided (define BOT_TOKEN or BOT_TOKEN_FILE variable)"),
54 )),
55 state: Arc::new(None),
56 #[cfg(feature = "commands")]
57 commands: None,
58 #[cfg(feature = "daemons")]
59 daemons: vec![],
60 handler: vec![],
61 update_limit: None,
62 allowed_updates: None,
63 offset: None,
64 timeout: None,
65 interval: None,
66 }
67 }
68
69 pub fn new_with_token(token: String) -> BotBuilder<()> {
71 BotBuilder {
72 bot: Arc::new(Bot::login(token)),
73 state: Arc::new(None),
74 #[cfg(feature = "commands")]
75 commands: None,
76 #[cfg(feature = "daemons")]
77 daemons: vec![],
78 handler: vec![],
79 update_limit: None,
80 allowed_updates: None,
81 offset: None,
82 timeout: None,
83 interval: None,
84 }
85 }
86}
87
88impl Default for BotBuilder<()> {
89 fn default() -> Self {
90 BotBuilder::new()
91 }
92}
93
94impl<T: Sync + Send + 'static> BotBuilder<T> {
95 pub fn with_state<S>(self, state: S) -> BotBuilder<S>
98 where
99 S: Sync + Send + 'static,
100 {
101 if self.state.is_some() {
102 panic!("State already set")
103 }
104 if !self.handler.is_empty() {
105 panic!("Cannot set state after handler")
106 }
107 #[cfg(feature = "daemons")]
108 if !self.daemons.is_empty() {
109 panic!("Cannot set state after daemons")
110 }
111 #[cfg(feature = "commands")]
112 if self.commands.is_some() {
113 panic!("Cannot set state after commands")
114 }
115
116 BotBuilder {
117 bot: self.bot,
118 state: Arc::new(Some(BotState { state })),
119 #[cfg(feature = "commands")]
120 commands: None,
121 #[cfg(feature = "daemons")]
122 daemons: vec![],
123 handler: vec![],
124 update_limit: self.update_limit,
125 allowed_updates: self.allowed_updates,
126 offset: self.offset,
127 timeout: self.timeout,
128 interval: self.interval,
129 }
130 }
131
132 pub fn handlers(mut self, handlers: Vec<GenericHandler<T>>) -> Self {
134 if !self.handler.is_empty() {
135 panic!("Handlers already set")
136 }
137
138 handlers.iter().for_each(|h| info!("Registered handler {} with priority {}", h.name, h.rank));
139
140 self.handler = handlers;
141 self.handler.sort_by_key(|f| f.rank);
142 self
143 }
144
145 pub fn update_limit(mut self, limit: i64) -> BotBuilder<T> {
148 if self.update_limit.is_some() {
149 panic!("Update limit already set")
150 }
151
152 self.update_limit = Some(limit);
153 self
154 }
155
156 pub fn timeout(mut self, timeout: i64) -> BotBuilder<T> {
159 if self.timeout.is_some() {
160 panic!("Timeout already set")
161 }
162 self.timeout = Some(timeout);
165 self
166 }
167
168 pub fn interval(mut self, interval: Duration) -> BotBuilder<T> {
170 if self.interval.is_some() {
171 panic!("Interval already set")
172 }
173
174 self.interval = Some(interval);
175 self
176 }
177
178 #[cfg(feature = "commands")]
179 pub fn commands(mut self, commands: BTreeMap<String, Vec<CommandHandler<T>>>) -> BotBuilder<T> {
181 if self.commands.is_some() {
182 panic!("Commands already set")
183 }
184
185 for cmd in &commands {
186 info!("Registering handler for command {}: ", cmd.0);
187 for handler in cmd.1 {
188 info!(r#" - "{}" with priority {}"#, handler.syntax, handler.rank);
189 }
190 }
191
192 self.commands = Some(commands);
193 self
194 }
195
196 #[cfg(feature = "daemons")]
197 pub fn daemons(mut self, daemons: Vec<DaemonStruct<T>>) -> BotBuilder<T> {
199 if !self.daemons.is_empty() {
200 panic!("Daemons already set")
201 }
202
203 daemons.iter().for_each(|d| info!("Registering daemon {}", d.syntax));
204
205 self.daemons = daemons;
206 self
207 }
208
209 pub async fn launch(mut self) {
211 let allowed_updates = self.allowed_updates.unwrap_or_default();
212 let timeout = self.timeout.or(Some(5));
213
214 info!("Launching bot with parameters: [interval: {:?}, timeout: {:?}]", self.interval, timeout);
215
216 #[cfg(feature = "daemons")]
217 Self::launch_daemons(self.daemons, self.bot.clone(), self.state.clone()).await;
218
219 loop {
220 if let Some(interval) = self.interval {
221 tokio::time::sleep(interval).await;
222 }
223
224 let mut updates = vec![];
226 while updates.is_empty() {
227 updates = self
228 .bot
229 .get_updates(GetUpdates {
230 offset: self.offset,
231 limit: self.update_limit,
232 timeout,
233 allowed_updates: {
234 #[cfg(feature = "commands")]
235 if self.commands.is_some() {
236 [allowed_updates.clone(), vec![UpdateType::NewMessage]].concat()
237 } else {
238 allowed_updates.clone()
239 }
240
241 #[cfg(not(feature = "commands"))]
242 allowed_updates.clone()
243 },
244 })
245 .await
246 .unwrap()
247 }
248
249 updates.sort_by_key(|u| u.update_id);
251
252 self.offset = Some(updates.last().unwrap().update_id + 1);
255
256 for update in updates {
257 if let Ok(update) = Update::try_from(update.clone()) {
259 trace!("Received update {} ({:?})", update.get_id(), update.get_type());
260
261 #[cfg(feature = "commands")]
262 if Self::try_commands(&self.commands, &self.bot, self.state.as_ref().as_ref(), &update).await {
263 continue;
264 }
265
266 for h in &self.handler {
268 if h.updates.is_empty() || h.updates.iter().any(|u| *u == update.get_type()) {
269 match h.handler.handle(&self.bot, &update, self.state.as_ref().as_ref()).await {
270 Ok(_) => break,
271 Err(HandlerError::Parse) => continue,
272 Err(HandlerError::Runtime) => {
273 error!("Handler failure for {:?}", update);
274 break;
275 }
276 }
277 }
278 }
279 } else {
280 error!("Invalid update received: {:?}", update)
281 }
282 }
283 }
284 }
285
286 #[cfg(feature = "commands")]
287 async fn try_commands(commands: &Option<BTreeMap<String, Vec<CommandHandler<T>>>>, bot: &Bot, state: Option<&BotState<T>>, update: &Update) -> bool {
289 if let (Update::NewMessage(_, m), Some(commands)) = (&update, commands) {
290 if let Some(e) = m.entities.iter().find(|e| e.offset == 0 && matches!(e._type, MessageEntityType::BotCommand)) {
291 trace!("Command \"{}\" issued", m.text.as_ref().unwrap_or(&"".to_string()));
292
293 lazy_static! {
294 static ref PARAMETER_DELIMITER: Regex = Regex::new(r"\s+").unwrap();
295 }
296
297 if let Some(handlers) = commands.get(&m.text.as_ref().unwrap().as_str()[(e.offset + 1) as usize..(e.offset + e.length) as usize]) {
298 let args: Vec<String> = PARAMETER_DELIMITER
299 .split(&m.text.as_ref().unwrap().as_str()[(e.offset + e.length) as usize..])
300 .filter(|s| !s.is_empty())
301 .map(str::to_string)
302 .collect();
303
304 for h in handlers {
305 match h.handler.handle(&args, update, bot, state).await {
306 Ok(_) => break,
307 Err(HandlerError::Parse) => continue,
308 Err(HandlerError::Runtime) => panic!(),
309 }
310 }
311
312 trace!("Could not delegate to any handler");
313
314 return true;
315 } else {
316 trace!("No corresponding handler found")
317 }
318 }
319 }
320
321 false
322 }
323
324 #[cfg(feature = "daemons")]
325 async fn launch_daemons(daemons: Vec<DaemonStruct<T>>, bot: Arc<Bot>, state: Arc<Option<BotState<T>>>) -> Vec<Arc<Mutex<()>>> {
327 daemons
328 .into_iter()
329 .map(|d| {
330 let mutex = Arc::new(Mutex::new(()));
331
332 let bot = bot.clone();
333 let state = state.clone();
334 let lock = mutex.clone();
335
336 tokio::task::spawn(async move { d.daemon.launch(bot, state, lock).await });
337
338 mutex
339 })
340 .collect()
341 }
342}