0000: 75 73 65 20 63 72 61 74 65 3a 3a 7b 0a 09 63 6f use crate::{..co
0010: 6d 6d 61 6e 64 2c 0a 09 73 71 6c 3a 3a 44 62 2c mmand,..sql::Db,
0020: 0a 7d 3b 0a 0a 75 73 65 20 73 74 64 3a 3a 7b 0a .};..use std::{.
0030: 09 62 6f 72 72 6f 77 3a 3a 43 6f 77 2c 0a 09 63 .borrow::Cow,..c
0040: 6f 6c 6c 65 63 74 69 6f 6e 73 3a 3a 7b 0a 09 09 ollections::{...
0050: 42 54 72 65 65 4d 61 70 2c 0a 09 09 48 61 73 68 BTreeMap,...Hash
0060: 53 65 74 2c 0a 09 7d 2c 0a 09 73 79 6e 63 3a 3a Set,..},..sync::
0070: 41 72 63 2c 0a 7d 3b 0a 0a 75 73 65 20 61 73 79 Arc,.};..use asy
0080: 6e 63 5f 63 6f 6d 70 61 74 3a 3a 43 6f 6d 70 61 nc_compat::Compa
0090: 74 3b 0a 75 73 65 20 63 68 72 6f 6e 6f 3a 3a 7b t;.use chrono::{
00a0: 0a 09 44 61 74 65 54 69 6d 65 2c 0a 09 4c 6f 63 ..DateTime,..Loc
00b0: 61 6c 2c 0a 7d 3b 0a 75 73 65 20 6c 61 7a 79 5f al,.};.use lazy_
00c0: 73 74 61 74 69 63 3a 3a 6c 61 7a 79 5f 73 74 61 static::lazy_sta
00d0: 74 69 63 3b 0a 75 73 65 20 72 65 67 65 78 3a 3a tic;.use regex::
00e0: 52 65 67 65 78 3b 0a 75 73 65 20 72 65 71 77 65 Regex;.use reqwe
00f0: 73 74 3a 3a 68 65 61 64 65 72 3a 3a 4c 41 53 54 st::header::LAST
0100: 5f 4d 4f 44 49 46 49 45 44 3b 0a 75 73 65 20 73 _MODIFIED;.use s
0110: 6d 6f 6c 3a 3a 7b 0a 09 54 69 6d 65 72 2c 0a 09 mol::{..Timer,..
0120: 6c 6f 63 6b 3a 3a 4d 75 74 65 78 2c 0a 7d 3b 0a lock::Mutex,.};.
0130: 75 73 65 20 74 67 62 6f 74 3a 3a 7b 0a 09 61 70 use tgbot::{..ap
0140: 69 3a 3a 43 6c 69 65 6e 74 2c 0a 09 68 61 6e 64 i::Client,..hand
0150: 6c 65 72 3a 3a 55 70 64 61 74 65 48 61 6e 64 6c ler::UpdateHandl
0160: 65 72 2c 0a 09 74 79 70 65 73 3a 3a 7b 0a 09 09 er,..types::{...
0170: 42 6f 74 2c 0a 09 09 43 68 61 74 50 65 65 72 49 Bot,...ChatPeerI
0180: 64 2c 0a 09 09 43 6f 6d 6d 61 6e 64 2c 0a 09 09 d,...Command,...
0190: 47 65 74 42 6f 74 2c 0a 09 09 49 6e 70 75 74 54 GetBot,...InputT
01a0: 65 78 74 2c 0a 09 09 4d 65 73 73 61 67 65 2c 0a ext,...Message,.
01b0: 09 09 50 61 72 73 65 4d 6f 64 65 2c 0a 09 09 53 ..ParseMode,...S
01c0: 65 6e 64 4d 65 73 73 61 67 65 2c 0a 09 09 55 70 endMessage,...Up
01d0: 64 61 74 65 2c 0a 09 09 55 70 64 61 74 65 54 79 date,...UpdateTy
01e0: 70 65 2c 0a 09 09 55 73 65 72 50 65 65 72 49 64 pe,...UserPeerId
01f0: 2c 0a 09 7d 2c 0a 7d 3b 0a 75 73 65 20 73 74 61 ,..},.};.use sta
0200: 63 6b 65 64 5f 65 72 72 6f 72 73 3a 3a 7b 0a 09 cked_errors::{..
0210: 52 65 73 75 6c 74 2c 0a 09 53 74 61 63 6b 61 62 Result,..Stackab
0220: 6c 65 45 72 72 2c 0a 09 61 6e 79 68 6f 77 2c 0a leErr,..anyhow,.
0230: 09 62 61 69 6c 2c 0a 7d 3b 0a 0a 6c 61 7a 79 5f .bail,.};..lazy_
0240: 73 74 61 74 69 63 21 7b 0a 09 70 75 62 20 73 74 static!{..pub st
0250: 61 74 69 63 20 72 65 66 20 52 45 5f 53 50 45 43 atic ref RE_SPEC
0260: 49 41 4c 3a 20 52 65 67 65 78 20 3d 20 52 65 67 IAL: Regex = Reg
0270: 65 78 3a 3a 6e 65 77 28 72 22 28 5b 5c 2d 5f 2a ex::new(r"([\-_*
0280: 5c 5b 5c 5d 28 29 7e 60 3e 23 2b 7c 7b 7d 5c 2e \[\]()~`>#+|{}\.
0290: 21 5d 29 22 29 2e 75 6e 77 72 61 70 28 29 3b 0a !])").unwrap();.
02a0: 7d 0a 0a 2f 2f 2f 20 45 73 63 61 70 65 20 63 68 }../// Escape ch
02b0: 61 72 61 63 74 65 72 73 20 74 68 61 74 20 61 72 aracters that ar
02c0: 65 20 73 70 65 63 69 61 6c 20 69 6e 20 54 65 6c e special in Tel
02d0: 65 67 72 61 6d 20 4d 61 72 6b 64 6f 77 6e 56 32 egram MarkdownV2
02e0: 20 62 79 20 70 72 65 66 69 78 69 6e 67 20 74 68 by prefixing th
02f0: 65 6d 20 77 69 74 68 20 61 20 62 61 63 6b 73 6c em with a backsl
0300: 61 73 68 2e 0a 2f 2f 2f 0a 2f 2f 2f 20 54 68 69 ash..///./// Thi
0310: 73 20 65 6e 73 75 72 65 73 20 74 68 65 20 72 65 s ensures the re
0320: 74 75 72 6e 65 64 20 73 74 72 69 6e 67 20 63 61 turned string ca
0330: 6e 20 62 65 20 75 73 65 64 20 61 73 20 4d 61 72 n be used as Mar
0340: 6b 64 6f 77 6e 56 32 2d 66 6f 72 6d 61 74 74 65 kdownV2-formatte
0350: 64 20 54 65 6c 65 67 72 61 6d 20 6d 65 73 73 61 d Telegram messa
0360: 67 65 20 63 6f 6e 74 65 6e 74 0a 2f 2f 2f 20 77 ge content./// w
0370: 69 74 68 6f 75 74 20 73 70 65 63 69 61 6c 20 63 ithout special c
0380: 68 61 72 61 63 74 65 72 73 20 62 65 69 6e 67 20 haracters being
0390: 69 6e 74 65 72 70 72 65 74 65 64 20 61 73 20 4d interpreted as M
03a0: 61 72 6b 64 6f 77 6e 56 32 20 6d 61 72 6b 75 70 arkdownV2 markup
03b0: 2e 0a 70 75 62 20 66 6e 20 65 6e 63 6f 64 65 20 ..pub fn encode
03c0: 28 74 65 78 74 3a 20 26 73 74 72 29 20 2d 3e 20 (text: &str) ->
03d0: 43 6f 77 3c 27 5f 2c 20 73 74 72 3e 20 7b 0a 09 Cow<'_, str> {..
03e0: 52 45 5f 53 50 45 43 49 41 4c 2e 72 65 70 6c 61 RE_SPECIAL.repla
03f0: 63 65 5f 61 6c 6c 28 74 65 78 74 2c 20 22 5c 5c ce_all(text, "\\
0400: 24 31 22 29 0a 7d 0a 0a 2f 2f 20 54 68 69 73 20 $1").}..// This
0410: 6f 6e 65 20 64 6f 65 73 20 6e 6f 74 68 69 6e 67 one does nothing
0420: 20 65 78 63 65 70 74 20 6d 61 6b 69 6e 67 20 73 except making s
0430: 75 72 65 20 6f 6e 6c 79 20 6f 6e 65 20 74 6f 6b ure only one tok
0440: 65 6e 20 65 78 69 73 74 73 20 66 6f 72 20 65 61 en exists for ea
0450: 63 68 20 69 64 0a 70 75 62 20 73 74 72 75 63 74 ch id.pub struct
0460: 20 54 6f 6b 65 6e 20 7b 0a 09 72 75 6e 6e 69 6e Token {..runnin
0470: 67 3a 20 41 72 63 3c 4d 75 74 65 78 3c 48 61 73 g: Arc<Mutex<Has
0480: 68 53 65 74 3c 69 33 32 3e 3e 3e 2c 0a 09 6d 79 hSet<i32>>>,..my
0490: 5f 69 64 3a 20 69 33 32 2c 0a 7d 0a 0a 69 6d 70 _id: i32,.}..imp
04a0: 6c 20 54 6f 6b 65 6e 20 7b 0a 09 2f 2f 2f 20 41 l Token {../// A
04b0: 74 74 65 6d 70 74 73 20 74 6f 20 61 63 71 75 69 ttempts to acqui
04c0: 72 65 20 61 20 70 65 72 2d 69 64 20 74 6f 6b 65 re a per-id toke
04d0: 6e 20 62 79 20 69 6e 73 65 72 74 69 6e 67 20 60 n by inserting `
04e0: 6d 79 5f 69 64 60 20 69 6e 74 6f 20 74 68 65 20 my_id` into the
04f0: 73 68 61 72 65 64 20 60 72 75 6e 6e 69 6e 67 60 shared `running`
0500: 20 73 65 74 2e 0a 09 2f 2f 2f 0a 09 2f 2f 2f 20 set...///..///
0510: 49 66 20 74 68 65 20 69 64 20 77 61 73 20 6e 6f If the id was no
0520: 74 20 61 6c 72 65 61 64 79 20 70 72 65 73 65 6e t already presen
0530: 74 2c 20 74 68 65 20 66 75 6e 63 74 69 6f 6e 20 t, the function
0540: 69 6e 73 65 72 74 73 20 69 74 20 61 6e 64 20 72 inserts it and r
0550: 65 74 75 72 6e 73 20 60 53 6f 6d 65 28 54 6f 6b eturns `Some(Tok
0560: 65 6e 29 60 2e 0a 09 2f 2f 2f 20 57 68 65 6e 20 en)`.../// When
0570: 74 68 65 20 72 65 74 75 72 6e 65 64 20 60 54 6f the returned `To
0580: 6b 65 6e 60 20 69 73 20 64 72 6f 70 70 65 64 2c ken` is dropped,
0590: 20 74 68 65 20 69 64 20 77 69 6c 6c 20 62 65 20 the id will be
05a0: 72 65 6d 6f 76 65 64 20 66 72 6f 6d 20 74 68 65 removed from the
05b0: 20 60 72 75 6e 6e 69 6e 67 60 20 73 65 74 2c 0a `running` set,.
05c0: 09 2f 2f 2f 20 61 6c 6c 6f 77 69 6e 67 20 73 75 ./// allowing su
05d0: 62 73 65 71 75 65 6e 74 20 61 63 71 75 69 73 69 bsequent acquisi
05e0: 74 69 6f 6e 73 20 66 6f 72 20 74 68 65 20 73 61 tions for the sa
05f0: 6d 65 20 69 64 2e 0a 09 2f 2f 2f 0a 09 2f 2f 2f me id...///..///
0600: 20 23 20 50 61 72 61 6d 65 74 65 72 73 0a 09 2f # Parameters../
0610: 2f 2f 0a 09 2f 2f 2f 20 2d 20 60 72 75 6e 6e 69 //../// - `runni
0620: 6e 67 60 3a 20 53 68 61 72 65 64 20 73 65 74 20 ng`: Shared set
0630: 74 72 61 63 6b 69 6e 67 20 61 63 74 69 76 65 20 tracking active
0640: 69 64 73 2e 0a 09 2f 2f 2f 20 2d 20 60 6d 79 5f ids.../// - `my_
0650: 69 64 60 3a 20 49 64 65 6e 74 69 66 69 65 72 20 id`: Identifier
0660: 74 6f 20 61 63 71 75 69 72 65 20 61 20 74 6f 6b to acquire a tok
0670: 65 6e 20 66 6f 72 2e 0a 09 2f 2f 2f 0a 09 2f 2f en for...///..//
0680: 2f 20 23 20 52 65 74 75 72 6e 73 0a 09 2f 2f 2f / # Returns..///
0690: 0a 09 2f 2f 2f 20 60 4f 6b 28 54 6f 6b 65 6e 29 ../// `Ok(Token)
06a0: 60 20 69 66 20 74 68 65 20 69 64 20 77 61 73 20 ` if the id was
06b0: 73 75 63 63 65 73 73 66 75 6c 6c 79 20 61 63 71 successfully acq
06c0: 75 69 72 65 64 2c 20 60 45 72 72 6f 72 60 20 69 uired, `Error` i
06d0: 66 20 61 20 74 6f 6b 65 6e 20 66 6f 72 20 74 68 f a token for th
06e0: 65 20 69 64 20 69 73 20 61 6c 72 65 61 64 79 20 e id is already
06f0: 61 63 74 69 76 65 2e 0a 09 61 73 79 6e 63 20 66 active...async f
0700: 6e 20 6e 65 77 20 28 72 75 6e 6e 69 6e 67 3a 20 n new (running:
0710: 26 41 72 63 3c 4d 75 74 65 78 3c 48 61 73 68 53 &Arc<Mutex<HashS
0720: 65 74 3c 69 33 32 3e 3e 3e 2c 20 6d 79 5f 69 64 et<i32>>>, my_id
0730: 3a 20 69 33 32 29 20 2d 3e 20 52 65 73 75 6c 74 : i32) -> Result
0740: 3c 54 6f 6b 65 6e 3e 20 7b 0a 09 09 6c 65 74 20 <Token> {...let
0750: 72 75 6e 6e 69 6e 67 20 3d 20 72 75 6e 6e 69 6e running = runnin
0760: 67 2e 63 6c 6f 6e 65 28 29 3b 0a 09 09 6c 65 74 g.clone();...let
0770: 20 6d 75 74 20 73 65 74 20 3d 20 72 75 6e 6e 69 mut set = runni
0780: 6e 67 2e 6c 6f 63 6b 5f 61 72 63 28 29 2e 61 77 ng.lock_arc().aw
0790: 61 69 74 3b 0a 09 09 69 66 20 73 65 74 2e 63 6f ait;...if set.co
07a0: 6e 74 61 69 6e 73 28 26 6d 79 5f 69 64 29 20 7b ntains(&my_id) {
07b0: 0a 09 09 09 62 61 69 6c 21 28 22 54 6f 6b 65 6e ....bail!("Token
07c0: 20 61 6c 72 65 61 64 79 20 74 61 6b 65 6e 22 29 already taken")
07d0: 3b 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 ;...} else {....
07e0: 73 65 74 2e 69 6e 73 65 72 74 28 6d 79 5f 69 64 set.insert(my_id
07f0: 29 3b 0a 09 09 09 4f 6b 28 54 6f 6b 65 6e 20 7b );....Ok(Token {
0800: 0a 09 09 09 09 72 75 6e 6e 69 6e 67 2c 0a 09 09 .....running,...
0810: 09 09 6d 79 5f 69 64 2c 0a 09 09 09 7d 29 0a 09 ..my_id,....})..
0820: 09 7d 0a 09 7d 0a 7d 0a 0a 69 6d 70 6c 20 44 72 .}..}.}..impl Dr
0830: 6f 70 20 66 6f 72 20 54 6f 6b 65 6e 20 7b 0a 09 op for Token {..
0840: 2f 2f 2f 20 52 65 6c 65 61 73 65 73 20 74 68 69 /// Releases thi
0850: 73 20 74 6f 6b 65 6e 27 73 20 63 6c 61 69 6d 20 s token's claim
0860: 6f 6e 20 74 68 65 20 73 68 61 72 65 64 20 72 75 on the shared ru
0870: 6e 6e 69 6e 67 2d 73 65 74 20 77 68 65 6e 20 74 nning-set when t
0880: 68 65 20 74 6f 6b 65 6e 20 69 73 20 64 72 6f 70 he token is drop
0890: 70 65 64 2e 0a 09 2f 2f 2f 0a 09 2f 2f 2f 20 54 ped...///../// T
08a0: 68 65 20 74 6f 6b 65 6e 27 73 20 69 64 65 6e 74 he token's ident
08b0: 69 66 69 65 72 20 69 73 20 72 65 6d 6f 76 65 64 ifier is removed
08c0: 20 66 72 6f 6d 20 74 68 65 20 73 68 61 72 65 64 from the shared
08d0: 20 60 72 75 6e 6e 69 6e 67 60 20 73 65 74 20 73 `running` set s
08e0: 6f 20 74 68 61 74 20 66 75 74 75 72 65 0a 09 2f o that future../
08f0: 2f 2f 20 6f 70 65 72 61 74 69 6f 6e 73 20 66 6f // operations fo
0900: 72 20 74 68 65 20 73 61 6d 65 20 69 64 20 6d 61 r the same id ma
0910: 79 20 70 72 6f 63 65 65 64 2e 0a 09 2f 2f 2f 0a y proceed...///.
0920: 09 2f 2f 2f 20 54 4f 44 4f 3a 20 69 73 20 75 73 ./// TODO: is us
0930: 69 6e 67 20 62 6c 6f 63 6b 5f 6f 6e 20 69 6e 73 ing block_on ins
0940: 69 64 65 20 62 6c 6f 63 6b 5f 6f 6e 20 73 61 66 ide block_on saf
0950: 65 3f 20 43 75 72 72 65 6e 74 6c 79 20 74 65 73 e? Currently tes
0960: 74 65 64 20 61 6e 64 20 77 6f 72 6b 69 6e 67 20 ted and working
0970: 66 69 6e 65 2e 0a 09 66 6e 20 64 72 6f 70 20 28 fine...fn drop (
0980: 26 6d 75 74 20 73 65 6c 66 29 20 7b 0a 09 09 73 &mut self) {...s
0990: 6d 6f 6c 3a 3a 62 6c 6f 63 6b 5f 6f 6e 28 61 73 mol::block_on(as
09a0: 79 6e 63 20 7b 0a 09 09 09 6c 65 74 20 6d 75 74 ync {....let mut
09b0: 20 73 65 74 20 3d 20 73 65 6c 66 2e 72 75 6e 6e set = self.runn
09c0: 69 6e 67 2e 6c 6f 63 6b 5f 61 72 63 28 29 2e 61 ing.lock_arc().a
09d0: 77 61 69 74 3b 0a 09 09 09 73 65 74 2e 72 65 6d wait;....set.rem
09e0: 6f 76 65 28 26 73 65 6c 66 2e 6d 79 5f 69 64 29 ove(&self.my_id)
09f0: 3b 0a 09 09 7d 29 0a 09 7d 0a 7d 0a 0a 23 5b 64 ;...})..}.}..#[d
0a00: 65 72 69 76 65 28 43 6c 6f 6e 65 29 5d 0a 70 75 erive(Clone)].pu
0a10: 62 20 73 74 72 75 63 74 20 43 6f 72 65 20 7b 0a b struct Core {.
0a20: 09 6f 77 6e 65 72 5f 63 68 61 74 3a 20 43 68 61 .owner_chat: Cha
0a30: 74 50 65 65 72 49 64 2c 0a 09 2f 2f 20 6d 61 78 tPeerId,..// max
0a40: 5f 64 65 6c 61 79 3a 20 75 31 36 2c 0a 09 70 75 _delay: u16,..pu
0a50: 62 20 74 67 3a 20 43 6c 69 65 6e 74 2c 0a 09 70 b tg: Client,..p
0a60: 75 62 20 6d 65 3a 20 42 6f 74 2c 0a 09 70 75 62 ub me: Bot,..pub
0a70: 20 64 62 3a 20 44 62 2c 0a 09 72 75 6e 6e 69 6e db: Db,..runnin
0a80: 67 3a 20 41 72 63 3c 4d 75 74 65 78 3c 48 61 73 g: Arc<Mutex<Has
0a90: 68 53 65 74 3c 69 33 32 3e 3e 3e 2c 0a 09 68 74 hSet<i32>>>,..ht
0aa0: 74 70 5f 63 6c 69 65 6e 74 3a 20 72 65 71 77 65 tp_client: reqwe
0ab0: 73 74 3a 3a 43 6c 69 65 6e 74 2c 0a 7d 0a 0a 2f st::Client,.}../
0ac0: 2f 20 58 58 58 20 52 69 67 68 74 20 6e 6f 77 20 / XXX Right now
0ad0: 74 68 61 74 20 70 61 72 74 20 69 73 20 75 6e 66 that part is unf
0ae0: 69 6e 69 73 68 65 64 20 61 6e 64 20 49 20 67 75 inished and I gu
0af0: 65 73 73 20 49 20 6e 65 65 64 20 74 6f 20 66 69 ess I need to fi
0b00: 6e 69 73 68 20 6d 65 6e 75 20 66 69 72 73 74 0a nish menu first.
0b10: 23 5b 61 6c 6c 6f 77 28 75 6e 75 73 65 64 29 5d #[allow(unused)]
0b20: 0a 70 75 62 20 73 74 72 75 63 74 20 50 6f 73 74 .pub struct Post
0b30: 20 7b 0a 09 75 72 69 3a 20 53 74 72 69 6e 67 2c {..uri: String,
0b40: 0a 09 74 69 74 6c 65 3a 20 53 74 72 69 6e 67 2c ..title: String,
0b50: 0a 09 61 75 74 68 6f 72 73 3a 20 53 74 72 69 6e ..authors: Strin
0b60: 67 2c 0a 09 73 75 6d 6d 61 72 79 3a 20 53 74 72 g,..summary: Str
0b70: 69 6e 67 2c 0a 7d 0a 0a 69 6d 70 6c 20 43 6f 72 ing,.}..impl Cor
0b80: 65 20 7b 0a 09 2f 2f 2f 20 43 72 65 61 74 65 20 e {../// Create
0b90: 61 20 43 6f 72 65 20 69 6e 73 74 61 6e 63 65 20 a Core instance
0ba0: 66 72 6f 6d 20 63 6f 6e 66 69 67 75 72 61 74 69 from configurati
0bb0: 6f 6e 20 61 6e 64 20 73 74 61 72 74 20 69 74 73 on and start its
0bc0: 20 62 61 63 6b 67 72 6f 75 6e 64 20 61 75 74 6f background auto
0bd0: 66 65 74 63 68 20 6c 6f 6f 70 2e 0a 09 2f 2f 2f fetch loop...///
0be0: 0a 09 2f 2f 2f 20 54 68 65 20 70 72 6f 76 69 64 ../// The provid
0bf0: 65 64 20 60 73 65 74 74 69 6e 67 73 60 20 6d 75 ed `settings` mu
0c00: 73 74 20 69 6e 63 6c 75 64 65 3a 0a 09 2f 2f 2f st include:..///
0c10: 20 2d 20 60 6f 77 6e 65 72 60 20 28 69 6e 74 65 - `owner` (inte
0c20: 67 65 72 29 3a 20 63 68 61 74 20 69 64 20 74 6f ger): chat id to
0c30: 20 75 73 65 20 61 73 20 74 68 65 20 64 65 66 61 use as the defa
0c40: 75 6c 74 20 64 65 73 74 69 6e 61 74 69 6f 6e 2c ult destination,
0c50: 0a 09 2f 2f 2f 20 2d 20 60 61 70 69 5f 6b 65 79 ../// - `api_key
0c60: 60 20 28 73 74 72 69 6e 67 29 3a 20 54 65 6c 65 ` (string): Tele
0c70: 67 72 61 6d 20 62 6f 74 20 41 50 49 20 6b 65 79 gram bot API key
0c80: 2c 0a 09 2f 2f 2f 20 2d 20 60 61 70 69 5f 67 61 ,../// - `api_ga
0c90: 74 65 77 61 79 60 20 28 73 74 72 69 6e 67 29 3a teway` (string):
0ca0: 20 54 65 6c 65 67 72 61 6d 20 41 50 49 20 67 61 Telegram API ga
0cb0: 74 65 77 61 79 20 68 6f 73 74 2c 0a 09 2f 2f 2f teway host,..///
0cc0: 20 2d 20 60 70 67 60 20 28 73 74 72 69 6e 67 29 - `pg` (string)
0cd0: 3a 20 50 6f 73 74 67 72 65 53 51 4c 20 63 6f 6e : PostgreSQL con
0ce0: 6e 65 63 74 69 6f 6e 20 73 74 72 69 6e 67 2c 0a nection string,.
0cf0: 09 2f 2f 2f 20 2d 20 6f 70 74 69 6f 6e 61 6c 20 ./// - optional
0d00: 60 70 72 6f 78 79 60 20 28 73 74 72 69 6e 67 29 `proxy` (string)
0d10: 3a 20 70 72 6f 78 79 20 55 52 4c 20 66 6f 72 20 : proxy URL for
0d20: 74 68 65 20 48 54 54 50 20 63 6c 69 65 6e 74 2e the HTTP client.
0d30: 0a 09 2f 2f 2f 0a 09 2f 2f 2f 20 4f 6e 20 73 75 ..///../// On su
0d40: 63 63 65 73 73 20 72 65 74 75 72 6e 73 20 61 6e ccess returns an
0d50: 20 69 6e 69 74 69 61 6c 69 7a 65 64 20 60 43 6f initialized `Co
0d60: 72 65 60 20 77 69 74 68 20 54 65 6c 65 67 72 61 re` with Telegra
0d70: 6d 20 61 6e 64 20 48 54 54 50 20 63 6c 69 65 6e m and HTTP clien
0d80: 74 73 2c 20 64 61 74 61 62 61 73 65 20 63 6f 6e ts, database con
0d90: 6e 65 63 74 69 6f 6e 2c 0a 09 2f 2f 2f 20 61 6e nection,../// an
0da0: 20 65 6d 70 74 79 20 72 75 6e 6e 69 6e 67 20 73 empty running s
0db0: 65 74 20 66 6f 72 20 70 65 72 2d 69 64 20 74 6f et for per-id to
0dc0: 6b 65 6e 73 2c 20 61 6e 64 20 61 20 73 70 61 77 kens, and a spaw
0dd0: 6e 65 64 20 62 61 63 6b 67 72 6f 75 6e 64 20 74 ned background t
0de0: 61 73 6b 20 74 68 61 74 20 70 65 72 69 6f 64 69 ask that periodi
0df0: 63 61 6c 6c 79 20 72 75 6e 73 0a 09 2f 2f 2f 20 cally runs..///
0e00: 60 61 75 74 6f 66 65 74 63 68 60 2e 20 49 66 20 `autofetch`. If
0e10: 61 6e 79 20 72 65 71 75 69 72 65 64 20 73 65 74 any required set
0e20: 74 69 6e 67 20 69 73 20 6d 69 73 73 69 6e 67 20 ting is missing
0e30: 6f 72 20 69 6e 69 74 69 61 6c 69 7a 61 74 69 6f or initializatio
0e40: 6e 20 66 61 69 6c 73 2c 20 61 6e 20 65 72 72 6f n fails, an erro
0e50: 72 20 69 73 20 72 65 74 75 72 6e 65 64 2e 0a 09 r is returned...
0e60: 70 75 62 20 61 73 79 6e 63 20 66 6e 20 6e 65 77 pub async fn new
0e70: 28 73 65 74 74 69 6e 67 73 3a 20 63 6f 6e 66 69 (settings: confi
0e80: 67 3a 3a 43 6f 6e 66 69 67 29 20 2d 3e 20 52 65 g::Config) -> Re
0e90: 73 75 6c 74 3c 43 6f 72 65 3e 20 7b 0a 09 09 6c sult<Core> {...l
0ea0: 65 74 20 6f 77 6e 65 72 5f 63 68 61 74 20 3d 20 et owner_chat =
0eb0: 43 68 61 74 50 65 65 72 49 64 3a 3a 66 72 6f 6d ChatPeerId::from
0ec0: 28 73 65 74 74 69 6e 67 73 2e 67 65 74 5f 69 6e (settings.get_in
0ed0: 74 28 22 6f 77 6e 65 72 22 29 2e 73 74 61 63 6b t("owner").stack
0ee0: 28 29 3f 29 3b 0a 09 09 6c 65 74 20 61 70 69 5f ()?);...let api_
0ef0: 6b 65 79 20 3d 20 73 65 74 74 69 6e 67 73 2e 67 key = settings.g
0f00: 65 74 5f 73 74 72 69 6e 67 28 22 61 70 69 5f 6b et_string("api_k
0f10: 65 79 22 29 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 ey").stack()?;..
0f20: 09 6c 65 74 20 74 67 20 3d 20 43 6c 69 65 6e 74 .let tg = Client
0f30: 3a 3a 6e 65 77 28 26 61 70 69 5f 6b 65 79 29 2e ::new(&api_key).
0f40: 73 74 61 63 6b 28 29 3f 0a 09 09 09 2e 77 69 74 stack()?.....wit
0f50: 68 5f 68 6f 73 74 28 73 65 74 74 69 6e 67 73 2e h_host(settings.
0f60: 67 65 74 5f 73 74 72 69 6e 67 28 22 61 70 69 5f get_string("api_
0f70: 67 61 74 65 77 61 79 22 29 2e 73 74 61 63 6b 28 gateway").stack(
0f80: 29 3f 29 3b 0a 0a 09 09 6c 65 74 20 6d 75 74 20 )?);....let mut
0f90: 63 6c 69 65 6e 74 20 3d 20 72 65 71 77 65 73 74 client = reqwest
0fa0: 3a 3a 43 6c 69 65 6e 74 3a 3a 62 75 69 6c 64 65 ::Client::builde
0fb0: 72 28 29 3b 0a 09 09 69 66 20 6c 65 74 20 4f 6b r();...if let Ok
0fc0: 28 70 72 6f 78 79 29 20 3d 20 73 65 74 74 69 6e (proxy) = settin
0fd0: 67 73 2e 67 65 74 5f 73 74 72 69 6e 67 28 22 70 gs.get_string("p
0fe0: 72 6f 78 79 22 29 20 7b 0a 09 09 09 6c 65 74 20 roxy") {....let
0ff0: 70 72 6f 78 79 20 3d 20 72 65 71 77 65 73 74 3a proxy = reqwest:
1000: 3a 50 72 6f 78 79 3a 3a 61 6c 6c 28 70 72 6f 78 :Proxy::all(prox
1010: 79 29 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 09 09 y).stack()?;....
1020: 63 6c 69 65 6e 74 20 3d 20 63 6c 69 65 6e 74 2e client = client.
1030: 70 72 6f 78 79 28 70 72 6f 78 79 29 3b 0a 09 09 proxy(proxy);...
1040: 7d 0a 09 09 6c 65 74 20 68 74 74 70 5f 63 6c 69 }...let http_cli
1050: 65 6e 74 20 3d 20 63 6c 69 65 6e 74 2e 62 75 69 ent = client.bui
1060: 6c 64 28 29 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 ld().stack()?;..
1070: 09 6c 65 74 20 6d 65 20 3d 20 74 67 2e 65 78 65 .let me = tg.exe
1080: 63 75 74 65 28 47 65 74 42 6f 74 29 2e 61 77 61 cute(GetBot).awa
1090: 69 74 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 09 6c it.stack()?;...l
10a0: 65 74 20 63 6f 72 65 20 3d 20 43 6f 72 65 20 7b et core = Core {
10b0: 0a 09 09 09 74 67 2c 0a 09 09 09 6d 65 2c 0a 09 ....tg,....me,..
10c0: 09 09 6f 77 6e 65 72 5f 63 68 61 74 2c 0a 09 09 ..owner_chat,...
10d0: 09 64 62 3a 20 44 62 3a 3a 6e 65 77 28 26 73 65 .db: Db::new(&se
10e0: 74 74 69 6e 67 73 2e 67 65 74 5f 73 74 72 69 6e ttings.get_strin
10f0: 67 28 22 70 67 22 29 2e 73 74 61 63 6b 28 29 3f g("pg").stack()?
1100: 29 3f 2c 0a 09 09 09 72 75 6e 6e 69 6e 67 3a 20 )?,....running:
1110: 41 72 63 3a 3a 6e 65 77 28 4d 75 74 65 78 3a 3a Arc::new(Mutex::
1120: 6e 65 77 28 48 61 73 68 53 65 74 3a 3a 6e 65 77 new(HashSet::new
1130: 28 29 29 29 2c 0a 09 09 09 68 74 74 70 5f 63 6c ())),....http_cl
1140: 69 65 6e 74 2c 0a 09 09 09 2f 2f 20 6d 61 78 5f ient,....// max_
1150: 64 65 6c 61 79 3a 20 36 30 2c 0a 09 09 7d 3b 0a delay: 60,...};.
1160: 09 09 6c 65 74 20 63 6c 6f 6e 65 20 3d 20 63 6f ..let clone = co
1170: 72 65 2e 63 6c 6f 6e 65 28 29 3b 0a 09 09 73 6d re.clone();...sm
1180: 6f 6c 3a 3a 73 70 61 77 6e 28 43 6f 6d 70 61 74 ol::spawn(Compat
1190: 3a 3a 6e 65 77 28 61 73 79 6e 63 20 6d 6f 76 65 ::new(async move
11a0: 20 7b 0a 09 09 09 6c 6f 6f 70 20 7b 0a 09 09 09 {....loop {....
11b0: 09 6c 65 74 20 64 65 6c 61 79 20 3d 20 6d 61 74 .let delay = mat
11c0: 63 68 20 26 63 6c 6f 6e 65 2e 61 75 74 6f 66 65 ch &clone.autofe
11d0: 74 63 68 28 29 2e 61 77 61 69 74 20 7b 0a 09 09 tch().await {...
11e0: 09 09 09 45 72 72 28 65 72 72 29 20 3d 3e 20 7b ...Err(err) => {
11f0: 0a 09 09 09 09 09 09 69 66 20 6c 65 74 20 45 72 .......if let Er
1200: 72 28 65 72 72 29 20 3d 20 63 6c 6f 6e 65 2e 73 r(err) = clone.s
1210: 65 6e 64 28 66 6f 72 6d 61 74 21 28 22 f0 9f 9b end(format!("ðŸ›
1220: 91 20 7b 65 72 72 7d 22 29 2c 20 4e 6f 6e 65 2c ‘ {err}"), None,
1230: 20 4e 6f 6e 65 29 2e 61 77 61 69 74 20 7b 0a 09 None).await {..
1240: 09 09 09 09 09 09 65 70 72 69 6e 74 6c 6e 21 28 ......eprintln!(
1250: 22 41 75 74 6f 66 65 74 63 68 20 65 72 72 6f 72 "Autofetch error
1260: 3a 20 7b 65 72 72 3a 3f 7d 22 29 3b 0a 09 09 09 : {err:?}");....
1270: 09 09 09 7d 3b 0a 09 09 09 09 09 09 73 74 64 3a ...};.......std:
1280: 3a 74 69 6d 65 3a 3a 44 75 72 61 74 69 6f 6e 3a :time::Duration:
1290: 3a 66 72 6f 6d 5f 73 65 63 73 28 36 30 29 0a 09 :from_secs(60)..
12a0: 09 09 09 09 7d 2c 0a 09 09 09 09 09 4f 6b 28 74 ....},......Ok(t
12b0: 69 6d 65 29 20 3d 3e 20 2a 74 69 6d 65 2c 0a 09 ime) => *time,..
12c0: 09 09 09 7d 3b 0a 09 09 09 09 54 69 6d 65 72 3a ...};.....Timer:
12d0: 3a 61 66 74 65 72 28 64 65 6c 61 79 29 2e 61 77 :after(delay).aw
12e0: 61 69 74 3b 0a 09 09 09 7d 0a 09 09 7d 29 29 2e ait;....}...})).
12f0: 64 65 74 61 63 68 28 29 3b 0a 09 09 4f 6b 28 63 detach();...Ok(c
1300: 6f 72 65 29 0a 09 7d 0a 0a 09 70 75 62 20 61 73 ore)..}...pub as
1310: 79 6e 63 20 66 6e 20 73 65 6e 64 20 3c 53 3e 28 ync fn send <S>(
1320: 26 73 65 6c 66 2c 20 6d 73 67 3a 20 53 2c 20 74 &self, msg: S, t
1330: 61 72 67 65 74 3a 20 4f 70 74 69 6f 6e 3c 43 68 arget: Option<Ch
1340: 61 74 50 65 65 72 49 64 3e 2c 20 6d 6f 64 65 3a atPeerId>, mode:
1350: 20 4f 70 74 69 6f 6e 3c 50 61 72 73 65 4d 6f 64 Option<ParseMod
1360: 65 3e 29 20 2d 3e 20 52 65 73 75 6c 74 3c 4d 65 e>) -> Result<Me
1370: 73 73 61 67 65 3e 0a 09 77 68 65 72 65 20 53 3a ssage>..where S:
1380: 20 49 6e 74 6f 3c 53 74 72 69 6e 67 3e 20 7b 0a Into<String> {.
1390: 09 09 6c 65 74 20 6d 73 67 20 3d 20 6d 73 67 2e ..let msg = msg.
13a0: 69 6e 74 6f 28 29 3b 0a 0a 09 09 6c 65 74 20 6d into();....let m
13b0: 6f 64 65 20 3d 20 6d 6f 64 65 2e 75 6e 77 72 61 ode = mode.unwra
13c0: 70 5f 6f 72 28 50 61 72 73 65 4d 6f 64 65 3a 3a p_or(ParseMode::
13d0: 48 74 6d 6c 29 3b 0a 09 09 6c 65 74 20 74 61 72 Html);...let tar
13e0: 67 65 74 20 3d 20 74 61 72 67 65 74 2e 75 6e 77 get = target.unw
13f0: 72 61 70 5f 6f 72 28 73 65 6c 66 2e 6f 77 6e 65 rap_or(self.owne
1400: 72 5f 63 68 61 74 29 3b 0a 09 09 73 65 6c 66 2e r_chat);...self.
1410: 74 67 2e 65 78 65 63 75 74 65 28 0a 09 09 09 53 tg.execute(....S
1420: 65 6e 64 4d 65 73 73 61 67 65 3a 3a 6e 65 77 28 endMessage::new(
1430: 74 61 72 67 65 74 2c 20 49 6e 70 75 74 54 65 78 target, InputTex
1440: 74 3a 3a 66 72 6f 6d 28 6d 73 67 29 0a 09 09 09 t::from(msg)....
1450: 09 2e 77 69 74 68 5f 66 6f 72 6d 61 74 28 6d 6f ..with_format(mo
1460: 64 65 29 29 0a 09 09 29 2e 61 77 61 69 74 2e 73 de))...).await.s
1470: 74 61 63 6b 28 29 0a 09 7d 0a 0a 09 2f 2f 2f 20 tack()..}...///
1480: 46 65 74 63 68 65 73 20 74 68 65 20 66 65 65 64 Fetches the feed
1490: 20 66 6f 72 20 61 20 73 6f 75 72 63 65 2c 20 73 for a source, s
14a0: 65 6e 64 73 20 61 6e 79 20 6e 65 77 6c 79 20 64 ends any newly d
14b0: 69 73 63 6f 76 65 72 65 64 20 70 6f 73 74 73 20 iscovered posts
14c0: 74 6f 20 74 68 65 20 61 70 70 72 6f 70 72 69 61 to the appropria
14d0: 74 65 20 63 68 61 74 2c 20 61 6e 64 20 72 65 63 te chat, and rec
14e0: 6f 72 64 73 20 74 68 65 6d 20 69 6e 20 74 68 65 ords them in the
14f0: 20 64 61 74 61 62 61 73 65 2e 0a 09 2f 2f 2f 0a database...///.
1500: 09 2f 2f 2f 20 54 68 69 73 20 61 63 71 75 69 72 ./// This acquir
1510: 65 73 20 61 20 70 65 72 2d 73 6f 75 72 63 65 20 es a per-source
1520: 67 75 61 72 64 20 74 6f 20 70 72 65 76 65 6e 74 guard to prevent
1530: 20 63 6f 6e 63 75 72 72 65 6e 74 20 63 68 65 63 concurrent chec
1540: 6b 73 20 66 6f 72 20 74 68 65 20 73 61 6d 65 20 ks for the same
1550: 60 69 64 60 2e 20 49 66 20 61 20 63 68 65 63 6b `id`. If a check
1560: 20 69 73 20 61 6c 72 65 61 64 79 20 72 75 6e 6e is already runn
1570: 69 6e 67 20 66 6f 72 0a 09 2f 2f 2f 20 74 68 65 ing for../// the
1580: 20 67 69 76 65 6e 20 60 69 64 60 2c 20 74 68 65 given `id`, the
1590: 20 66 75 6e 63 74 69 6f 6e 20 72 65 74 75 72 6e function return
15a0: 73 20 61 6e 20 65 72 72 6f 72 2e 20 49 66 20 60 s an error. If `
15b0: 6c 61 73 74 5f 73 63 72 61 70 65 60 20 69 73 20 last_scrape` is
15c0: 70 72 6f 76 69 64 65 64 2c 20 69 74 20 69 73 20 provided, it is
15d0: 73 65 6e 74 20 61 73 20 74 68 65 20 60 49 66 2d sent as the `If-
15e0: 4d 6f 64 69 66 69 65 64 2d 53 69 6e 63 65 60 0a Modified-Since`.
15f0: 09 2f 2f 2f 20 68 65 61 64 65 72 20 74 6f 20 74 ./// header to t
1600: 68 65 20 66 65 65 64 20 72 65 71 75 65 73 74 2e he feed request.
1610: 20 54 68 65 20 66 75 6e 63 74 69 6f 6e 20 70 61 The function pa
1620: 72 73 65 73 20 52 53 53 20 6f 72 20 41 74 6f 6d rses RSS or Atom
1630: 20 66 65 65 64 73 2c 20 73 65 6e 64 73 20 75 6e feeds, sends un
1640: 73 65 65 6e 20 70 6f 73 74 20 55 52 4c 73 20 74 seen post URLs t
1650: 6f 20 65 69 74 68 65 72 20 74 68 65 20 73 6f 75 o either the sou
1660: 72 63 65 27 73 0a 09 2f 2f 2f 20 63 68 61 6e 6e rce's../// chann
1670: 65 6c 20 28 77 68 65 6e 20 60 72 65 61 6c 60 20 el (when `real`
1680: 69 73 20 74 72 75 65 29 20 6f 72 20 74 68 65 20 is true) or the
1690: 73 6f 75 72 63 65 20 6f 77 6e 65 72 20 28 77 68 source owner (wh
16a0: 65 6e 20 60 72 65 61 6c 60 20 69 73 20 66 61 6c en `real` is fal
16b0: 73 65 29 2c 20 61 6e 64 20 70 65 72 73 69 73 74 se), and persist
16c0: 73 20 70 6f 73 74 65 64 20 65 6e 74 72 69 65 73 s posted entries
16d0: 20 73 6f 20 74 68 65 79 20 61 72 65 0a 09 2f 2f so they are..//
16e0: 2f 20 6e 6f 74 20 72 65 70 6f 73 74 65 64 20 6c / not reposted l
16f0: 61 74 65 72 2e 0a 09 2f 2f 2f 0a 09 2f 2f 2f 20 ater...///..///
1700: 50 61 72 61 6d 65 74 65 72 73 3a 0a 09 2f 2f 2f Parameters:..///
1710: 20 2d 20 60 69 64 60 3a 20 49 64 65 6e 74 69 66 - `id`: Identif
1720: 69 65 72 20 6f 66 20 74 68 65 20 73 6f 75 72 63 ier of the sourc
1730: 65 20 74 6f 20 63 68 65 63 6b 2e 0a 09 2f 2f 2f e to check...///
1740: 20 2d 20 60 72 65 61 6c 60 3a 20 57 68 65 6e 20 - `real`: When
1750: 60 74 72 75 65 60 2c 20 73 65 6e 64 20 70 6f 73 `true`, send pos
1760: 74 73 20 74 6f 20 74 68 65 20 73 6f 75 72 63 65 ts to the source
1770: 27 73 20 63 68 61 6e 6e 65 6c 3b 20 77 68 65 6e 's channel; when
1780: 20 60 66 61 6c 73 65 60 2c 20 73 65 6e 64 20 74 `false`, send t
1790: 6f 20 74 68 65 20 73 6f 75 72 63 65 20 6f 77 6e o the source own
17a0: 65 72 2e 0a 09 2f 2f 2f 20 2d 20 60 6c 61 73 74 er.../// - `last
17b0: 5f 73 63 72 61 70 65 60 3a 20 4f 70 74 69 6f 6e _scrape`: Option
17c0: 61 6c 20 74 69 6d 65 73 74 61 6d 70 20 75 73 65 al timestamp use
17d0: 64 20 74 6f 20 73 65 74 20 74 68 65 20 60 49 66 d to set the `If
17e0: 2d 4d 6f 64 69 66 69 65 64 2d 53 69 6e 63 65 60 -Modified-Since`
17f0: 20 68 65 61 64 65 72 20 66 6f 72 20 74 68 65 20 header for the
1800: 48 54 54 50 20 72 65 71 75 65 73 74 2e 0a 09 2f HTTP request.../
1810: 2f 2f 0a 09 2f 2f 2f 20 23 20 52 65 74 75 72 6e //../// # Return
1820: 73 0a 09 2f 2f 2f 0a 09 2f 2f 2f 20 60 50 6f 73 s..///../// `Pos
1830: 74 65 64 3a 20 4e 60 20 77 68 65 72 65 20 60 4e ted: N` where `N
1840: 60 20 69 73 20 74 68 65 20 6e 75 6d 62 65 72 20 ` is the number
1850: 6f 66 20 70 6f 73 74 73 20 70 72 6f 63 65 73 73 of posts process
1860: 65 64 20 61 6e 64 20 73 65 6e 74 2e 0a 09 70 75 ed and sent...pu
1870: 62 20 61 73 79 6e 63 20 66 6e 20 63 68 65 63 6b b async fn check
1880: 20 28 26 73 65 6c 66 2c 20 69 64 3a 20 69 33 32 (&self, id: i32
1890: 2c 20 72 65 61 6c 3a 20 62 6f 6f 6c 2c 20 6c 61 , real: bool, la
18a0: 73 74 5f 73 63 72 61 70 65 3a 20 4f 70 74 69 6f st_scrape: Optio
18b0: 6e 3c 44 61 74 65 54 69 6d 65 3c 4c 6f 63 61 6c n<DateTime<Local
18c0: 3e 3e 29 20 2d 3e 20 52 65 73 75 6c 74 3c 53 74 >>) -> Result<St
18d0: 72 69 6e 67 3e 20 7b 0a 09 09 6c 65 74 20 6d 75 ring> {...let mu
18e0: 74 20 70 6f 73 74 65 64 3a 20 69 33 32 20 3d 20 t posted: i32 =
18f0: 30 3b 0a 09 09 6c 65 74 20 6d 75 74 20 63 6f 6e 0;...let mut con
1900: 6e 20 3d 20 73 65 6c 66 2e 64 62 2e 62 65 67 69 n = self.db.begi
1910: 6e 28 29 2e 61 77 61 69 74 2e 73 74 61 63 6b 28 n().await.stack(
1920: 29 3f 3b 0a 0a 09 09 6c 65 74 20 5f 74 6f 6b 65 )?;....let _toke
1930: 6e 20 3d 20 54 6f 6b 65 6e 3a 3a 6e 65 77 28 26 n = Token::new(&
1940: 73 65 6c 66 2e 72 75 6e 6e 69 6e 67 2c 20 69 64 self.running, id
1950: 29 2e 61 77 61 69 74 2e 73 74 61 63 6b 28 29 3f ).await.stack()?
1960: 3b 0a 09 09 6c 65 74 20 73 6f 75 72 63 65 20 3d ;...let source =
1970: 20 63 6f 6e 6e 2e 67 65 74 5f 73 6f 75 72 63 65 conn.get_source
1980: 28 69 64 2c 20 73 65 6c 66 2e 6f 77 6e 65 72 5f (id, self.owner_
1990: 63 68 61 74 29 2e 61 77 61 69 74 2e 73 74 61 63 chat).await.stac
19a0: 6b 28 29 3f 3b 0a 09 09 63 6f 6e 6e 2e 73 65 74 k()?;...conn.set
19b0: 5f 73 63 72 61 70 65 28 69 64 29 2e 61 77 61 69 _scrape(id).awai
19c0: 74 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 09 6c 65 t.stack()?;...le
19d0: 74 20 64 65 73 74 69 6e 61 74 69 6f 6e 20 3d 20 t destination =
19e0: 43 68 61 74 50 65 65 72 49 64 3a 3a 66 72 6f 6d ChatPeerId::from
19f0: 28 6d 61 74 63 68 20 72 65 61 6c 20 7b 0a 09 09 (match real {...
1a00: 09 74 72 75 65 20 3d 3e 20 73 6f 75 72 63 65 2e .true => source.
1a10: 63 68 61 6e 6e 65 6c 5f 69 64 2c 0a 09 09 09 66 channel_id,....f
1a20: 61 6c 73 65 20 3d 3e 20 73 6f 75 72 63 65 2e 6f alse => source.o
1a30: 77 6e 65 72 2c 0a 09 09 7d 29 3b 0a 09 09 6c 65 wner,...});...le
1a40: 74 20 6d 75 74 20 74 68 69 73 5f 66 65 74 63 68 t mut this_fetch
1a50: 3a 20 4f 70 74 69 6f 6e 3c 44 61 74 65 54 69 6d : Option<DateTim
1a60: 65 3c 63 68 72 6f 6e 6f 3a 3a 46 69 78 65 64 4f e<chrono::FixedO
1a70: 66 66 73 65 74 3e 3e 20 3d 20 4e 6f 6e 65 3b 0a ffset>> = None;.
1a80: 09 09 6c 65 74 20 6d 75 74 20 70 6f 73 74 73 3a ..let mut posts:
1a90: 20 42 54 72 65 65 4d 61 70 3c 44 61 74 65 54 69 BTreeMap<DateTi
1aa0: 6d 65 3c 63 68 72 6f 6e 6f 3a 3a 46 69 78 65 64 me<chrono::Fixed
1ab0: 4f 66 66 73 65 74 3e 2c 20 50 6f 73 74 3e 20 3d Offset>, Post> =
1ac0: 20 42 54 72 65 65 4d 61 70 3a 3a 6e 65 77 28 29 BTreeMap::new()
1ad0: 3b 0a 0a 09 09 6c 65 74 20 6d 75 74 20 62 75 69 ;....let mut bui
1ae0: 6c 64 65 72 20 3d 20 73 65 6c 66 2e 68 74 74 70 lder = self.http
1af0: 5f 63 6c 69 65 6e 74 2e 67 65 74 28 26 73 6f 75 _client.get(&sou
1b00: 72 63 65 2e 75 72 6c 29 3b 0a 09 09 69 66 20 6c rce.url);...if l
1b10: 65 74 20 53 6f 6d 65 28 6c 61 73 74 5f 73 63 72 et Some(last_scr
1b20: 61 70 65 29 20 3d 20 6c 61 73 74 5f 73 63 72 61 ape) = last_scra
1b30: 70 65 20 7b 0a 09 09 09 62 75 69 6c 64 65 72 20 pe {....builder
1b40: 3d 20 62 75 69 6c 64 65 72 2e 68 65 61 64 65 72 = builder.header
1b50: 28 4c 41 53 54 5f 4d 4f 44 49 46 49 45 44 2c 20 (LAST_MODIFIED,
1b60: 6c 61 73 74 5f 73 63 72 61 70 65 2e 74 6f 5f 72 last_scrape.to_r
1b70: 66 63 32 38 32 32 28 29 29 3b 0a 09 09 7d 3b 0a fc2822());...};.
1b80: 09 09 6c 65 74 20 72 65 73 70 6f 6e 73 65 20 3d ..let response =
1b90: 20 62 75 69 6c 64 65 72 2e 73 65 6e 64 28 29 2e builder.send().
1ba0: 61 77 61 69 74 2e 73 74 61 63 6b 28 29 3f 3b 0a await.stack()?;.
1bb0: 09 09 23 5b 63 66 67 28 64 65 62 75 67 5f 61 73 ..#[cfg(debug_as
1bc0: 73 65 72 74 69 6f 6e 73 29 5d 0a 09 09 7b 0a 09 sertions)]...{..
1bd0: 09 09 6c 65 74 20 68 65 61 64 65 72 73 20 3d 20 ..let headers =
1be0: 72 65 73 70 6f 6e 73 65 2e 68 65 61 64 65 72 73 response.headers
1bf0: 28 29 3b 0a 09 09 09 6c 65 74 20 65 78 70 69 72 ();....let expir
1c00: 65 73 20 3d 20 68 65 61 64 65 72 73 2e 67 65 74 es = headers.get
1c10: 28 45 58 50 49 52 45 53 29 3b 0a 09 09 09 6c 65 (EXPIRES);....le
1c20: 74 20 63 61 63 68 65 20 3d 20 68 65 61 64 65 72 t cache = header
1c30: 73 2e 67 65 74 28 43 41 43 48 45 5f 43 4f 4e 54 s.get(CACHE_CONT
1c40: 52 4f 4c 29 3b 0a 09 09 09 69 66 20 65 78 70 69 ROL);....if expi
1c50: 72 65 73 2e 69 73 5f 73 6f 6d 65 28 29 20 7c 7c res.is_some() ||
1c60: 20 63 61 63 68 65 2e 69 73 5f 73 6f 6d 65 28 29 cache.is_some()
1c70: 20 7b 0a 09 09 09 09 70 72 69 6e 74 6c 6e 21 28 {.....println!(
1c80: 22 7b 7d 20 7b 7d 20 7b 3a 3f 7d 20 7b 3a 3f 7d "{} {} {:?} {:?}
1c90: 20 7b 3a 3f 7d 22 2c 20 4c 6f 63 61 6c 3a 3a 6e {:?}", Local::n
1ca0: 6f 77 28 29 2e 74 6f 5f 72 66 63 32 38 32 32 28 ow().to_rfc2822(
1cb0: 29 2c 20 73 6f 75 72 63 65 2e 75 72 6c 2c 20 6c ), source.url, l
1cc0: 61 73 74 5f 73 63 72 61 70 65 2c 20 65 78 70 69 ast_scrape, expi
1cd0: 72 65 73 2c 20 63 61 63 68 65 29 3b 0a 09 09 09 res, cache);....
1ce0: 7d 0a 09 09 7d 0a 09 09 6c 65 74 20 73 74 61 74 }...}...let stat
1cf0: 75 73 20 3d 20 72 65 73 70 6f 6e 73 65 2e 73 74 us = response.st
1d00: 61 74 75 73 28 29 3b 0a 09 09 6c 65 74 20 63 6f atus();...let co
1d10: 6e 74 65 6e 74 20 3d 20 72 65 73 70 6f 6e 73 65 ntent = response
1d20: 2e 62 79 74 65 73 28 29 2e 61 77 61 69 74 2e 73 .bytes().await.s
1d30: 74 61 63 6b 28 29 3f 3b 0a 09 09 6d 61 74 63 68 tack()?;...match
1d40: 20 72 73 73 3a 3a 43 68 61 6e 6e 65 6c 3a 3a 72 rss::Channel::r
1d50: 65 61 64 5f 66 72 6f 6d 28 26 63 6f 6e 74 65 6e ead_from(&conten
1d60: 74 5b 2e 2e 5d 29 20 7b 0a 09 09 09 4f 6b 28 66 t[..]) {....Ok(f
1d70: 65 65 64 29 20 3d 3e 20 7b 0a 09 09 09 09 66 6f eed) => {.....fo
1d80: 72 20 69 74 65 6d 20 69 6e 20 66 65 65 64 2e 69 r item in feed.i
1d90: 74 65 6d 73 28 29 20 7b 0a 09 09 09 09 09 69 66 tems() {......if
1da0: 20 6c 65 74 20 53 6f 6d 65 28 6c 69 6e 6b 29 20 let Some(link)
1db0: 3d 20 69 74 65 6d 2e 6c 69 6e 6b 28 29 20 7b 0a = item.link() {.
1dc0: 09 09 09 09 09 09 6c 65 74 20 64 61 74 65 20 3d ......let date =
1dd0: 20 6d 61 74 63 68 20 69 74 65 6d 2e 70 75 62 5f match item.pub_
1de0: 64 61 74 65 28 29 20 7b 0a 09 09 09 09 09 09 09 date() {........
1df0: 53 6f 6d 65 28 66 65 65 64 5f 64 61 74 65 29 20 Some(feed_date)
1e00: 3d 3e 20 44 61 74 65 54 69 6d 65 3a 3a 70 61 72 => DateTime::par
1e10: 73 65 5f 66 72 6f 6d 5f 72 66 63 32 38 32 32 28 se_from_rfc2822(
1e20: 66 65 65 64 5f 64 61 74 65 29 2c 0a 09 09 09 09 feed_date),.....
1e30: 09 09 09 4e 6f 6e 65 20 3d 3e 20 44 61 74 65 54 ...None => DateT
1e40: 69 6d 65 3a 3a 70 61 72 73 65 5f 66 72 6f 6d 5f ime::parse_from_
1e50: 72 66 63 33 33 33 39 28 6d 61 74 63 68 20 69 74 rfc3339(match it
1e60: 65 6d 2e 64 75 62 6c 69 6e 5f 63 6f 72 65 5f 65 em.dublin_core_e
1e70: 78 74 28 29 20 7b 0a 09 09 09 09 09 09 09 09 53 xt() {.........S
1e80: 6f 6d 65 28 65 78 74 29 20 3d 3e 20 7b 0a 09 09 ome(ext) => {...
1e90: 09 09 09 09 09 09 09 6c 65 74 20 64 61 74 65 73 .......let dates
1ea0: 20 3d 20 65 78 74 2e 64 61 74 65 73 28 29 3b 0a = ext.dates();.
1eb0: 09 09 09 09 09 09 09 09 09 69 66 20 64 61 74 65 .........if date
1ec0: 73 2e 69 73 5f 65 6d 70 74 79 28 29 20 7b 0a 09 s.is_empty() {..
1ed0: 09 09 09 09 09 09 09 09 09 62 61 69 6c 21 28 22 .........bail!("
1ee0: 46 65 65 64 20 69 74 65 6d 20 68 61 73 20 44 75 Feed item has Du
1ef0: 62 6c 69 6e 20 43 6f 72 65 20 65 78 74 65 6e 73 blin Core extens
1f00: 69 6f 6e 20 62 75 74 20 6e 6f 20 64 61 74 65 73 ion but no dates
1f10: 2e 22 29 0a 09 09 09 09 09 09 09 09 09 7d 20 65 .")..........} e
1f20: 6c 73 65 20 7b 0a 09 09 09 09 09 09 09 09 09 09 lse {...........
1f30: 26 64 61 74 65 73 5b 30 5d 0a 09 09 09 09 09 09 &dates[0].......
1f40: 09 09 09 7d 0a 09 09 09 09 09 09 09 09 7d 2c 0a ...}.........},.
1f50: 09 09 09 09 09 09 09 09 4e 6f 6e 65 20 3d 3e 20 ........None =>
1f60: 62 61 69 6c 21 28 22 46 65 65 64 20 69 74 65 6d bail!("Feed item
1f70: 20 6d 69 73 73 65 73 20 70 6f 73 74 69 6e 67 20 misses posting
1f80: 64 61 74 65 2e 22 29 2c 0a 09 09 09 09 09 09 09 date."),........
1f90: 7d 29 2c 0a 09 09 09 09 09 09 7d 2e 73 74 61 63 }),.......}.stac
1fa0: 6b 28 29 3f 3b 0a 09 09 09 09 09 09 6c 65 74 20 k()?;.......let
1fb0: 75 72 69 20 3d 20 6c 69 6e 6b 2e 74 6f 5f 73 74 uri = link.to_st
1fc0: 72 69 6e 67 28 29 3b 0a 09 09 09 09 09 09 6c 65 ring();.......le
1fd0: 74 20 74 69 74 6c 65 20 3d 20 69 74 65 6d 2e 74 t title = item.t
1fe0: 69 74 6c 65 28 29 2e 75 6e 77 72 61 70 5f 6f 72 itle().unwrap_or
1ff0: 28 22 22 29 2e 74 6f 5f 73 74 72 69 6e 67 28 29 ("").to_string()
2000: 3b 0a 09 09 09 09 09 09 6c 65 74 20 61 75 74 68 ;.......let auth
2010: 6f 72 73 20 3d 20 69 74 65 6d 2e 61 75 74 68 6f ors = item.autho
2020: 72 28 29 2e 75 6e 77 72 61 70 5f 6f 72 28 22 22 r().unwrap_or(""
2030: 29 2e 74 6f 5f 73 74 72 69 6e 67 28 29 3b 0a 09 ).to_string();..
2040: 09 09 09 09 09 6c 65 74 20 73 75 6d 6d 61 72 79 .....let summary
2050: 20 3d 20 69 74 65 6d 2e 63 6f 6e 74 65 6e 74 28 = item.content(
2060: 29 2e 75 6e 77 72 61 70 5f 6f 72 28 22 22 29 2e ).unwrap_or("").
2070: 74 6f 5f 73 74 72 69 6e 67 28 29 3b 0a 09 09 09 to_string();....
2080: 09 09 09 70 6f 73 74 73 2e 69 6e 73 65 72 74 28 ...posts.insert(
2090: 64 61 74 65 2c 20 50 6f 73 74 7b 0a 09 09 09 09 date, Post{.....
20a0: 09 09 09 75 72 69 2c 0a 09 09 09 09 09 09 09 74 ...uri,........t
20b0: 69 74 6c 65 2c 0a 09 09 09 09 09 09 09 61 75 74 itle,........aut
20c0: 68 6f 72 73 2c 0a 09 09 09 09 09 09 09 73 75 6d hors,........sum
20d0: 6d 61 72 79 2c 0a 09 09 09 09 09 09 7d 29 3b 0a mary,.......});.
20e0: 09 09 09 09 09 7d 0a 09 09 09 09 7d 3b 0a 09 09 .....}.....};...
20f0: 09 7d 2c 0a 09 09 09 45 72 72 28 65 72 72 29 20 .},....Err(err)
2100: 3d 3e 20 6d 61 74 63 68 20 65 72 72 20 7b 0a 09 => match err {..
2110: 09 09 09 72 73 73 3a 3a 45 72 72 6f 72 3a 3a 49 ...rss::Error::I
2120: 6e 76 61 6c 69 64 53 74 61 72 74 54 61 67 20 3d nvalidStartTag =
2130: 3e 20 7b 0a 09 09 09 09 09 6d 61 74 63 68 20 61 > {......match a
2140: 74 6f 6d 5f 73 79 6e 64 69 63 61 74 69 6f 6e 3a tom_syndication:
2150: 3a 46 65 65 64 3a 3a 72 65 61 64 5f 66 72 6f 6d :Feed::read_from
2160: 28 26 63 6f 6e 74 65 6e 74 5b 2e 2e 5d 29 20 7b (&content[..]) {
2170: 0a 09 09 09 09 09 09 4f 6b 28 66 65 65 64 29 20 .......Ok(feed)
2180: 3d 3e 20 7b 0a 09 09 09 09 09 09 09 66 6f 72 20 => {........for
2190: 69 74 65 6d 20 69 6e 20 66 65 65 64 2e 65 6e 74 item in feed.ent
21a0: 72 69 65 73 28 29 20 7b 0a 09 09 09 09 09 09 09 ries() {........
21b0: 09 6c 65 74 20 64 61 74 65 20 3d 20 69 74 65 6d .let date = item
21c0: 2e 70 75 62 6c 69 73 68 65 64 28 29 0a 09 09 09 .published()....
21d0: 09 09 09 09 09 09 2e 73 74 61 63 6b 5f 65 72 72 .......stack_err
21e0: 28 22 46 65 65 64 20 69 74 65 6d 20 6d 69 73 73 ("Feed item miss
21f0: 69 6e 67 20 70 75 62 6c 69 73 68 69 6e 67 20 64 ing publishing d
2200: 61 74 65 2e 22 29 3f 3b 0a 09 09 09 09 09 09 09 ate.")?;........
2210: 09 6c 65 74 20 75 72 69 20 3d 20 7b 0a 09 09 09 .let uri = {....
2220: 09 09 09 09 09 09 6c 65 74 20 6c 69 6e 6b 73 20 ......let links
2230: 3d 20 69 74 65 6d 2e 6c 69 6e 6b 73 28 29 3b 0a = item.links();.
2240: 09 09 09 09 09 09 09 09 09 69 66 20 6c 69 6e 6b .........if link
2250: 73 2e 69 73 5f 65 6d 70 74 79 28 29 20 7b 0a 09 s.is_empty() {..
2260: 09 09 09 09 09 09 09 09 09 62 61 69 6c 21 28 22 .........bail!("
2270: 46 65 65 64 20 69 74 65 6d 20 6d 69 73 73 69 6e Feed item missin
2280: 67 20 70 6f 73 74 20 6c 69 6e 6b 73 2e 22 29 3b g post links.");
2290: 0a 09 09 09 09 09 09 09 09 09 7d 20 65 6c 73 65 ..........} else
22a0: 20 7b 0a 09 09 09 09 09 09 09 09 09 09 6c 69 6e {...........lin
22b0: 6b 73 5b 30 5d 2e 68 72 65 66 28 29 2e 74 6f 5f ks[0].href().to_
22c0: 73 74 72 69 6e 67 28 29 0a 09 09 09 09 09 09 09 string()........
22d0: 09 09 7d 0a 09 09 09 09 09 09 09 09 7d 3b 0a 09 ..}.........};..
22e0: 09 09 09 09 09 09 09 6c 65 74 20 74 69 74 6c 65 .......let title
22f0: 20 3d 20 69 74 65 6d 2e 74 69 74 6c 65 28 29 2e = item.title().
2300: 74 6f 5f 73 74 72 69 6e 67 28 29 3b 0a 09 09 09 to_string();....
2310: 09 09 09 09 09 6c 65 74 20 61 75 74 68 6f 72 73 .....let authors
2320: 20 3d 20 69 74 65 6d 2e 61 75 74 68 6f 72 73 28 = item.authors(
2330: 29 2e 69 74 65 72 28 29 2e 6d 61 70 28 7c 78 7c ).iter().map(|x|
2340: 20 66 6f 72 6d 61 74 21 28 22 7b 7d 20 3c 7b 3a format!("{} <{:
2350: 3f 7d 3e 22 2c 20 78 2e 6e 61 6d 65 28 29 2c 20 ?}>", x.name(),
2360: 78 2e 65 6d 61 69 6c 28 29 29 29 2e 63 6f 6c 6c x.email())).coll
2370: 65 63 74 3a 3a 3c 56 65 63 3c 53 74 72 69 6e 67 ect::<Vec<String
2380: 3e 3e 28 29 2e 6a 6f 69 6e 28 22 2c 20 22 29 3b >>().join(", ");
2390: 0a 09 09 09 09 09 09 09 09 6c 65 74 20 73 75 6d .........let sum
23a0: 6d 61 72 79 20 3d 20 69 66 20 6c 65 74 20 53 6f mary = if let So
23b0: 6d 65 28 73 75 6d 29 20 3d 20 69 74 65 6d 2e 73 me(sum) = item.s
23c0: 75 6d 6d 61 72 79 28 29 20 7b 20 73 75 6d 2e 76 ummary() { sum.v
23d0: 61 6c 75 65 2e 63 6c 6f 6e 65 28 29 20 7d 20 65 alue.clone() } e
23e0: 6c 73 65 20 7b 20 53 74 72 69 6e 67 3a 3a 6e 65 lse { String::ne
23f0: 77 28 29 20 7d 3b 0a 09 09 09 09 09 09 09 09 70 w() };.........p
2400: 6f 73 74 73 2e 69 6e 73 65 72 74 28 2a 64 61 74 osts.insert(*dat
2410: 65 2c 20 50 6f 73 74 7b 0a 09 09 09 09 09 09 09 e, Post{........
2420: 09 09 75 72 69 2c 0a 09 09 09 09 09 09 09 09 09 ..uri,..........
2430: 74 69 74 6c 65 2c 0a 09 09 09 09 09 09 09 09 09 title,..........
2440: 61 75 74 68 6f 72 73 2c 0a 09 09 09 09 09 09 09 authors,........
2450: 09 09 73 75 6d 6d 61 72 79 2c 0a 09 09 09 09 09 ..summary,......
2460: 09 09 09 7d 29 3b 0a 09 09 09 09 09 09 09 7d 3b ...});........};
2470: 0a 09 09 09 09 09 09 7d 2c 0a 09 09 09 09 09 09 .......},.......
2480: 45 72 72 28 65 72 72 29 20 3d 3e 20 7b 0a 09 09 Err(err) => {...
2490: 09 09 09 09 09 62 61 69 6c 21 28 22 55 6e 73 75 .....bail!("Unsu
24a0: 70 70 6f 72 74 65 64 20 6f 72 20 6d 61 6e 67 6c pported or mangl
24b0: 65 64 20 63 6f 6e 74 65 6e 74 3a 5c 6e 7b 3a 3f ed content:\n{:?
24c0: 7d 5c 6e 7b 65 72 72 7d 5c 6e 7b 73 74 61 74 75 }\n{err}\n{statu
24d0: 73 3a 23 3f 7d 5c 6e 22 2c 20 26 73 6f 75 72 63 s:#?}\n", &sourc
24e0: 65 2e 75 72 6c 29 0a 09 09 09 09 09 09 7d 2c 0a e.url).......},.
24f0: 09 09 09 09 09 7d 0a 09 09 09 09 7d 2c 0a 09 09 .....}.....},...
2500: 09 09 72 73 73 3a 3a 45 72 72 6f 72 3a 3a 45 6f ..rss::Error::Eo
2510: 66 20 3d 3e 20 28 29 2c 0a 09 09 09 09 5f 20 3d f => (),....._ =
2520: 3e 20 62 61 69 6c 21 28 22 55 6e 73 75 70 70 6f > bail!("Unsuppo
2530: 72 74 65 64 20 6f 72 20 6d 61 6e 67 6c 65 64 20 rted or mangled
2540: 63 6f 6e 74 65 6e 74 3a 5c 6e 7b 3a 3f 7d 5c 6e content:\n{:?}\n
2550: 7b 65 72 72 7d 5c 6e 7b 73 74 61 74 75 73 3a 23 {err}\n{status:#
2560: 3f 7d 5c 6e 22 2c 20 26 73 6f 75 72 63 65 2e 75 ?}\n", &source.u
2570: 72 6c 29 0a 09 09 09 7d 0a 09 09 7d 3b 0a 09 09 rl)....}...};...
2580: 66 6f 72 20 28 64 61 74 65 2c 20 70 6f 73 74 29 for (date, post)
2590: 20 69 6e 20 70 6f 73 74 73 2e 69 74 65 72 28 29 in posts.iter()
25a0: 20 7b 0a 09 09 09 6c 65 74 20 70 6f 73 74 5f 75 {....let post_u
25b0: 72 6c 3a 20 43 6f 77 3c 73 74 72 3e 20 3d 20 6d rl: Cow<str> = m
25c0: 61 74 63 68 20 73 6f 75 72 63 65 2e 75 72 6c 5f atch source.url_
25d0: 72 65 20 7b 0a 09 09 09 09 53 6f 6d 65 28 72 65 re {.....Some(re
25e0: 66 20 78 29 20 3d 3e 20 73 65 64 72 65 67 65 78 f x) => sedregex
25f0: 3a 3a 52 65 70 6c 61 63 65 43 6f 6d 6d 61 6e 64 ::ReplaceCommand
2600: 3a 3a 6e 65 77 28 78 29 2e 73 74 61 63 6b 28 29 ::new(x).stack()
2610: 3f 2e 65 78 65 63 75 74 65 28 26 70 6f 73 74 2e ?.execute(&post.
2620: 75 72 69 29 2c 0a 09 09 09 09 4e 6f 6e 65 20 3d uri),.....None =
2630: 3e 20 70 6f 73 74 2e 75 72 69 2e 63 6c 6f 6e 65 > post.uri.clone
2640: 28 29 2e 69 6e 74 6f 28 29 2c 0a 09 09 09 7d 3b ().into(),....};
2650: 0a 09 09 09 69 66 20 21 20 63 6f 6e 6e 2e 65 78 ....if ! conn.ex
2660: 69 73 74 73 28 26 70 6f 73 74 5f 75 72 6c 2c 20 ists(&post_url,
2670: 69 64 29 2e 61 77 61 69 74 2e 73 74 61 63 6b 28 id).await.stack(
2680: 29 3f 20 7b 0a 09 09 09 09 69 66 20 74 68 69 73 )? {.....if this
2690: 5f 66 65 74 63 68 2e 69 73 5f 6e 6f 6e 65 28 29 _fetch.is_none()
26a0: 20 7c 7c 20 2a 64 61 74 65 20 3e 20 74 68 69 73 || *date > this
26b0: 5f 66 65 74 63 68 2e 75 6e 77 72 61 70 28 29 20 _fetch.unwrap()
26c0: 7b 0a 09 09 09 09 09 74 68 69 73 5f 66 65 74 63 {......this_fetc
26d0: 68 20 3d 20 53 6f 6d 65 28 2a 64 61 74 65 29 3b h = Some(*date);
26e0: 0a 09 09 09 09 7d 3b 0a 09 09 09 09 73 65 6c 66 .....};.....self
26f0: 2e 73 65 6e 64 28 20 6d 61 74 63 68 20 26 73 6f .send( match &so
2700: 75 72 63 65 2e 69 76 5f 68 61 73 68 20 7b 0a 09 urce.iv_hash {..
2710: 09 09 09 09 53 6f 6d 65 28 68 61 73 68 29 20 3d ....Some(hash) =
2720: 3e 20 66 6f 72 6d 61 74 21 28 22 3c 61 20 68 72 > format!("<a hr
2730: 65 66 3d 5c 22 68 74 74 70 73 3a 2f 2f 74 2e 6d ef=\"https://t.m
2740: 65 2f 69 76 3f 75 72 6c 3d 7b 70 6f 73 74 5f 75 e/iv?url={post_u
2750: 72 6c 7d 26 72 68 61 73 68 3d 7b 68 61 73 68 7d rl}&rhash={hash}
2760: 5c 22 3e 20 3c 2f 61 3e 7b 70 6f 73 74 5f 75 72 \"> </a>{post_ur
2770: 6c 7d 22 29 2c 0a 09 09 09 09 09 4e 6f 6e 65 20 l}"),......None
2780: 3d 3e 20 66 6f 72 6d 61 74 21 28 22 7b 70 6f 73 => format!("{pos
2790: 74 5f 75 72 6c 7d 22 29 2c 0a 09 09 09 09 7d 2c t_url}"),.....},
27a0: 20 53 6f 6d 65 28 64 65 73 74 69 6e 61 74 69 6f Some(destinatio
27b0: 6e 29 2c 20 53 6f 6d 65 28 50 61 72 73 65 4d 6f n), Some(ParseMo
27c0: 64 65 3a 3a 48 74 6d 6c 29 29 2e 61 77 61 69 74 de::Html)).await
27d0: 2e 73 74 61 63 6b 28 29 3f 3b 0a 09 09 09 09 63 .stack()?;.....c
27e0: 6f 6e 6e 2e 61 64 64 5f 70 6f 73 74 28 69 64 2c onn.add_post(id,
27f0: 20 64 61 74 65 2c 20 26 70 6f 73 74 5f 75 72 6c date, &post_url
2800: 29 2e 61 77 61 69 74 2e 73 74 61 63 6b 28 29 3f ).await.stack()?
2810: 3b 0a 09 09 09 09 70 6f 73 74 65 64 20 2b 3d 20 ;.....posted +=
2820: 31 3b 0a 09 09 09 7d 3b 0a 09 09 7d 3b 0a 09 09 1;....};...};...
2830: 70 6f 73 74 73 2e 63 6c 65 61 72 28 29 3b 0a 09 posts.clear();..
2840: 09 4f 6b 28 66 6f 72 6d 61 74 21 28 22 50 6f 73 .Ok(format!("Pos
2850: 74 65 64 3a 20 7b 70 6f 73 74 65 64 7d 22 29 29 ted: {posted}"))
2860: 0a 09 7d 0a 0a 09 61 73 79 6e 63 20 66 6e 20 61 ..}...async fn a
2870: 75 74 6f 66 65 74 63 68 28 26 73 65 6c 66 29 20 utofetch(&self)
2880: 2d 3e 20 52 65 73 75 6c 74 3c 73 74 64 3a 3a 74 -> Result<std::t
2890: 69 6d 65 3a 3a 44 75 72 61 74 69 6f 6e 3e 20 7b ime::Duration> {
28a0: 0a 09 09 6c 65 74 20 6d 75 74 20 64 65 6c 61 79 ...let mut delay
28b0: 20 3d 20 63 68 72 6f 6e 6f 3a 3a 44 75 72 61 74 = chrono::Durat
28c0: 69 6f 6e 3a 3a 6d 69 6e 75 74 65 73 28 31 29 3b ion::minutes(1);
28d0: 0a 09 09 6c 65 74 20 6e 6f 77 20 3d 20 63 68 72 ...let now = chr
28e0: 6f 6e 6f 3a 3a 4c 6f 63 61 6c 3a 3a 6e 6f 77 28 ono::Local::now(
28f0: 29 3b 0a 09 09 6c 65 74 20 71 75 65 75 65 20 3d );...let queue =
2900: 20 7b 0a 09 09 09 6c 65 74 20 6d 75 74 20 63 6f {....let mut co
2910: 6e 6e 20 3d 20 73 65 6c 66 2e 64 62 2e 62 65 67 nn = self.db.beg
2920: 69 6e 28 29 2e 61 77 61 69 74 2e 73 74 61 63 6b in().await.stack
2930: 28 29 3f 3b 0a 09 09 09 63 6f 6e 6e 2e 67 65 74 ()?;....conn.get
2940: 5f 71 75 65 75 65 28 29 2e 61 77 61 69 74 2e 73 _queue().await.s
2950: 74 61 63 6b 28 29 3f 0a 09 09 7d 3b 0a 09 09 66 tack()?...};...f
2960: 6f 72 20 72 6f 77 20 69 6e 20 71 75 65 75 65 20 or row in queue
2970: 7b 0a 09 09 09 69 66 20 6c 65 74 20 53 6f 6d 65 {....if let Some
2980: 28 6e 65 78 74 5f 66 65 74 63 68 29 20 3d 20 72 (next_fetch) = r
2990: 6f 77 2e 6e 65 78 74 5f 66 65 74 63 68 20 7b 0a ow.next_fetch {.
29a0: 09 09 09 09 69 66 20 6e 65 78 74 5f 66 65 74 63 ....if next_fetc
29b0: 68 20 3c 20 6e 6f 77 20 7b 0a 09 09 09 09 09 69 h < now {......i
29c0: 66 20 6c 65 74 20 28 53 6f 6d 65 28 6f 77 6e 65 f let (Some(owne
29d0: 72 29 2c 20 53 6f 6d 65 28 73 6f 75 72 63 65 5f r), Some(source_
29e0: 69 64 29 2c 20 6c 61 73 74 5f 73 63 72 61 70 65 id), last_scrape
29f0: 29 20 3d 20 28 72 6f 77 2e 6f 77 6e 65 72 2c 20 ) = (row.owner,
2a00: 72 6f 77 2e 73 6f 75 72 63 65 5f 69 64 2c 20 72 row.source_id, r
2a10: 6f 77 2e 6c 61 73 74 5f 73 63 72 61 70 65 29 20 ow.last_scrape)
2a20: 7b 0a 09 09 09 09 09 09 6c 65 74 20 63 6c 6f 6e {.......let clon
2a30: 65 20 3d 20 43 6f 72 65 20 7b 0a 09 09 09 09 09 e = Core {......
2a40: 09 09 6f 77 6e 65 72 5f 63 68 61 74 3a 20 43 68 ..owner_chat: Ch
2a50: 61 74 50 65 65 72 49 64 3a 3a 66 72 6f 6d 28 6f atPeerId::from(o
2a60: 77 6e 65 72 29 2c 0a 09 09 09 09 09 09 09 2e 2e wner),..........
2a70: 73 65 6c 66 2e 63 6c 6f 6e 65 28 29 0a 09 09 09 self.clone()....
2a80: 09 09 09 7d 3b 0a 09 09 09 09 09 09 6c 65 74 20 ...};.......let
2a90: 73 6f 75 72 63 65 20 3d 20 7b 0a 09 09 09 09 09 source = {......
2aa0: 09 09 6c 65 74 20 6d 75 74 20 63 6f 6e 6e 20 3d ..let mut conn =
2ab0: 20 73 65 6c 66 2e 64 62 2e 62 65 67 69 6e 28 29 self.db.begin()
2ac0: 2e 61 77 61 69 74 2e 73 74 61 63 6b 28 29 3f 3b .await.stack()?;
2ad0: 0a 09 09 09 09 09 09 09 6d 61 74 63 68 20 63 6f ........match co
2ae0: 6e 6e 2e 67 65 74 5f 6f 6e 65 28 6f 77 6e 65 72 nn.get_one(owner
2af0: 2c 20 73 6f 75 72 63 65 5f 69 64 29 2e 61 77 61 , source_id).awa
2b00: 69 74 20 7b 0a 09 09 09 09 09 09 09 09 4f 6b 28 it {.........Ok(
2b10: 53 6f 6d 65 28 73 6f 75 72 63 65 29 29 20 3d 3e Some(source)) =>
2b20: 20 73 6f 75 72 63 65 2e 74 6f 5f 73 74 72 69 6e source.to_strin
2b30: 67 28 29 2c 0a 09 09 09 09 09 09 09 09 4f 6b 28 g(),.........Ok(
2b40: 4e 6f 6e 65 29 20 3d 3e 20 22 53 6f 75 72 63 65 None) => "Source
2b50: 20 6e 6f 74 20 66 6f 75 6e 64 20 69 6e 20 64 61 not found in da
2b60: 74 61 62 61 73 65 3f 22 2e 74 6f 5f 73 74 72 69 tabase?".to_stri
2b70: 6e 67 28 29 2c 0a 09 09 09 09 09 09 09 09 45 72 ng(),.........Er
2b80: 72 28 65 72 72 29 20 3d 3e 20 66 6f 72 6d 61 74 r(err) => format
2b90: 21 28 22 46 61 69 6c 65 64 20 74 6f 20 66 65 74 !("Failed to fet
2ba0: 63 68 20 73 6f 75 72 63 65 20 64 61 74 61 3a 5c ch source data:\
2bb0: 6e 7b 65 72 72 7d 22 29 2c 0a 09 09 09 09 09 09 n{err}"),.......
2bc0: 09 7d 0a 09 09 09 09 09 09 7d 3b 0a 09 09 09 09 .}.......};.....
2bd0: 09 09 73 6d 6f 6c 3a 3a 73 70 61 77 6e 28 43 6f ..smol::spawn(Co
2be0: 6d 70 61 74 3a 3a 6e 65 77 28 61 73 79 6e 63 20 mpat::new(async
2bf0: 6d 6f 76 65 20 7b 0a 09 09 09 09 09 09 09 69 66 move {........if
2c00: 20 6c 65 74 20 45 72 72 28 65 72 72 29 20 3d 20 let Err(err) =
2c10: 63 6c 6f 6e 65 2e 63 68 65 63 6b 28 73 6f 75 72 clone.check(sour
2c20: 63 65 5f 69 64 2c 20 74 72 75 65 2c 20 53 6f 6d ce_id, true, Som
2c30: 65 28 6c 61 73 74 5f 73 63 72 61 70 65 29 29 2e e(last_scrape)).
2c40: 61 77 61 69 74 20 7b 0a 09 09 09 09 09 09 09 09 await {.........
2c50: 69 66 20 6c 65 74 20 45 72 72 28 65 72 72 29 20 if let Err(err)
2c60: 3d 20 63 6c 6f 6e 65 2e 73 65 6e 64 28 26 66 6f = clone.send(&fo
2c70: 72 6d 61 74 21 28 22 f0 9f 9b 91 20 7b 73 6f 75 rmat!("🛑 {sou
2c80: 72 63 65 7d 5c 6e 7b 7d 22 2c 20 65 6e 63 6f 64 rce}\n{}", encod
2c90: 65 28 26 65 72 72 2e 74 6f 5f 73 74 72 69 6e 67 e(&err.to_string
2ca0: 28 29 29 29 2c 20 4e 6f 6e 65 2c 20 53 6f 6d 65 ())), None, Some
2cb0: 28 50 61 72 73 65 4d 6f 64 65 3a 3a 4d 61 72 6b (ParseMode::Mark
2cc0: 64 6f 77 6e 56 32 29 29 2e 61 77 61 69 74 20 7b downV2)).await {
2cd0: 0a 09 09 09 09 09 09 09 09 09 65 70 72 69 6e 74 ..........eprint
2ce0: 6c 6e 21 28 22 43 68 65 63 6b 20 65 72 72 6f 72 ln!("Check error
2cf0: 3a 20 7b 65 72 72 7d 22 29 3b 0a 09 09 09 09 09 : {err}");......
2d00: 09 09 09 09 2f 2f 20 63 6c 6f 6e 65 2e 64 69 73 ....// clone.dis
2d10: 61 62 6c 65 28 26 73 6f 75 72 63 65 5f 69 64 2c able(&source_id,
2d20: 20 6f 77 6e 65 72 29 2e 61 77 61 69 74 2e 75 6e owner).await.un
2d30: 77 72 61 70 28 29 3b 0a 09 09 09 09 09 09 09 09 wrap();.........
2d40: 7d 3b 0a 09 09 09 09 09 09 09 7d 3b 0a 09 09 09 };........};....
2d50: 09 09 09 7d 29 29 2e 64 65 74 61 63 68 28 29 3b ...})).detach();
2d60: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 20 65 6c ......}.....} el
2d70: 73 65 20 69 66 20 6e 65 78 74 5f 66 65 74 63 68 se if next_fetch
2d80: 20 2d 20 6e 6f 77 20 3c 20 64 65 6c 61 79 20 7b - now < delay {
2d90: 0a 09 09 09 09 09 64 65 6c 61 79 20 3d 20 6e 65 ......delay = ne
2da0: 78 74 5f 66 65 74 63 68 20 2d 20 6e 6f 77 3b 0a xt_fetch - now;.
2db0: 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 7d 3b 0a ....}....}...};.
2dc0: 09 09 64 65 6c 61 79 2e 74 6f 5f 73 74 64 28 29 ..delay.to_std()
2dd0: 2e 73 74 61 63 6b 28 29 0a 09 7d 0a 0a 09 70 75 .stack()..}...pu
2de0: 62 20 61 73 79 6e 63 20 66 6e 20 6c 69 73 74 20 b async fn list
2df0: 28 26 73 65 6c 66 2c 20 6f 77 6e 65 72 3a 20 55 (&self, owner: U
2e00: 73 65 72 50 65 65 72 49 64 29 20 2d 3e 20 52 65 serPeerId) -> Re
2e10: 73 75 6c 74 3c 53 74 72 69 6e 67 3e 20 7b 0a 09 sult<String> {..
2e20: 09 6c 65 74 20 6d 75 74 20 72 65 70 6c 79 3a 20 .let mut reply:
2e30: 56 65 63 3c 53 74 72 69 6e 67 3e 20 3d 20 76 65 Vec<String> = ve
2e40: 63 21 5b 5d 3b 0a 09 09 72 65 70 6c 79 2e 70 75 c![];...reply.pu
2e50: 73 68 28 22 43 68 61 6e 6e 65 6c 73 3a 22 2e 69 sh("Channels:".i
2e60: 6e 74 6f 28 29 29 3b 0a 09 09 6c 65 74 20 6d 75 nto());...let mu
2e70: 74 20 63 6f 6e 6e 20 3d 20 73 65 6c 66 2e 64 62 t conn = self.db
2e80: 2e 62 65 67 69 6e 28 29 2e 61 77 61 69 74 2e 73 .begin().await.s
2e90: 74 61 63 6b 28 29 3f 3b 0a 09 09 66 6f 72 20 72 tack()?;...for r
2ea0: 6f 77 20 69 6e 20 63 6f 6e 6e 2e 67 65 74 5f 6c ow in conn.get_l
2eb0: 69 73 74 28 6f 77 6e 65 72 29 2e 61 77 61 69 74 ist(owner).await
2ec0: 2e 73 74 61 63 6b 28 29 3f 20 7b 0a 09 09 09 72 .stack()? {....r
2ed0: 65 70 6c 79 2e 70 75 73 68 28 72 6f 77 2e 74 6f eply.push(row.to
2ee0: 5f 73 74 72 69 6e 67 28 29 29 3b 0a 09 09 7d 3b _string());...};
2ef0: 0a 09 09 4f 6b 28 72 65 70 6c 79 2e 6a 6f 69 6e ...Ok(reply.join
2f00: 28 22 5c 6e 5c 6e 22 29 29 0a 09 7d 0a 7d 0a 0a ("\n\n"))..}.}..
2f10: 69 6d 70 6c 20 55 70 64 61 74 65 48 61 6e 64 6c impl UpdateHandl
2f20: 65 72 20 66 6f 72 20 43 6f 72 65 20 7b 0a 09 61 er for Core {..a
2f30: 73 79 6e 63 20 66 6e 20 68 61 6e 64 6c 65 20 28 sync fn handle (
2f40: 26 73 65 6c 66 2c 20 75 70 64 61 74 65 3a 20 55 &self, update: U
2f50: 70 64 61 74 65 29 20 7b 0a 09 09 69 66 20 6c 65 pdate) {...if le
2f60: 74 20 55 70 64 61 74 65 54 79 70 65 3a 3a 4d 65 t UpdateType::Me
2f70: 73 73 61 67 65 28 6d 73 67 29 20 3d 20 75 70 64 ssage(msg) = upd
2f80: 61 74 65 2e 75 70 64 61 74 65 5f 74 79 70 65 20 ate.update_type
2f90: 7b 0a 09 09 09 69 66 20 6c 65 74 20 4f 6b 28 63 {....if let Ok(c
2fa0: 6d 64 29 20 3d 20 43 6f 6d 6d 61 6e 64 3a 3a 74 md) = Command::t
2fb0: 72 79 5f 66 72 6f 6d 28 2a 6d 73 67 29 20 7b 0a ry_from(*msg) {.
2fc0: 09 09 09 09 6c 65 74 20 6d 73 67 20 3d 20 63 6d ....let msg = cm
2fd0: 64 2e 67 65 74 5f 6d 65 73 73 61 67 65 28 29 3b d.get_message();
2fe0: 0a 09 09 09 09 6c 65 74 20 77 6f 72 64 73 20 3d .....let words =
2ff0: 20 63 6d 64 2e 67 65 74 5f 61 72 67 73 28 29 3b cmd.get_args();
3000: 0a 09 09 09 09 6c 65 74 20 63 6f 6d 6d 61 6e 64 .....let command
3010: 20 3d 20 63 6d 64 2e 67 65 74 5f 6e 61 6d 65 28 = cmd.get_name(
3020: 29 3b 0a 09 09 09 09 6c 65 74 20 72 65 73 20 3d );.....let res =
3030: 20 6d 61 74 63 68 20 63 6f 6d 6d 61 6e 64 20 7b match command {
3040: 0a 09 09 09 09 09 22 2f 63 68 65 63 6b 22 20 7c ......"/check" |
3050: 20 22 2f 63 6c 65 61 6e 22 20 7c 20 22 2f 65 6e "/clean" | "/en
3060: 61 62 6c 65 22 20 7c 20 22 2f 64 65 6c 65 74 65 able" | "/delete
3070: 22 20 7c 20 22 2f 64 69 73 61 62 6c 65 22 20 3d " | "/disable" =
3080: 3e 20 63 6f 6d 6d 61 6e 64 3a 3a 63 6f 6d 6d 61 > command::comma
3090: 6e 64 28 73 65 6c 66 2c 20 63 6f 6d 6d 61 6e 64 nd(self, command
30a0: 2c 20 6d 73 67 2c 20 77 6f 72 64 73 29 2e 61 77 , msg, words).aw
30b0: 61 69 74 2c 0a 09 09 09 09 09 22 2f 73 74 61 72 ait,......"/star
30c0: 74 22 20 3d 3e 20 63 6f 6d 6d 61 6e 64 3a 3a 73 t" => command::s
30d0: 74 61 72 74 28 73 65 6c 66 2c 20 6d 73 67 29 2e tart(self, msg).
30e0: 61 77 61 69 74 2c 0a 09 09 09 09 09 22 2f 6c 69 await,......"/li
30f0: 73 74 22 20 3d 3e 20 63 6f 6d 6d 61 6e 64 3a 3a st" => command::
3100: 6c 69 73 74 28 73 65 6c 66 2c 20 6d 73 67 29 2e list(self, msg).
3110: 61 77 61 69 74 2c 0a 09 09 09 09 09 22 2f 61 64 await,......"/ad
3120: 64 22 20 7c 20 22 2f 75 70 64 61 74 65 22 20 3d d" | "/update" =
3130: 3e 20 63 6f 6d 6d 61 6e 64 3a 3a 75 70 64 61 74 > command::updat
3140: 65 28 73 65 6c 66 2c 20 63 6f 6d 6d 61 6e 64 2c e(self, command,
3150: 20 6d 73 67 2c 20 77 6f 72 64 73 29 2e 61 77 61 msg, words).awa
3160: 69 74 2c 0a 09 09 09 09 09 61 6e 79 20 3d 3e 20 it,......any =>
3170: 45 72 72 28 61 6e 79 68 6f 77 21 28 22 55 6e 6b Err(anyhow!("Unk
3180: 6e 6f 77 6e 20 63 6f 6d 6d 61 6e 64 3a 20 7b 61 nown command: {a
3190: 6e 79 7d 22 29 29 2c 0a 09 09 09 09 7d 3b 0a 09 ny}")),.....};..
31a0: 09 09 09 69 66 20 6c 65 74 20 45 72 72 28 65 72 ...if let Err(er
31b0: 72 29 20 3d 20 72 65 73 20 7b 0a 09 09 09 09 09 r) = res {......
31c0: 69 66 20 6c 65 74 20 45 72 72 28 65 72 72 32 29 if let Err(err2)
31d0: 20 3d 20 73 65 6c 66 2e 73 65 6e 64 28 66 6f 72 = self.send(for
31e0: 6d 61 74 21 28 22 5c 5c 23 65 72 72 6f 72 5c 6e mat!("\\#error\n
31f0: 60 60 60 5c 6e 7b 65 72 72 7d 5c 6e 60 60 60 22 ```\n{err}\n```"
3200: 29 2c 0a 09 09 09 09 09 09 53 6f 6d 65 28 6d 73 ),.......Some(ms
3210: 67 2e 63 68 61 74 2e 67 65 74 5f 69 64 28 29 29 g.chat.get_id())
3220: 2c 0a 09 09 09 09 09 09 53 6f 6d 65 28 50 61 72 ,.......Some(Par
3230: 73 65 4d 6f 64 65 3a 3a 4d 61 72 6b 64 6f 77 6e seMode::Markdown
3240: 56 32 29 0a 09 09 09 09 09 29 2e 61 77 61 69 74 V2)......).await
3250: 20 7b 0a 09 09 09 09 09 09 6c 65 74 20 5f 20 3d {.......let _ =
3260: 20 64 62 67 21 28 65 72 72 32 29 3b 0a 09 09 09 dbg!(err2);....
3270: 09 09 7d 3b 0a 09 09 09 09 7d 0a 09 09 09 7d 3b ..};.....}....};
3280: 0a 09 09 7d 3b 0a 09 7d 0a 7d 0a ...};..}.}.