Annotation For src/main.rs
Logged in as anonymous

Lines of src/main.rs from check-in 39ee25f5c3 that are changed by the sequence of edits moving toward check-in f322efafd9:

                         1: use std::collections::BTreeMap;
                         2: 
                         3: use config;
                         4: 
                         5: use tokio;
                         6: 
                         7: use rss;
                         8: 
                         9: use chrono::DateTime;
                        10: 
                        11: use regex::Regex;
                        12: 
                        13: use telegram_bot::*;
                        14: use tokio::stream::StreamExt;
                        15: 
                        16: use sqlx::postgres::PgPoolOptions;
                        17: use sqlx::Row;
                        18: use sqlx::Done; // .rows_affected()
                        19: 
                        20: #[macro_use]
                        21: extern crate lazy_static;
                        22: 
39ee25f5c3 2020-11-29   23: use anyhow::{anyhow, Context, Result};
                        24: 
                        25: #[derive(Clone)]
                        26: struct Core {
                        27: 	owner: i64,
                        28: 	api_key: String,
                        29: 	owner_chat: UserId,
                        30: 	tg: telegram_bot::Api,
                        31: 	my: User,
                        32: 	pool: sqlx::Pool<sqlx::Postgres>,
                        33: }
                        34: 
                        35: impl Core {
                        36: 	async fn new(settings: config::Config) -> Result<Core> {
                        37: 		let owner = settings.get_int("owner")?;
                        38: 		let api_key = settings.get_str("api_key")?;
                        39: 		let tg = Api::new(&api_key);
                        40: 		let core = Core {
                        41: 			owner: owner,
                        42: 			api_key: api_key.clone(),
                        43: 			my: tg.send(telegram_bot::GetMe).await?,
                        44: 			tg: tg,
                        45: 			owner_chat: UserId::new(owner),
                        46: 			pool: PgPoolOptions::new()
                        47: 				.max_connections(5)
                        48: 				.connect_timeout(std::time::Duration::new(300, 0))
                        49: 				.idle_timeout(std::time::Duration::new(60, 0))
                        50: 				.connect_lazy(&settings.get_str("pg")?)?,
                        51: 		};
                        52: 		let clone = core.clone();
                        53: 		tokio::spawn(async move {
                        54: 			if let Err(err) = &clone.autofetch().await {
39ee25f5c3 2020-11-29   55: 				if let Err(err) = clone.debug(&format!("{:?}", err)) {
                        56: 					eprintln!("Autofetch error: {}", err);
                        57: 				};
                        58: 			}
                        59: 		});
                        60: 		Ok(core)
                        61: 	}
                        62: 
                        63: 	fn stream(&self) -> telegram_bot::UpdatesStream {
                        64: 		self.tg.stream()
                        65: 	}
                        66: 
                        67: 	fn debug(&self, msg: &str) -> Result<()> {
                        68: 		self.tg.spawn(SendMessage::new(self.owner_chat, msg));
                        69: 		Ok(())
                        70: 	}
                        71: 
                        72: 	async fn check<S>(&self, id: &i32, owner: S, real: bool) -> Result<()>
                        73: 	where S: Into<i64> {
                        74: 		let owner: i64 = owner.into();
                        75: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29   76: 			.with_context(|| format!("πŸ›‘ Query queue fetch conn:\n{:?}", &self.pool))?;
                        77: 		let row = sqlx::query("select source_id, channel_id, url, iv_hash, owner from rsstg_source where source_id = $1 and owner = $2")
                        78: 			.bind(id)
                        79: 			.bind(owner)
                        80: 			.fetch_one(&mut conn).await
39ee25f5c3 2020-11-29   81: 			.with_context(|| format!("πŸ›‘ Query source:\n{:?}", &self.pool))?;
                        82: 		drop(conn);
                        83: 		let channel_id: i64 = row.try_get("channel_id")?;
                        84: 		let destination = match real {
                        85: 			true => UserId::new(channel_id),
                        86: 			false => UserId::new(row.try_get("owner")?),
                        87: 		};
                        88: 		let url: &str = row.try_get("url")?;
                        89: 		let mut this_fetch: Option<DateTime<chrono::FixedOffset>> = None;
                        90: 		let iv_hash: Option<&str> = row.try_get("iv_hash")?;
                        91: 		let mut posts: BTreeMap<DateTime<chrono::FixedOffset>, String> = BTreeMap::new();
                        92: 		let feed = rss::Channel::from_url(url)
39ee25f5c3 2020-11-29   93: 			.with_context(|| format!("πŸ›‘ Problem opening feed url:\n{}", &url))?;
                        94: 		for item in feed.items() {
                        95: 			let date = match item.pub_date() {
                        96: 				Some(feed_date) => DateTime::parse_from_rfc2822(feed_date),
                        97: 				None => DateTime::parse_from_rfc3339(&item.dublin_core_ext().unwrap().dates()[0]),
                        98: 			}?;
                        99: 			let url = item.link().unwrap().to_string();
                       100: 			posts.insert(date.clone(), url.clone());
                       101: 		};
                       102: 		for (date, url) in posts.iter() {
                       103: 			let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  104: 				.with_context(|| format!("πŸ›‘ Check post fetch conn:\n{:?}", &self.pool))?;
                       105: 			let row = sqlx::query("select exists(select true from rsstg_post where url = $1 and source_id = $2) as exists;")
                       106: 				.bind(&url)
                       107: 				.bind(id)
                       108: 				.fetch_one(&mut conn).await
39ee25f5c3 2020-11-29  109: 				.with_context(|| format!("πŸ›‘ Check post:\n{:?}", &conn))?;
                       110: 			let exists: bool = row.try_get("exists")?;
                       111: 			if ! exists {
                       112: 				if this_fetch == None || *date > this_fetch.unwrap() {
                       113: 					this_fetch = Some(*date);
                       114: 				};
                       115: 				self.tg.send( match iv_hash {
                       116: 						Some(x) => SendMessage::new(destination, format!("<a href=\"https://t.me/iv?url={}&rhash={}\"> </a>{0}", url, x)),
                       117: 						None => SendMessage::new(destination, format!("{}", url)),
                       118: 					}.parse_mode(types::ParseMode::Html)).await
39ee25f5c3 2020-11-29  119: 					.context("πŸ›‘ Can't post message:")?;
                       120: 				sqlx::query("insert into rsstg_post (source_id, posted, url) values ($1, $2, $3);")
                       121: 					.bind(id)
                       122: 					.bind(date)
                       123: 					.bind(url)
                       124: 					.execute(&mut conn).await
39ee25f5c3 2020-11-29  125: 					.with_context(|| format!("πŸ›‘Record post:\n{:?}", &conn))?;
                       126: 				drop(conn);
                       127: 				tokio::time::delay_for(std::time::Duration::new(4, 0)).await;
                       128: 			};
                       129: 		};
                       130: 		posts.clear();
                       131: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  132: 			.with_context(|| format!("πŸ›‘ Update scrape fetch conn:\n{:?}", &self.pool))?;
                       133: 		sqlx::query("update rsstg_source set last_scrape = now() where source_id = $1;")
                       134: 			.bind(id)
                       135: 			.execute(&mut conn).await
39ee25f5c3 2020-11-29  136: 			.with_context(|| format!("πŸ›‘ Update scrape:\n{:?}", &conn))?;
                       137: 		Ok(())
                       138: 	}
                       139: 
39ee25f5c3 2020-11-29  140: 	async fn clean<S>(&self, source_id: &i32, id: S) -> Result<String>
39ee25f5c3 2020-11-29  141: 	where S: Into<i64> {
39ee25f5c3 2020-11-29  142: 		let id: i64 = id.into();
39ee25f5c3 2020-11-29  143: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  144: 			.with_context(|| format!("πŸ›‘ Clean fetch conn:\n{:?}", &self.pool))?;
                       145: 		match sqlx::query("delete from rsstg_post p using rsstg_source s where p.source_id = $1 and owner = $2 and p.source_id = s.source_id;")
                       146: 			.bind(source_id)
39ee25f5c3 2020-11-29  147: 			.bind(id)
                       148: 			.execute(&mut conn).await
39ee25f5c3 2020-11-29  149: 			.with_context(|| format!("πŸ›‘ Clean seen posts:\n{:?}", &self.pool))?
                       150: 			.rows_affected() {
                       151: 			0 => { Ok("No data found found\\.".to_string()) },
                       152: 			x => { Ok(format!("{} posts purged\\.", x)) },
                       153: 		}
                       154: 	}
                       155: 
39ee25f5c3 2020-11-29  156: 	async fn enable<S>(&self, source_id: &i32, id: S) -> Result<&str>
                       157: 	where S: Into<i64> {
39ee25f5c3 2020-11-29  158: 		let id: i64 = id.into();
                       159: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  160: 			.with_context(|| format!("πŸ›‘ Enable fetch conn:\n{:?}", &self.pool))?;
                       161: 		match sqlx::query("update rsstg_source set enabled = true where source_id = $1 and owner = $2")
                       162: 			.bind(source_id)
39ee25f5c3 2020-11-29  163: 			.bind(id)
39ee25f5c3 2020-11-29  164: 			.execute(&mut conn).await
39ee25f5c3 2020-11-29  165: 			.with_context(|| format!("πŸ›‘ Enable source:\n{:?}", &self.pool))?
39ee25f5c3 2020-11-29  166: 			.rows_affected() {
39ee25f5c3 2020-11-29  167: 			1 => { Ok("Source disabled\\.") },
39ee25f5c3 2020-11-29  168: 			0 => { Ok("Source not found\\.") },
39ee25f5c3 2020-11-29  169: 			_ => { Err(anyhow!("Database error.")) },
39ee25f5c3 2020-11-29  170: 		}
39ee25f5c3 2020-11-29  171: 	}
39ee25f5c3 2020-11-29  172: 
39ee25f5c3 2020-11-29  173: 	async fn disable<S>(&self, source_id: &i32, id: S) -> Result<&str>
39ee25f5c3 2020-11-29  174: 	where S: Into<i64> {
39ee25f5c3 2020-11-29  175: 		let id: i64 = id.into();
39ee25f5c3 2020-11-29  176: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  177: 			.with_context(|| format!("πŸ›‘ Disable fetch conn:\n{:?}", &self.pool))?;
39ee25f5c3 2020-11-29  178: 		match sqlx::query("update rsstg_source set enabled = false where source_id = $1 and owner = $2")
39ee25f5c3 2020-11-29  179: 			.bind(source_id)
39ee25f5c3 2020-11-29  180: 			.bind(id)
                       181: 			.execute(&mut conn).await
39ee25f5c3 2020-11-29  182: 			.with_context(|| format!("πŸ›‘ Disable source:\n{:?}", &self.pool))?
                       183: 			.rows_affected() {
                       184: 			1 => { Ok("Source disabled\\.") },
                       185: 			0 => { Ok("Source not found\\.") },
                       186: 			_ => { Err(anyhow!("Database error.")) },
                       187: 		}
                       188: 	}
                       189: 
                       190: 	async fn autofetch(&self) -> Result<()> {
                       191: 		let mut delay = chrono::Duration::minutes(5);
                       192: 		let mut now;
                       193: 		loop {
                       194: 			let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  195: 				.with_context(|| format!("πŸ›‘ Autofetch fetch conn:\n{:?}", &self.pool))?;
                       196: 			now = chrono::Local::now();
39ee25f5c3 2020-11-29  197: 			let mut queue = sqlx::query("select source_id, next_fetch, owner from rsstg_order natural left join rsstg_source natural left join rsstg_channel where next_fetch < now();")
                       198: 				.fetch_all(&mut conn).await?;
                       199: 			for row in queue.iter() {
                       200: 				let source_id: i32 = row.try_get("source_id")?;
                       201: 				let owner: i64 = row.try_get("owner")?;
                       202: 				let next_fetch: DateTime<chrono::Local> = row.try_get("next_fetch")?;
                       203: 				if next_fetch < now {
                       204: 					sqlx::query("update rsstg_source set last_scrape = now() + interval '1 hour' where source_id = $1;")
                       205: 						.bind(source_id)
                       206: 						.execute(&mut conn).await
39ee25f5c3 2020-11-29  207: 						.with_context(|| format!("πŸ›‘ Lock source:\n\n{:?}", &self.pool))?;
                       208: 					let clone = self.clone();
                       209: 					tokio::spawn(async move {
                       210: 						if let Err(err) = clone.check(&source_id, owner, true).await {
39ee25f5c3 2020-11-29  211: 							if let Err(err) = clone.debug(&format!("{:?}", err)) {
                       212: 								eprintln!("Check error: {}", err);
                       213: 							};
                       214: 						};
                       215: 					});
                       216: 				} else {
                       217: 					if next_fetch - now < delay {
                       218: 						delay = next_fetch - now;
                       219: 					}
                       220: 				}
                       221: 			};
                       222: 			queue.clear();
                       223: 			tokio::time::delay_for(delay.to_std()?).await;
                       224: 			delay = chrono::Duration::minutes(5);
                       225: 		}
                       226: 	}
                       227: 
39ee25f5c3 2020-11-29  228: 	async fn list(&self, id: telegram_bot::UserId) -> Result<Vec<String>> {
39ee25f5c3 2020-11-29  229: 		let id = i64::from(id);
                       230: 		let mut reply = vec![];
                       231: 		let mut conn = self.pool.acquire().await
39ee25f5c3 2020-11-29  232: 			.with_context(|| format!("πŸ›‘ List fetch conn:\n{:?}", &self.pool))?;
                       233: 		reply.push("Channels:".to_string());
39ee25f5c3 2020-11-29  234: 		let rows = sqlx::query("select source_id, username, enabled, url, iv_hash from rsstg_source left join rsstg_channel using (channel_id) where owner = $1 order by source_id")
39ee25f5c3 2020-11-29  235: 			.bind(id)
                       236: 			.fetch_all(&mut conn).await?;
                       237: 		for row in rows.iter() {
                       238: 			let source_id: i32 = row.try_get("source_id")?;
39ee25f5c3 2020-11-29  239: 			let username: &str = row.try_get("username")?;
                       240: 			let enabled: bool = row.try_get("enabled")?;
                       241: 			let url: &str = row.try_get("url")?;
                       242: 			let iv_hash: Option<&str> = row.try_get("iv_hash")?;
                       243: 			reply.push(format!("\n\\#️⃣ {} \\*️⃣ `{}` {}\nπŸ”— `{}`", source_id, username,  
                       244: 				match enabled {
                       245: 					true  => "πŸ”„ enabled",
                       246: 					false => "β›” disabled",
                       247: 				}, url));
                       248: 			if let Some(hash) = iv_hash {
                       249: 				reply.push(format!("IV `{}`", hash));
                       250: 			}
                       251: 		};
                       252: 		Ok(reply)
                       253: 	}
                       254: }
                       255: 
                       256: #[tokio::main]
                       257: async fn main() -> Result<()> {
                       258: 	let mut settings = config::Config::default();
                       259: 	settings.merge(config::File::with_name("rsstg"))?;
                       260: 
                       261: 	let core = Core::new(settings).await?;
                       262: 
                       263: 	let mut stream = core.stream();
                       264: 
                       265: 	while let Some(update) = stream.next().await {
                       266: 		if let Err(err) = handle(update?, &core).await {
39ee25f5c3 2020-11-29  267: 			core.debug(&format!("{:?}", err))?;
                       268: 		};
                       269: 	}
                       270: 
                       271: 	Ok(())
                       272: }
                       273: 
                       274: async fn handle(update: telegram_bot::Update, core: &Core) -> Result<()> {
                       275: 	lazy_static! {
                       276: 		static ref RE_USERNAME: Regex = Regex::new(r"^@[a-zA-Z][a-zA-Z0-9_]+$").unwrap();
                       277: 		static ref RE_LINK: Regex = Regex::new(r"^https?://[a-zA-Z.0-9-]+/[-_a-zA-Z.0-9/?=]+$").unwrap();
                       278: 		static ref RE_IV_HASH: Regex = Regex::new(r"^[a-f0-9]{14}$").unwrap();
                       279: 	}
                       280: 
                       281: 	match update.kind {
                       282: 		UpdateKind::Message(message) => {
                       283: 			let mut reply: Vec<String> = vec![];
                       284: 			match message.kind {
                       285: 				MessageKind::Text { ref data, .. } => {
                       286: 					let mut words = data.split_whitespace();
                       287: 					let cmd = words.next().unwrap();
                       288: 					match cmd {
                       289: 
                       290: // start
                       291: 
                       292: 						"/start" => {
                       293: 							reply.push("We are open\\. Probably\\. Visit [channel](https://t.me/rsstg_bot_help/3) for details\\.".to_string());
                       294: 						},
                       295: 
                       296: // list
                       297: 
                       298: 						"/list" => {
                       299: 							reply.append(&mut core.list(message.from.id).await?);
                       300: 						},
                       301: 
                       302: // add
                       303: 
                       304: 						"/add" | "/update" => {
39ee25f5c3 2020-11-29  305: 							let mut source_id: i32 = 0;
39ee25f5c3 2020-11-29  306: 							if cmd == "/update" {
39ee25f5c3 2020-11-29  307: 								source_id = words.next().unwrap().parse::<i32>()?;
39ee25f5c3 2020-11-29  308: 							}
39ee25f5c3 2020-11-29  309: 							let (channel, url, iv_hash) = (words.next().unwrap(), words.next().unwrap(), words.next());
39ee25f5c3 2020-11-29  310: 							let ok_link = RE_LINK.is_match(&url);
39ee25f5c3 2020-11-29  311: 							let ok_hash = match iv_hash {
39ee25f5c3 2020-11-29  312: 								Some(hash) => RE_IV_HASH.is_match(&hash),
39ee25f5c3 2020-11-29  313: 								None => true,
39ee25f5c3 2020-11-29  314: 							};
39ee25f5c3 2020-11-29  315: 							if ! ok_link {
39ee25f5c3 2020-11-29  316: 								reply.push("Link should be link to atom/rss feed, something like \"https://domain/path\"\\.".to_string());
39ee25f5c3 2020-11-29  317: 								core.debug(&format!("Url: {:?}", &url))?;
39ee25f5c3 2020-11-29  318: 							}
39ee25f5c3 2020-11-29  319: 							if ! ok_hash {
39ee25f5c3 2020-11-29  320: 								reply.push("IV hash should be 14 hex digits.".to_string());
39ee25f5c3 2020-11-29  321: 								core.debug(&format!("IV: {:?}", &iv_hash))?;
39ee25f5c3 2020-11-29  322: 							}
39ee25f5c3 2020-11-29  323: 							if ok_link && ok_hash {
39ee25f5c3 2020-11-29  324: 								let chan: Option<i64> = match sqlx::query("select channel_id from rsstg_channel where username = $1")
39ee25f5c3 2020-11-29  325: 									.bind(channel)
39ee25f5c3 2020-11-29  326: 									.fetch_one(&core.pool).await {
39ee25f5c3 2020-11-29  327: 										Ok(chan) => Some(chan.try_get("channel_id")?),
39ee25f5c3 2020-11-29  328: 										Err(sqlx::Error::RowNotFound) => {
39ee25f5c3 2020-11-29  329: 											let chan_id = i64::from(core.tg.send(telegram_bot::GetChat::new(telegram_bot::types::ChatRef::ChannelUsername(channel.to_string()))).await?.id());
39ee25f5c3 2020-11-29  330: 											sqlx::query("insert into rsstg_channel (channel_id, username) values ($1, $2);")
39ee25f5c3 2020-11-29  331: 												.bind(chan_id)
39ee25f5c3 2020-11-29  332: 												.bind(channel)
39ee25f5c3 2020-11-29  333: 												.execute(&core.pool).await?;
39ee25f5c3 2020-11-29  334: 											Some(chan_id)
39ee25f5c3 2020-11-29  335: 										},
39ee25f5c3 2020-11-29  336: 										Err(err) => {
39ee25f5c3 2020-11-29  337: 											reply.push("Sorry, unknown error\\.".to_string());
39ee25f5c3 2020-11-29  338: 											core.debug(&format!("Sorry, unknown error:\n{:#?}\n", err))?;
39ee25f5c3 2020-11-29  339: 											None
39ee25f5c3 2020-11-29  340: 										},
39ee25f5c3 2020-11-29  341: 								};
39ee25f5c3 2020-11-29  342: 								if let Some(chan) = chan {
39ee25f5c3 2020-11-29  343: 									match if cmd == "/update" {
39ee25f5c3 2020-11-29  344: 											sqlx::query("update rsstg_source set channel_id = $2, url = $3, iv_hash = $4, owner = $4 where source_id = $1").bind(source_id)
39ee25f5c3 2020-11-29  345: 										} else {
39ee25f5c3 2020-11-29  346: 											sqlx::query("insert into rsstg_source (channel_id, url, iv_hash, owner) values ($1, $2, $3, $4)")
39ee25f5c3 2020-11-29  347: 										}
39ee25f5c3 2020-11-29  348: 										.bind(chan)
39ee25f5c3 2020-11-29  349: 										.bind(url)
39ee25f5c3 2020-11-29  350: 										.bind(iv_hash)
39ee25f5c3 2020-11-29  351: 										.bind(i64::from(message.from.id))
39ee25f5c3 2020-11-29  352: 										.execute(&core.pool).await {
39ee25f5c3 2020-11-29  353: 										Ok(_) => reply.push("Channel added\\.".to_string()),
39ee25f5c3 2020-11-29  354: 										Err(sqlx::Error::Database(err)) => {
39ee25f5c3 2020-11-29  355: 											match err.downcast::<sqlx::postgres::PgDatabaseError>().routine() {
39ee25f5c3 2020-11-29  356: 												Some("_bt_check_unique", ) => {
39ee25f5c3 2020-11-29  357: 													reply.push("Duplicate key\\.".to_string());
39ee25f5c3 2020-11-29  358: 												},
39ee25f5c3 2020-11-29  359: 												Some(_) => {
39ee25f5c3 2020-11-29  360: 													reply.push("Database error\\.".to_string());
39ee25f5c3 2020-11-29  361: 												},
39ee25f5c3 2020-11-29  362: 												None => {
39ee25f5c3 2020-11-29  363: 													reply.push("No database error extracted\\.".to_string());
39ee25f5c3 2020-11-29  364: 												},
39ee25f5c3 2020-11-29  365: 											};
39ee25f5c3 2020-11-29  366: 										},
39ee25f5c3 2020-11-29  367: 										Err(err) => {
39ee25f5c3 2020-11-29  368: 											reply.push("Sorry, unknown error\\.".to_string());
39ee25f5c3 2020-11-29  369: 											core.debug(&format!("Sorry, unknown error:\n{:#?}\n", err))?;
39ee25f5c3 2020-11-29  370: 										},
39ee25f5c3 2020-11-29  371: 									};
39ee25f5c3 2020-11-29  372: 								};
39ee25f5c3 2020-11-29  373: 							};
39ee25f5c3 2020-11-29  374: 						},
39ee25f5c3 2020-11-29  375: 
39ee25f5c3 2020-11-29  376: // addchan
39ee25f5c3 2020-11-29  377: 
39ee25f5c3 2020-11-29  378: 						"/addchan" => {
39ee25f5c3 2020-11-29  379: 							let channel = words.next().unwrap();
39ee25f5c3 2020-11-29  380: 							if ! RE_USERNAME.is_match(&channel) {
39ee25f5c3 2020-11-29  381: 								reply.push("Usernames should be something like \"@\\[a\\-zA\\-Z]\\[a\\-zA\\-Z0\\-9\\_]+\", aren't they?".to_string());
39ee25f5c3 2020-11-29  382: 							} else {
39ee25f5c3 2020-11-29  383: 								let chan: Option<i64> = match sqlx::query("select channel_id from rsstg_channel where username = $1")
39ee25f5c3 2020-11-29  384: 									.bind(channel)
39ee25f5c3 2020-11-29  385: 									.fetch_one(&core.pool).await {
39ee25f5c3 2020-11-29  386: 										Ok(chan) => Some(chan.try_get("channel_id")?),
39ee25f5c3 2020-11-29  387: 										Err(sqlx::Error::RowNotFound) => None,
39ee25f5c3 2020-11-29  388: 										Err(err) => {
39ee25f5c3 2020-11-29  389: 											reply.push("Sorry, unknown error\\.".to_string());
39ee25f5c3 2020-11-29  390: 											core.debug(&format!("Sorry, unknown error:\n{:#?}", err))?;
39ee25f5c3 2020-11-29  391: 											None
39ee25f5c3 2020-11-29  392: 										},
39ee25f5c3 2020-11-29  393: 								};
39ee25f5c3 2020-11-29  394: 								match chan {
39ee25f5c3 2020-11-29  395: 									Some(chan) => {
39ee25f5c3 2020-11-29  396: 										let new_chan = core.tg.send(telegram_bot::GetChat::new(telegram_bot::types::ChatId::new(chan))).await?;
39ee25f5c3 2020-11-29  397: 										if i64::from(new_chan.id()) == chan {
39ee25f5c3 2020-11-29  398: 											reply.push("I already know that channel\\.".to_string());
39ee25f5c3 2020-11-29  399: 										} else {
39ee25f5c3 2020-11-29  400: 											reply.push("Hmm, channel has changed… I'll fix it later\\.".to_string());
39ee25f5c3 2020-11-29  401: 										};
39ee25f5c3 2020-11-29  402: 									},
39ee25f5c3 2020-11-29  403: 									None => {
39ee25f5c3 2020-11-29  404: 										match core.tg.send(telegram_bot::GetChatAdministrators::new(telegram_bot::types::ChatRef::ChannelUsername(channel.to_string()))).await {
39ee25f5c3 2020-11-29  405: 											Ok(chan_adm) => {
39ee25f5c3 2020-11-29  406: 												let (mut me, mut user) = (false, false);
39ee25f5c3 2020-11-29  407: 												for admin in &chan_adm {
39ee25f5c3 2020-11-29  408: 													if admin.user.id == core.my.id {
39ee25f5c3 2020-11-29  409: 														me = true;
39ee25f5c3 2020-11-29  410: 													};
39ee25f5c3 2020-11-29  411: 													if admin.user.id == message.from.id {
39ee25f5c3 2020-11-29  412: 														user = true;
39ee25f5c3 2020-11-29  413: 													};
39ee25f5c3 2020-11-29  414: 												};
39ee25f5c3 2020-11-29  415: 												if ! me   { reply.push("I need to be admin on that channel\\.".to_string()); };
39ee25f5c3 2020-11-29  416: 												if ! user { reply.push("You should be admin on that channel\\.".to_string()); };
39ee25f5c3 2020-11-29  417: 												if me && user {
39ee25f5c3 2020-11-29  418: 													let chan_id = core.tg.send(telegram_bot::GetChat::new(telegram_bot::types::ChatRef::ChannelUsername(channel.to_string()))).await?;
39ee25f5c3 2020-11-29  419: 													sqlx::query("insert into rsstg_channel (channel_id, username) values ($1, $2);")
39ee25f5c3 2020-11-29  420: 														.bind(i64::from(chan_id.id()))
39ee25f5c3 2020-11-29  421: 														.bind(channel)
39ee25f5c3 2020-11-29  422: 														.execute(&core.pool).await?;
39ee25f5c3 2020-11-29  423: 													reply.push("Good, I know that channel now\\.\n".to_string());
39ee25f5c3 2020-11-29  424: 												};
39ee25f5c3 2020-11-29  425: 											},
39ee25f5c3 2020-11-29  426: 											Err(_) => {
39ee25f5c3 2020-11-29  427: 												reply.push("Sorry, I have no access to that chat\\.".to_string());
39ee25f5c3 2020-11-29  428: 											},
39ee25f5c3 2020-11-29  429: 										};
39ee25f5c3 2020-11-29  430: 									},
39ee25f5c3 2020-11-29  431: 								};
39ee25f5c3 2020-11-29  432: 							};
                       433: 						},
                       434: 
                       435: // check
                       436: 
                       437: 						"/check" => {
                       438: 							match &words.next().unwrap().parse::<i32>() {
                       439: 								Err(err) => {
                       440: 									reply.push(format!("I need a number\\.\n{}", &err));
                       441: 								},
                       442: 								Ok(number) => {
                       443: 									core.check(&number, message.from.id, false).await
39ee25f5c3 2020-11-29  444: 										.context("πŸ›‘ Channel check failed.")?;
                       445: 								},
                       446: 							};
                       447: 						},
                       448: 
                       449: // clean
                       450: 
                       451: 						"/clean" => {
                       452: 							match &words.next().unwrap().parse::<i32>() {
                       453: 								Err(err) => {
                       454: 									reply.push(format!("I need a number\\.\n{}", &err));
                       455: 								},
                       456: 								Ok(number) => {
                       457: 									let result = core.clean(&number, message.from.id).await?;
                       458: 									reply.push(result.to_string());
                       459: 								},
                       460: 							};
                       461: 						},
                       462: 
                       463: // enable
                       464: 
                       465: 						"/enable" => {
                       466: 							match &words.next().unwrap().parse::<i32>() {
                       467: 								Err(err) => {
                       468: 									reply.push(format!("I need a number\\.\n{}", &err));
                       469: 								},
                       470: 								Ok(number) => {
                       471: 									let result = core.enable(&number, message.from.id).await?;
                       472: 									reply.push(result.to_string());
                       473: 								},
                       474: 							};
                       475: 						},
                       476: 
                       477: // disable
                       478: 
                       479: 						"/disable" => {
                       480: 							match &words.next().unwrap().parse::<i32>() {
                       481: 								Err(err) => {
                       482: 									reply.push(format!("I need a number\\.\n{}", &err));
                       483: 								},
                       484: 								Ok(number) => {
                       485: 									let result = core.disable(&number, message.from.id).await?;
                       486: 									reply.push(result.to_string());
                       487: 								},
                       488: 							};
                       489: 						},
                       490: 
                       491: 						_ => {
                       492: 						},
                       493: 					};
                       494: 				},
                       495: 				_ => {
                       496: 				},
                       497: 			};
                       498: 
                       499: 			if reply.len() > 0 {
                       500: 				if let Err(err) = core.tg.send(message.text_reply(reply.join("\n")).parse_mode(types::ParseMode::MarkdownV2)).await {
                       501: 					dbg!(reply.join("\n"));
                       502: 					println!("{}", err);
                       503: 				};
                       504: 			};
                       505: 		},
                       506: 		_ => {},
                       507: 	};
                       508: 
                       509: 	Ok(())
                       510: }