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