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 103 104
//! 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 a utility struct with methods that
//! simplify access to the database, and then 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 use the stateful event loop, 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`]: crate::Bot::stateful_event_loop
//! [`event_loop`]: crate::Bot::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 use interior mutability. 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 needs 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`]: tokio::sync::RwLock
//!
//! Now, if we would have gone with the stateless event loop, we'd write this:
//!
//! ```compile_fail
//! # let mut bot = tbot::Bot::new(String::new()).stateful_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
//! parallel 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);
//! }
//! });
//! ```
//!
//! [`Arc`]: std::sync::Arc
//!
//! `tbot` also provides a few utility state storages for common patterns.
//! You can combine them with other state storages or with your own storage
//! if needed.
pub mod chats;
mod event_loop;
pub mod messages;
mod polling;
pub use chats::Chats;
pub use event_loop::StatefulEventLoop;
pub use messages::Messages;
pub use polling::Polling;