1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
//! The stateful event loop and utilities for it. //! //! The stateful event loop makes it easier to write bots that depend on some //! kind of state. It can be a chatting bot, a quiz bot or any other bot that //! needs to store data. The stateful event loop also can be used to easily //! share some data between all handlers, e.g. if you use a database that //! stores all your data, you can implement an utility struct with methods that //! simplify access to the database and share it across all your handlers. //! //! For example, let's think of a bot with a global counter, and the bot allows //! increasing and showing it. //! //! If we'd have gone with a stateless event loop, we'd start with this: //! //! ``` //! let mut bot = tbot::from_env!("BOT_TOKEN").event_loop(); //! ``` //! //! To go with a stateful bot, we simply call [`stateful_event_loop`] instead of //! [`event_loop`]: //! //! ``` //! # let initial_state = (); //! let mut bot = tbot::from_env!("BOT_TOKEN") //! .stateful_event_loop(initial_state); //! ``` //! //! [`stateful_event_loop`]: ../struct.Bot.html#method.stateful_event_loop //! [`event_loop`]: ../struct.Bot.html#method.event_loop //! //! What should `initial_state` be? It can actually be any value (that can be //! shared across threads). You'd think that, as we only have a counter, //! we would simply use any integer type. However, `tbot` won't let you mutate //! the state — instead, you should make use of interior mutability yourself. //! This design decision was made to avoid mutability if it isn't needed and //! for users to decide what parts of their state should be mutable — that is //! to prevent all the state being locked when only a part of it is actually //! needed to be locked. //! //! So what we need is an integer wrapped in an [`RwLock`]: //! //! ``` //! # use std::sync::RwLock; /* //! use tokio::sync::RwLock; //! # */ //! //! let mut bot = tbot::from_env!("BOT_TOKEN") //! .stateful_event_loop(RwLock::new(0)); //! ``` //! //! [`RwLock`]: https://docs.rs/tokio/0.2.*/tokio/sync/struct.RwLock.html //! //! Now, if we would have gone with the stateless event loop, we'd write this: //! //! ``` //! # let mut bot = tbot::Bot::new(String::new()).event_loop(); //! bot.command("increase", |context| async move { /* .. */ }); //! ``` //! //! Once we opt in to the stateful event loop, we need to write this: //! //! ``` //! # let mut bot = tbot::Bot::new(String::new()).stateful_event_loop(()); //! bot.command("increase", |context, state| async move { /* .. */ }); //! ``` //! //! The state is passed being wrapped in an `Arc`, that is, this handler //! receives `Arc<RwLock<i32>>` as the second argument. This allows concurrent //! access to the state. Now you only need to use the state: //! //! ``` //! # struct RwLock(std::sync::RwLock<i32>); //! # impl RwLock { //! # async fn write(&self) -> std::sync::RwLockWriteGuard<'_, i32> { //! # self.0.write().unwrap() //! # } //! # } //! # let mut bot = tbot::Bot::new(String::new()) //! # .stateful_event_loop(RwLock(std::sync::RwLock::new(0))); //! use tbot::prelude::*; //! //! bot.command("increase", |context, state| async move { //! *state.write().await += 1; //! let call_result = //! context.send_message("Increased the counter").call().await; //! //! if let Err(err) = call_result { //! dbg!(err); //! } //! }); //! ``` //! //! `tbot` also provides a few utility state stores for common patterns. //! You can combine them with other state stores or with your state if needed. pub mod chats; mod event_loop; pub mod messages; pub use chats::Chats; pub use event_loop::StatefulEventLoop; pub use messages::Messages;