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
|
use crate::{
Arc,
Mutex,
core::FeedList,
};
use std::{
borrow::Cow,
fmt,
};
use serde::{
Deserialize,
Serialize,
};
use stacked_errors::{
bail,
Result,
StackableErr,
};
use tgbot::{
api::Client,
types::{
AnswerCallbackQuery,
Bot,
ChatPeerId,
GetBot,
InlineKeyboardButton,
InlineKeyboardMarkup,
Message,
ParseMode,
SendMessage,
},
};
const CB_VERSION: u8 = 0;
#[derive(Serialize, Deserialize, Debug)]
pub enum Callback {
// Edit one feed (version, name)
Edit(u8, String),
// List all feeds (version, name to show, page number)
List(u8, String, u8),
// Show root menu (version)
Menu(u8),
}
impl Callback {
pub fn edit <S>(text: S) -> Callback
where S: Into<String> {
Callback::Edit(CB_VERSION, text.into())
}
pub fn list <S>(text: S, page: u8) -> Callback
where S: Into<String> {
Callback::List(CB_VERSION, text.into(), page)
}
pub fn menu () -> Callback {
Callback::Menu(CB_VERSION)
}
|
>
>
>
|
>
>
>
>
|
|
|
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
|
use crate::{
Arc,
Mutex,
core::FeedList,
};
use std::{
borrow::Cow,
fmt,
time::Duration,
};
use serde::{
Deserialize,
Serialize,
};
use smol::Timer;
use stacked_errors::{
bail,
Result,
StackableErr,
};
use tgbot::{
api::{
Client,
ExecuteError
},
types::{
AnswerCallbackQuery,
Bot,
ChatPeerId,
EditMessageResult,
EditMessageText,
GetBot,
InlineKeyboardButton,
InlineKeyboardMarkup,
Message,
ParseMode,
SendMessage,
},
};
const CB_VERSION: u8 = 0;
#[derive(Serialize, Deserialize, Debug)]
pub enum Callback {
// Edit one feed (version, name)
Edit(u8, String),
// List all feeds (version, name to show, page number)
List(u8, String, usize),
// Show root menu (version)
Menu(u8),
}
impl Callback {
pub fn edit <S>(text: S) -> Callback
where S: Into<String> {
Callback::Edit(CB_VERSION, text.into())
}
pub fn list <S>(text: S, page: usize) -> Callback
where S: Into<String> {
Callback::List(CB_VERSION, text.into(), page)
}
pub fn menu () -> Callback {
Callback::Menu(CB_VERSION)
}
|
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
|
impl fmt::Display for Callback {
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&toml::to_string(self).map_err(|_| fmt::Error)?)
}
}
/// Produce new Keyboard Markup from current Callback
pub async fn get_kb (cb: &Callback, feeds: Arc<Mutex<FeedList>>) -> Result<InlineKeyboardMarkup> {
if cb.version() != CB_VERSION {
bail!("Wrong callback version.");
}
let mark = match cb {
Callback::Edit(_, _name) => { // XXX edit missing
let kb: Vec<Vec<InlineKeyboardButton>> = vec![];
InlineKeyboardMarkup::from(kb)
},
Callback::List(_, name, page) => {
let mut kb = vec![];
let feeds = feeds.lock_arc().await;
let long = feeds.len() > 6;
let (start, end) = if long {
(page * 5 + 1, 5 + page * 5)
} else {
(0, 6)
};
let mut i = 0;
if name.is_empty() {
for (id, name) in feeds.iter() {
i += 1;
if i < start { continue }
kb.push(vec![
InlineKeyboardButton::for_callback_data(
format!("{}. {}", id, name),
Callback::edit(name).to_string()),
]);
if i > end { break }
}
} else {
let mut found = false;
let mut first_page = None;
for (id, feed_name) in feeds.iter() {
if name == feed_name {
found = true;
}
i += 1;
kb.push(vec![
InlineKeyboardButton::for_callback_data(
format!("{}. {}", id, feed_name),
Callback::list("xxx", *page).to_string()), // XXX edit
]);
if i > end {
// page complete, if found we got the right page, if not - reset and
// continue.
if found {
break
} else {
if first_page.is_none() {
first_page = Some(kb);
|
|
|
>
|
<
<
>
|
|
|
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
|
impl fmt::Display for Callback {
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&toml::to_string(self).map_err(|_| fmt::Error)?)
}
}
/// Produce new Keyboard Markup from current Callback
pub async fn get_kb (cb: &Callback, feeds: &Arc<Mutex<FeedList>>) -> Result<InlineKeyboardMarkup> {
if cb.version() != CB_VERSION {
bail!("Wrong callback version.");
}
let mark = match cb {
Callback::Edit(_, _name) => { // XXX edit missing
let kb: Vec<Vec<InlineKeyboardButton>> = vec![];
InlineKeyboardMarkup::from(kb)
},
Callback::List(_, name, page) => {
let mut kb = vec![];
let feeds = feeds.lock_arc().await;
let long = feeds.len() > 6;
let (start, end) = if long {
(page * 5, 5 + page * 5)
} else {
(0, 6)
};
let mut i = 0;
if name.is_empty() {
let feed_iter = feeds.iter().skip(start);
for (id, name) in feed_iter {
kb.push(vec![
InlineKeyboardButton::for_callback_data(
format!("{}. {}", id, name),
Callback::edit(name).to_string()),
]);
i += 1;
if i == end { break }
}
} else {
let mut found = false;
let mut first_page = None;
for (id, feed_name) in feeds.iter() {
if name == feed_name {
found = true;
}
i += 1;
kb.push(vec![
InlineKeyboardButton::for_callback_data(
format!("{}. {}", id, feed_name),
Callback::list("xxx", *page).to_string()), // XXX edit
]);
if i == end {
// page complete, if found we got the right page, if not - reset and
// continue.
if found {
break
} else {
if first_page.is_none() {
first_page = Some(kb);
|
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
///
/// The function initialises the client, configures the gateway and fetches the bot identity
/// before returning the constructed `Tg`.
pub async fn new (settings: &config::Config) -> Result<Tg> {
let api_key = settings.get_string("api_key").stack()?;
let owner = ChatPeerId::from(settings.get_int("owner").stack()?);
let client = Client::new(&api_key).stack()?
.with_host(settings.get_string("api_gateway").stack()?)
.with_max_retries(0);
let me = client.execute(GetBot).await.stack()?;
Ok(Tg {
me,
owner,
|
>
>
>
|
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
///
/// The function initialises the client, configures the gateway and fetches the bot identity
/// before returning the constructed `Tg`.
pub async fn new (settings: &config::Config) -> Result<Tg> {
let api_key = settings.get_string("api_key").stack()?;
let owner = ChatPeerId::from(settings.get_int("owner").stack()?);
// We don't use retries, as in async environment this will just get us stuck for extra
// amount of time on simple requests. Just bail, show error and ack it in the code. In
// other case we might got stuck with multiple open transactions in database.
let client = Client::new(&api_key).stack()?
.with_host(settings.get_string("api_gateway").stack()?)
.with_max_retries(0);
let me = client.execute(GetBot).await.stack()?;
Ok(Tg {
me,
owner,
|
265
266
267
268
269
270
271
272
|
pub fn with_owner <O>(&self, owner: O) -> Tg
where O: Into<i64> {
Tg {
owner: ChatPeerId::from(owner.into()),
..self.clone()
}
}
}
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
pub fn with_owner <O>(&self, owner: O) -> Tg
where O: Into<i64> {
Tg {
owner: ChatPeerId::from(owner.into()),
..self.clone()
}
}
pub async fn update_message (&self, chat_id: i64, message_id: i64, text: String, feeds: &Arc<Mutex<FeedList>>, cb: Callback) -> Result<EditMessageResult> {
loop {
let req = EditMessageText::for_chat_message(chat_id, message_id, &text)
.with_reply_markup(get_kb(&cb, feeds).await.stack()?);
let res = self.client.execute(req).await;
match res {
Ok(res) => return Ok(res),
Err(ref err) => {
if let ExecuteError::Response(resp) = err
&& let Some(delay) = resp.retry_after()
{
if delay > 60 {
return res.context("Delay too big (>60), not waiting.");
}
Timer::after(Duration::from_secs(delay)).await;
} else {
return res.context("Can't update message");
}
},
};
}
}
}
|