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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
/// `Router` is the main entry point to the bot. It is used to register handlers
/// for different types of events, and keeps track of the state of the bot,
/// passing it to the right handler.
///
/// Currently, Routers support two types of hanlders:
///
/// - `chat::Handler` for chat events
/// - `query::Handler` for inline query events
///
/// Chat handlers are called for every message that is sent to the bot that is part
/// of a chat session. The router keeps track of the state of each chat session,
/// and passes the relevant state for the current Chat ID to the handler.
///
/// Query handlers are called for every inline query that is sent to the bot.
use std::{cmp::max, collections::HashMap, sync::Arc};
use anyhow::bail;
use crate::{
api::{self, GetUpdatesRequest, SendMessageRequest, SendStickerRequest, Update},
chat::{self, MessageEvent},
handlers::query,
Client, API,
};
// Handler routing:
// - by chat ID
// - by user
// - by message type
// - by message text regex
//
// create filtering functions for each of these, and then compose them together
pub struct Router<S> {
api: Arc<API>,
chat_handlers: Vec<chat::Handler<S>>,
query_handlers: Vec<query::Handler<S>>,
chat_state: HashMap<i64, S>,
user_state: HashMap<i64, S>,
}
impl<S: Clone> Router<S> {
/// Create a new router with the given client.
pub fn new(client: Client) -> Self {
Self {
api: Arc::new(API::new(client)),
chat_handlers: vec![],
query_handlers: vec![],
chat_state: HashMap::new(),
user_state: HashMap::new(),
}
}
/// Add a handler for all messages in a chat. The handler is called with current
/// state of the chat ID.
pub fn add_chat_handler(&mut self, h: impl Into<chat::Handler<S>>) {
self.chat_handlers.push(h.into())
}
/// Add a handler for all queries. The handler is called with current state
/// of the user ID.
pub fn add_query_handler(&mut self, h: impl Into<query::Handler<S>>) {
self.query_handlers.push(h.into())
}
/// Start the router. This will block forever.
pub async fn start(&mut self) {
let mut last_update_id = 0;
loop {
debug!("last_update_id = {}", last_update_id);
let updates = self
.api
.get_updates(
&GetUpdatesRequest::new()
.with_timeout(60)
.with_offset(last_update_id + 1),
)
.await
.unwrap();
for update in updates {
last_update_id = max(last_update_id, update.update_id);
_ = self.handle_chat_update(&update).await;
_ = self.handle_query_update(&update).await;
}
}
}
async fn handle_chat_update(&mut self, update: &Update) -> anyhow::Result<()> {
let message_event;
let chat_id;
if let Some(ref m) = update.message {
debug!("New message: {:#?}", m);
chat_id = m.chat.id;
message_event = MessageEvent::New(m.clone());
} else if let Some(ref m) = update.edited_message {
debug!("Edited message: {:#?}", m);
chat_id = m.chat.id;
message_event = MessageEvent::Edited(m.clone());
} else {
bail!("Update is not a message");
}
for handler in &self.chat_handlers {
// If we don't have a state for this chat, create one by cloning
// the initial state stored in the handler.
let state = self
.chat_state
.entry(chat_id)
.or_insert(handler.state.clone());
let reply = (handler.f)(
chat::Event {
api: Arc::clone(&self.api),
message: message_event.clone(),
},
state.clone(),
)
.await
.unwrap();
match reply {
chat::Action::Next => {}
chat::Action::Done => {
break;
}
chat::Action::ReplyText(text) => {
self.api
.send_message(&SendMessageRequest {
chat_id,
text,
reply_to_message_id: None,
})
.await?;
}
chat::Action::ReplySticker(sticker) => {
self.api
.send_sticker(&SendStickerRequest::new(chat_id, sticker))
.await?;
}
}
}
Ok(())
}
async fn handle_query_update(&mut self, update: &Update) -> anyhow::Result<()> {
let Some(ref query) = update.inline_query else {
bail!("Update is not a query");
};
for handler in &self.query_handlers {
let state = self
.user_state
.entry(query.from.id)
.or_insert(handler.state.clone());
let reply = (handler.f)(
query::Event {
api: Arc::clone(&self.api),
query: query.clone(),
},
state.clone(),
)
.await
.unwrap();
match reply {
query::Action::Next => {}
query::Action::Done => {
break;
}
query::Action::ReplyText(title, text) => {
self.api
.answer_inline_query(
&api::AnswerInlineQuery::new(query.id.clone())
.with_article_text(title, text),
)
.await?;
}
}
}
Ok(())
}
}