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