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(())
    }
}