Diff
Logged in as anonymous

Differences From Artifact [1e3d90a7dd]:

To Artifact [c0605e985c]:


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:?}")
			},
		}
	}
}