Diff
Logged in as anonymous

Differences From Artifact [a915a95027]:

To Artifact [5dad413b9c]:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13





14
15
16
17
18
19
20













-
-
-
-
-







use crate::{
	command,
	sql::Db,
};

use std::{
	borrow::Cow,
	collections::{
		BTreeMap,
		HashSet,
	},
};

use anyhow::{
	anyhow,
	bail,
	Result,
};
use async_std::{
	task,
	sync::{
		Arc,
		Mutex
	},
};
38
39
40
41
42
43
44






45
46
47
48
49
50
51
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52







+
+
+
+
+
+







		ParseMode,
		SendMessage,
		Update,
		UpdateType,
		UserPeerId,
	},
};
use stacked_errors::{
	Result,
	StackableErr,
	anyhow,
	bail,
};

lazy_static!{
	pub static ref RE_SPECIAL: Regex = Regex::new(r"([\-_*\[\]()~`>#+|{}\.!])").unwrap();
}

/// Encodes special HTML entities to prevent them interfering with Telegram HTML
pub fn encode (text: &str) -> Cow<'_, str> {
61
62
63
64
65
66
67
68
69
70



71
72
73
74

75
76
77
78


79
80
81
82
83

84
85
86
87
88
89
90
62
63
64
65
66
67
68



69
70
71
72
73
74

75
76
77


78
79
80
81
82
83

84
85
86
87
88
89
90
91







-
-
-
+
+
+



-
+


-
-
+
+




-
+







	pub db: Db,
	sources: Arc<Mutex<HashSet<Arc<i32>>>>,
	http_client: reqwest::Client,
}

impl Core {
	pub async fn new(settings: config::Config) -> Result<Core> {
		let owner_chat = ChatPeerId::from(settings.get_int("owner")?);
		let api_key = settings.get_string("api_key")?;
		let tg = Client::new(&api_key)?;
		let owner_chat = ChatPeerId::from(settings.get_int("owner").stack()?);
		let api_key = settings.get_string("api_key").stack()?;
		let tg = Client::new(&api_key).stack()?;

		let mut client = reqwest::Client::builder();
		if let Ok(proxy) = settings.get_string("proxy") {
			let proxy = reqwest::Proxy::all(proxy)?;
			let proxy = reqwest::Proxy::all(proxy).stack()?;
			client = client.proxy(proxy);
		}
		let http_client = client.build()?;
		let me = tg.execute(GetBot).await?;
		let http_client = client.build().stack()?;
		let me = tg.execute(GetBot).await.stack()?;
		let core = Core {
			tg,
			me,
			owner_chat,
			db: Db::new(&settings.get_string("pg")?)?,
			db: Db::new(&settings.get_string("pg").stack()?)?,
			sources: Arc::new(Mutex::new(HashSet::new())),
			http_client,
			// max_delay: 60,
		};
		let clone = core.clone();
		task::spawn(async move {
			loop {
105
106
107
108
109
110
111
112

113
114
115

116
117
118
119
120

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144

145
146

147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
106
107
108
109
110
111
112

113
114
115

116
117
118
119
120

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135


136
137
138
139
140
141
142
143
144

145
146

147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162







-
+


-
+




-
+














-
-
+
+







-
+

-
+







-
+








	pub async fn send <S>(&self, msg: S, target: Option<ChatPeerId>, mode: Option<ParseMode>) -> Result<Message>
	where S: Into<String> {
		let msg = msg.into();

		let mode = mode.unwrap_or(ParseMode::Html);
		let target = target.unwrap_or(self.owner_chat);
		Ok(self.tg.execute(
		self.tg.execute(
			SendMessage::new(target, msg)
				.with_parse_mode(mode)
		).await?)
		).await.stack()
	}

	pub async fn check (&self, id: i32, real: bool) -> Result<String> {
		let mut posted: i32 = 0;
		let mut conn = self.db.begin().await?;
		let mut conn = self.db.begin().await.stack()?;

		let id = {
			let mut set = self.sources.lock_arc().await;
			match set.get(&id) {
				Some(id) => id.clone(),
				None => {
					let id = Arc::new(id);
					set.insert(id.clone());
					id.clone()
				},
			}
		};
		let count = Arc::strong_count(&id);
		if count == 2 {
			let source = conn.get_source(*id, self.owner_chat).await?;
			conn.set_scrape(*id).await?;
			let source = conn.get_source(*id, self.owner_chat).await.stack()?;
			conn.set_scrape(*id).await.stack()?;
			let destination = ChatPeerId::from(match real {
				true => source.channel_id,
				false => source.owner,
			});
			let mut this_fetch: Option<DateTime<chrono::FixedOffset>> = None;
			let mut posts: BTreeMap<DateTime<chrono::FixedOffset>, String> = BTreeMap::new();

			let response = self.http_client.get(&source.url).send().await?;
			let response = self.http_client.get(&source.url).send().await.stack()?;
			let status = response.status();
			let content = response.bytes().await?;
			let content = response.bytes().await.stack()?;
			match rss::Channel::read_from(&content[..]) {
				Ok(feed) => {
					for item in feed.items() {
						if let Some(link) = item.link() {
							let date = match item.pub_date() {
								Some(feed_date) => DateTime::parse_from_rfc2822(feed_date),
								None => DateTime::parse_from_rfc3339(&item.dublin_core_ext().unwrap().dates()[0]),
							}?;
							}.stack()?;
							let url = link;
							posts.insert(date, url.to_string());
						}
					};
				},
				Err(err) => match err {
					rss::Error::InvalidStartTag => {
174
175
176
177
178
179
180
181

182
183
184

185
186
187
188
189
190
191
192
193


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208


209
210
211
212
213
214
215
216
217
218
219

220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247


248
249
250
251
252
253
254
175
176
177
178
179
180
181

182
183
184

185
186
187
188
189
190
191
192


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207


208
209
210
211
212
213
214
215
216
217
218
219

220
221
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246


247
248
249
250
251
252
253
254
255







-
+


-
+







-
-
+
+













-
-
+
+










-
+


-
+

















-
+





-
-
+
+







					},
					rss::Error::Eof => (),
					_ => bail!("Unsupported or mangled content:\n{:?}\n{err:#?}\n{status:#?}\n", &source.url)
				}
			};
			for (date, url) in posts.iter() {
				let post_url: Cow<str> = match source.url_re {
					Some(ref x) => sedregex::ReplaceCommand::new(x)?.execute(url),
					Some(ref x) => sedregex::ReplaceCommand::new(x).stack()?.execute(url),
					None => url.into(),
				};
				if let Some(exists) = conn.exists(&post_url, *id).await? {
				if let Some(exists) = conn.exists(&post_url, *id).await.stack()? {
					if ! exists {
						if this_fetch.is_none() || *date > this_fetch.unwrap() {
							this_fetch = Some(*date);
						};
						self.send( match &source.iv_hash {
							Some(hash) => format!("<a href=\"https://t.me/iv?url={post_url}&rhash={hash}\"> </a>{post_url}"),
							None => format!("{post_url}"),
						}, Some(destination), Some(ParseMode::Html)).await?;
						conn.add_post(*id, date, &post_url).await?;
						}, Some(destination), Some(ParseMode::Html)).await.stack()?;
						conn.add_post(*id, date, &post_url).await.stack()?;
					};
				};
				posted += 1;
			};
			posts.clear();
		};
		Ok(format!("Posted: {posted}"))
	}

	async fn autofetch(&self) -> Result<std::time::Duration> {
		let mut delay = chrono::Duration::minutes(1);
		let now = chrono::Local::now();
		let queue = {
			let mut conn = self.db.begin().await?;
			conn.get_queue().await?
			let mut conn = self.db.begin().await.stack()?;
			conn.get_queue().await.stack()?
		};
		for row in queue {
			if let Some(next_fetch) = row.next_fetch {
				if next_fetch < now {
					if let (Some(owner), Some(source_id)) = (row.owner, row.source_id) {
						let clone = Core {
							owner_chat: ChatPeerId::from(owner),
							..self.clone()
						};
						let source = {
							let mut conn = self.db.begin().await?;
							let mut conn = self.db.begin().await.stack()?;
							match conn.get_one(owner, source_id).await {
								Ok(Some(source)) => source.to_string(),
								Ok(None) => "Source not found in database?".to_string(),
								Ok(None) => "Source not found in database.stack()?".to_string(),
								Err(err) => format!("Failed to fetch source data:\n{err}"),
							}
						};
						task::spawn(async move {
							if let Err(err) = clone.check(source_id, true).await {
								if let Err(err) = clone.send(&format!("{source}\n\nšŸ›‘ {}", encode(&err.to_string())), None, Some(ParseMode::MarkdownV2)).await {
									eprintln!("Check error: {err:?}");
									// clone.disable(&source_id, owner).await.unwrap();
								};
							};
						});
					}
				} else if next_fetch - now < delay {
					delay = next_fetch - now;
				}
			}
		};
		Ok(delay.to_std()?)
		delay.to_std().stack()
	}

	pub async fn list (&self, owner: UserPeerId) -> Result<String> {
		let mut reply: Vec<String> = vec![];
		reply.push("Channels:".into());
		let mut conn = self.db.begin().await?;
		for row in conn.get_list(owner).await? {
		let mut conn = self.db.begin().await.stack()?;
		for row in conn.get_list(owner).await.stack()? {
			reply.push(row.to_string());
		};
		Ok(reply.join("\n\n"))
	}
}

impl UpdateHandler for Core {