1
2
3
4
5
6
7
8
9
10
11
12
13
|
use crate::{
Arc,
command,
Mutex,
sql::Db,
tg_bot::{
MyMessage,
Tg,
},
};
use std::{
borrow::Cow,
|
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
use crate::{
Arc,
command,
Mutex,
sql::Db,
tg_bot::{
Callback,
MyMessage,
Tg,
},
};
use std::{
borrow::Cow,
|
| ︙ | | | ︙ | |
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
|
StackableErr,
anyhow,
bail,
};
use tgbot::{
handler::UpdateHandler,
types::{
ChatPeerId,
Command,
Update,
UpdateType,
UserPeerId,
},
};
use ttl_cache::TtlCache;
lazy_static!{
pub static ref RE_SPECIAL: Regex = Regex::new(r"([\-_*\[\]()~`>#+|{}\.!])").unwrap();
}
/// Escape characters that are special in Telegram MarkdownV2 by prefixing them with a backslash.
///
/// This ensures the returned string can be used as MarkdownV2-formatted Telegram message content
/// without special characters being interpreted as MarkdownV2 markup.
pub fn encode (text: &str) -> Cow<'_, str> {
RE_SPECIAL.replace_all(text, "\\$1")
}
// This one does nothing except making sure only one token exists for each id
pub struct Token {
running: Arc<Mutex<HashSet<i32>>>,
my_id: i32,
}
impl Token {
|
>
<
<
<
<
<
<
<
<
|
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
|
StackableErr,
anyhow,
bail,
};
use tgbot::{
handler::UpdateHandler,
types::{
CallbackQuery,
ChatPeerId,
Command,
Update,
UpdateType,
UserPeerId,
},
};
use ttl_cache::TtlCache;
lazy_static!{
pub static ref RE_SPECIAL: Regex = Regex::new(r"([\-_*\[\]()~`>#+|{}\.!])").unwrap();
}
// This one does nothing except making sure only one token exists for each id
pub struct Token {
running: Arc<Mutex<HashSet<i32>>>,
my_id: i32,
}
impl Token {
|
| ︙ | | | ︙ | |
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
smol::block_on(async {
let mut set = self.running.lock_arc().await;
set.remove(&self.my_id);
})
}
}
type FeedList = HashMap<i32, String>;
type UserCache = TtlCache<i64, Arc<Mutex<FeedList>>>;
#[derive(Clone)]
pub struct Core {
pub tg: Tg,
pub db: Db,
pub feeds: Arc<Mutex<UserCache>>,
|
|
|
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
smol::block_on(async {
let mut set = self.running.lock_arc().await;
set.remove(&self.my_id);
})
}
}
pub type FeedList = HashMap<i32, String>;
type UserCache = TtlCache<i64, Arc<Mutex<FeedList>>>;
#[derive(Clone)]
pub struct Core {
pub tg: Tg,
pub db: Db,
pub feeds: Arc<Mutex<UserCache>>,
|
| ︙ | | | ︙ | |
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
|
Ok(Some(source)) => source.to_string(),
Ok(None) => "Source not found in database?".to_string(),
Err(err) => format!("Failed to fetch source data:\n{err}"),
}
};
smol::spawn(Compat::new(async move {
if let Err(err) = clone.check(source_id, true, Some(last_scrape)).await
&& let Err(err) = clone.tg.send(MyMessage::text(format!("🛑 {source}\n{}", encode(&err.to_string())))).await
{
eprintln!("Check error: {err}");
};
})).detach();
}
} else if next_fetch - now < delay {
delay = next_fetch - now;
|
|
|
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
Ok(Some(source)) => source.to_string(),
Ok(None) => "Source not found in database?".to_string(),
Err(err) => format!("Failed to fetch source data:\n{err}"),
}
};
smol::spawn(Compat::new(async move {
if let Err(err) = clone.check(source_id, true, Some(last_scrape)).await
&& let Err(err) = clone.tg.send(MyMessage::html(format!("🛑 {source}\n<pre>{}</pre>", &err.to_string()))).await
{
eprintln!("Check error: {err}");
};
})).detach();
}
} else if next_fetch - now < delay {
delay = next_fetch - now;
|
| ︙ | | | ︙ | |
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
|
for row in conn.get_list(owner).await.stack()? {
reply.push(row.to_string());
};
Ok(reply.join("\n\n"))
}
/// Returns current cached list of feed for requested user, or loads data from database
pub async fn get_feeds (&self, owner: i64) -> Result<Arc<Mutex<HashMap<i32, String>>>> {
let mut feeds = self.feeds.lock_arc().await;
Ok(match feeds.get(&owner) {
None => {
let mut conn = self.db.begin().await.stack()?;
let feed_list = conn.get_feeds(owner).await.stack()?;
let mut map = HashMap::new();
for feed in feed_list {
|
|
|
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
|
for row in conn.get_list(owner).await.stack()? {
reply.push(row.to_string());
};
Ok(reply.join("\n\n"))
}
/// Returns current cached list of feed for requested user, or loads data from database
pub async fn get_feeds (&self, owner: i64) -> Result<Arc<Mutex<FeedList>>> {
let mut feeds = self.feeds.lock_arc().await;
Ok(match feeds.get(&owner) {
None => {
let mut conn = self.db.begin().await.stack()?;
let feed_list = conn.get_feeds(owner).await.stack()?;
let mut map = HashMap::new();
for feed in feed_list {
|
| ︙ | | | ︙ | |
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
|
}
// in case we failed to found feed we need to remove - just reload everything from database
if !dropped {
self.get_feeds(owner).await.stack()?;
}
Ok(())
}
}
impl UpdateHandler for Core {
/// Dispatches an incoming Telegram update to a matching command handler and reports handler errors to the originating chat.
///
/// This method inspects the update; if it contains a message that can be parsed as a bot command,
/// it executes the corresponding command handler. If the handler returns an error, the error text
/// is sent back to the message's chat using MarkdownV2 formatting. Unknown commands produce an error
/// which is also reported to the chat.
async fn handle (&self, update: Update) {
if let UpdateType::Message(msg) = update.update_type
&& let Ok(cmd) = Command::try_from(*msg)
{
let msg = cmd.get_message();
let words = cmd.get_args();
let command = cmd.get_name();
let res = match command {
"/check" | "/clean" | "/enable" | "/delete" | "/disable" => command::command(self, command, msg, words).await,
"/start" => command::start(self, msg).await,
"/list" => command::list(self, msg).await,
"/test" => command::test(self, msg).await,
"/add" | "/update" => command::update(self, command, msg, words).await,
any => Err(anyhow!("Unknown command: {any}")),
};
if let Err(err) = res
&& let Err(err2) = self.tg.send(MyMessage::text_to(
format!("\\#error\n```\n{err}\n```"),
msg.chat.get_id(),
)).await
{
dbg!(err2);
}
} // TODO: debug log for skipped updates?;
}
}
|
>
>
>
>
>
>
|
>
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
>
|
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
|
}
// in case we failed to found feed we need to remove - just reload everything from database
if !dropped {
self.get_feeds(owner).await.stack()?;
}
Ok(())
}
pub async fn cb (&self, query: &CallbackQuery, cb: &str) -> Result<()> {
let cb: Callback = toml::from_str(cb).stack()?;
todo!();
Ok(())
}
}
impl UpdateHandler for Core {
/// Dispatches an incoming Telegram update to a matching command handler and reports handler errors to the originating chat.
///
/// This method inspects the update; if it contains a message that can be parsed as a bot command,
/// it executes the corresponding command handler. If the handler returns an error, the error text
/// is sent back to the message's chat using MarkdownV2 formatting. Unknown commands produce an error
/// which is also reported to the chat.
async fn handle (&self, update: Update) -> () {
match update.update_type {
UpdateType::Message(msg) => {
if let Ok(cmd) = Command::try_from(*msg) {
let msg = cmd.get_message();
let words = cmd.get_args();
let command = cmd.get_name();
let res = match command {
"/check" | "/clean" | "/enable" | "/delete" | "/disable" => command::command(self, command, msg, words).await,
"/start" => command::start(self, msg).await,
"/list" => command::list(self, msg).await,
"/test" => command::test(self, msg).await,
"/add" | "/update" => command::update(self, command, msg, words).await,
any => Err(anyhow!("Unknown command: {any}")),
};
if let Err(err) = res
&& let Err(err2) = self.tg.send(MyMessage::html_to(
format!("#error<pre>{err}</pre>"),
msg.chat.get_id(),
)).await
{
dbg!(err2);
}
} else {
// not a command
}
},
UpdateType::CallbackQuery(query) => {
if let Some(ref cb) = query.data
&& let Err(err) = self.cb(&query, cb).await
{
if let Err(err) = self.tg.answer_cb(query.id, err.to_string()).await {
println!("{err:?}");
}
}
},
_ => {
println!("Unhandled UpdateKind:\n{update:?}")
},
}
}
}
|