Fix locking whole handler on update.
This commit is contained in:
parent
d14e689cb9
commit
91711bc687
|
@ -591,14 +591,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
[[package]]
|
||||
name = "furaffinity-rs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.huefox.com/syfaro/furaffinity-rs.git#75254a58385b3a8ea7fde0c86ff764fd1688bad9"
|
||||
source = "git+https://git.huefox.com/syfaro/furaffinity-rs.git#1548d7f0132e65b360af8bef16f864a1c3b3df02"
|
||||
dependencies = [
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"img_hash 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.10.1 (git+https://github.com/seanmonstar/reqwest)",
|
||||
"reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scraper 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1862,40 +1862,6 @@ dependencies = [
|
|||
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/seanmonstar/reqwest#147831375613a5e508487b2d85a99104ae1505af"
|
||||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project-lite 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.10.1"
|
||||
|
@ -3157,7 +3123,6 @@ dependencies = [
|
|||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum rental 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f"
|
||||
"checksum rental-impl 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de"
|
||||
"checksum reqwest 0.10.1 (git+https://github.com/seanmonstar/reqwest)" = "<none>"
|
||||
"checksum reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0e798e19e258bf6c30a304622e3e9ac820e483b06a1857a026e1f109b113fe4"
|
||||
"checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
|
|
|
@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = "1.0"
|
||||
|
||||
egg-mode = "0.13.0"
|
||||
tokio = { version = "0.2.0", features = ["macros", "time", "stream"] }
|
||||
tokio = { version = "0.2.0", features = ["macros", "time", "stream", "sync"] }
|
||||
futures = "0.3.1"
|
||||
futures-util = "0.3.1"
|
||||
tokio01 = { version = "0.1", package = "tokio" }
|
||||
|
|
|
@ -85,7 +85,7 @@ impl FAUtil {
|
|||
pub async fn image_search(
|
||||
&self,
|
||||
data: &[u8],
|
||||
exact: MatchType
|
||||
exact: MatchType,
|
||||
) -> reqwest::Result<Vec<ImageLookup>> {
|
||||
use reqwest::multipart::{Form, Part};
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ welcome-group =
|
|||
· /mirror - I'll look at all the links in your message or the message you're replying to and mirror them
|
||||
· /source - I'll attempt to find if the photo you're replying to has been posted on FurAffinity
|
||||
|
||||
welcome-try-me = Try Me!
|
||||
|
||||
# Inline Keyboard
|
||||
inline-direct = Direct Link
|
||||
inline-source = Source
|
||||
|
|
440
src/main.rs
440
src/main.rs
|
@ -5,7 +5,7 @@ use sites::{PostInfo, Site};
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use telegram::*;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tokio01::runtime::current_thread::block_on_all;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
|
@ -146,20 +146,20 @@ async fn main() {
|
|||
.await
|
||||
.expect("Unable to fetch bot user");
|
||||
|
||||
let handler = Arc::new(Mutex::new(MessageHandler {
|
||||
sites,
|
||||
let handler = Arc::new(MessageHandler {
|
||||
sites: Mutex::new(sites),
|
||||
bot: bot.clone(),
|
||||
bot_user,
|
||||
finder,
|
||||
fapi,
|
||||
db,
|
||||
db: RwLock::new(db),
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
langs,
|
||||
best_lang: HashMap::new(),
|
||||
best_lang: RwLock::new(HashMap::new()),
|
||||
influx: Arc::new(influx),
|
||||
use_proxy,
|
||||
}));
|
||||
});
|
||||
|
||||
let use_webhooks: bool = std::env::var("USE_WEBHOOKS")
|
||||
.unwrap_or_else(|_| "false".to_string())
|
||||
|
@ -197,7 +197,7 @@ async fn main() {
|
|||
|
||||
async fn handle_request(
|
||||
req: hyper::Request<hyper::Body>,
|
||||
handler: Arc<Mutex<MessageHandler>>,
|
||||
handler: Arc<MessageHandler>,
|
||||
secret: &str,
|
||||
) -> hyper::Result<hyper::Response<hyper::Body>> {
|
||||
use hyper::{Body, Response, StatusCode};
|
||||
|
@ -222,27 +222,21 @@ async fn handle_request(
|
|||
}
|
||||
};
|
||||
|
||||
{
|
||||
let mut handler = handler.lock().await;
|
||||
log::debug!("Got update: {:?}", update);
|
||||
handler.handle_message(update).await;
|
||||
log::debug!("Got update: {:?}", update);
|
||||
handler.handle_message(update).await;
|
||||
|
||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "http")
|
||||
.add_field("duration", now.elapsed().as_millis() as i64);
|
||||
|
||||
if let Err(e) = handler.influx.query(&point).await {
|
||||
let uuid = capture_fail(&e);
|
||||
log::error!(
|
||||
"[{}] Unable to send http request info to InfluxDB: {:?}",
|
||||
uuid,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "http")
|
||||
.add_field("duration", now.elapsed().as_millis() as i64);
|
||||
|
||||
let handler = handler.lock().await;
|
||||
if let Err(e) = handler.influx.query(&point).await {
|
||||
let uuid = capture_fail(&e);
|
||||
log::error!(
|
||||
"[{}] Unable to send http request info to InfluxDB: {:?}",
|
||||
uuid,
|
||||
e
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Response::new(Body::from("✓")))
|
||||
}
|
||||
_ => {
|
||||
|
@ -253,7 +247,7 @@ async fn handle_request(
|
|||
}
|
||||
}
|
||||
|
||||
async fn receive_webhook(handler: Arc<Mutex<MessageHandler>>) {
|
||||
async fn receive_webhook(handler: Arc<MessageHandler>) {
|
||||
let host = std::env::var("HTTP_HOST").expect("Missing HTTP_HOST");
|
||||
let addr = host.parse().expect("Invalid HTTP_HOST");
|
||||
|
||||
|
@ -280,7 +274,7 @@ async fn receive_webhook(handler: Arc<Mutex<MessageHandler>>) {
|
|||
}
|
||||
}
|
||||
|
||||
async fn poll_updates(bot: Arc<Telegram>, handler: Arc<Mutex<MessageHandler>>) {
|
||||
async fn poll_updates(bot: Arc<Telegram>, handler: Arc<MessageHandler>) {
|
||||
let mut update_req = GetUpdates::default();
|
||||
update_req.timeout = Some(30);
|
||||
|
||||
|
@ -300,10 +294,7 @@ async fn poll_updates(bot: Arc<Telegram>, handler: Arc<Mutex<MessageHandler>>) {
|
|||
|
||||
let handler = handler.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut handler = handler.lock().await;
|
||||
handler.handle_message(update).await
|
||||
});
|
||||
tokio::spawn(async move { handler.handle_message(update).await });
|
||||
|
||||
update_req.offset = Some(id + 1);
|
||||
}
|
||||
|
@ -316,7 +307,7 @@ struct MessageHandler {
|
|||
// State
|
||||
bot_user: User,
|
||||
langs: HashMap<LanguageIdentifier, Vec<String>>,
|
||||
best_lang: HashMap<String, fluent::FluentBundle<fluent::FluentResource>>,
|
||||
best_lang: RwLock<HashMap<String, fluent::FluentBundle<fluent::FluentResource>>>,
|
||||
|
||||
// API clients
|
||||
bot: Arc<Telegram>,
|
||||
|
@ -325,20 +316,20 @@ struct MessageHandler {
|
|||
finder: linkify::LinkFinder,
|
||||
|
||||
// Configuration
|
||||
sites: Vec<BoxedSite>,
|
||||
sites: Mutex<Vec<BoxedSite>>, // We always need mutable access, no reason to use a RwLock
|
||||
consumer_key: String,
|
||||
consumer_secret: String,
|
||||
use_proxy: bool,
|
||||
|
||||
// Storage
|
||||
db: pickledb::PickleDb,
|
||||
db: RwLock<pickledb::PickleDb>,
|
||||
}
|
||||
|
||||
impl MessageHandler {
|
||||
fn get_fluent_bundle(
|
||||
&mut self,
|
||||
requested: Option<&str>,
|
||||
) -> &fluent::FluentBundle<fluent::FluentResource> {
|
||||
async fn get_fluent_bundle<C, R>(&self, requested: Option<&str>, callback: C) -> R
|
||||
where
|
||||
C: FnOnce(&fluent::FluentBundle<fluent::FluentResource>) -> R,
|
||||
{
|
||||
let requested = if let Some(requested) = requested {
|
||||
requested
|
||||
} else {
|
||||
|
@ -347,11 +338,12 @@ impl MessageHandler {
|
|||
|
||||
log::trace!("Looking up language bundle for {}", requested);
|
||||
|
||||
if self.best_lang.contains_key(requested) {
|
||||
return self
|
||||
.best_lang
|
||||
.get(requested)
|
||||
.expect("Should have contained");
|
||||
{
|
||||
let lock = self.best_lang.read().await;
|
||||
if lock.contains_key(requested) {
|
||||
let bundle = lock.get(requested).expect("Should have contained");
|
||||
return callback(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Got new language {}, building bundle", requested);
|
||||
|
@ -390,14 +382,18 @@ impl MessageHandler {
|
|||
|
||||
bundle.set_use_isolating(false);
|
||||
|
||||
self.best_lang.insert(requested.to_string(), bundle);
|
||||
self.best_lang
|
||||
.get(requested)
|
||||
.expect("Value just inserted is missing")
|
||||
{
|
||||
let mut lock = self.best_lang.write().await;
|
||||
lock.insert(requested.to_string(), bundle);
|
||||
}
|
||||
|
||||
let lock = self.best_lang.read().await;
|
||||
let bundle = lock.get(requested).expect("Value just inserted is missing");
|
||||
callback(bundle)
|
||||
}
|
||||
|
||||
async fn report_error<C>(
|
||||
&mut self,
|
||||
&self,
|
||||
message: &Message,
|
||||
tags: Option<Vec<(&str, String)>>,
|
||||
callback: C,
|
||||
|
@ -411,18 +407,21 @@ impl MessageHandler {
|
|||
.clone()
|
||||
.map(|from| from.language_code)
|
||||
.flatten();
|
||||
let bundle = self.get_fluent_bundle(lang_code.as_deref());
|
||||
|
||||
let msg = if u.is_nil() {
|
||||
utils::get_message(&bundle, "error-generic", None)
|
||||
} else {
|
||||
let f = format!("`{}`", u.to_string());
|
||||
let msg = self
|
||||
.get_fluent_bundle(lang_code.as_deref(), |bundle| {
|
||||
if u.is_nil() {
|
||||
utils::get_message(&bundle, "error-generic", None)
|
||||
} else {
|
||||
let f = format!("`{}`", u.to_string());
|
||||
|
||||
let mut args = fluent::FluentArgs::new();
|
||||
args.insert("uuid", fluent::FluentValue::from(f));
|
||||
let mut args = fluent::FluentArgs::new();
|
||||
args.insert("uuid", fluent::FluentValue::from(f));
|
||||
|
||||
utils::get_message(&bundle, "error-uuid", Some(args))
|
||||
};
|
||||
utils::get_message(&bundle, "error-uuid", Some(args))
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
|
@ -440,7 +439,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_inline(&mut self, inline: InlineQuery) {
|
||||
async fn handle_inline(&self, inline: InlineQuery) {
|
||||
let links: Vec<_> = self.finder.links(&inline.query).collect();
|
||||
let mut results: Vec<PostInfo> = Vec::new();
|
||||
|
||||
|
@ -448,46 +447,52 @@ impl MessageHandler {
|
|||
log::debug!("Found links: {:?}", links);
|
||||
|
||||
let influx = self.influx.clone();
|
||||
utils::find_images(&inline.from, links, &mut self.sites, &mut |info| {
|
||||
let influx = influx.clone();
|
||||
let duration = info.duration;
|
||||
let count = info.results.len();
|
||||
let name = info.site.name();
|
||||
{
|
||||
let mut sites = self.sites.lock().await;
|
||||
utils::find_images(&inline.from, links, &mut sites, &mut |info| {
|
||||
let influx = influx.clone();
|
||||
let duration = info.duration;
|
||||
let count = info.results.len();
|
||||
let name = info.site.name();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "inline")
|
||||
.add_tag("site", name.replace(" ", "_"))
|
||||
.add_field("count", count as i32)
|
||||
.add_field("duration", duration);
|
||||
tokio::spawn(async move {
|
||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "inline")
|
||||
.add_tag("site", name.replace(" ", "_"))
|
||||
.add_field("count", count as i32)
|
||||
.add_field("duration", duration);
|
||||
|
||||
influx.query(&point).await
|
||||
});
|
||||
influx.query(&point).await
|
||||
});
|
||||
|
||||
results.extend(info.results);
|
||||
})
|
||||
.await;
|
||||
results.extend(info.results);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
let personal = results.iter().any(|result| result.personal);
|
||||
|
||||
let mut responses: Vec<InlineQueryResult> = results
|
||||
.iter()
|
||||
.map(|result| self.process_result(result, &inline.from))
|
||||
.filter_map(|result| result)
|
||||
.flatten()
|
||||
.collect();
|
||||
let mut responses: Vec<InlineQueryResult> = vec![];
|
||||
|
||||
if responses.is_empty() {
|
||||
let bundle = self.get_fluent_bundle(inline.from.language_code.as_deref());
|
||||
|
||||
if !inline.query.is_empty() {
|
||||
responses.push(InlineQueryResult::article(
|
||||
generate_id(),
|
||||
utils::get_message(&bundle, "inline-no-results-title", None).unwrap(),
|
||||
utils::get_message(&bundle, "inline-no-results-body", None).unwrap(),
|
||||
));
|
||||
for result in results {
|
||||
if let Some(items) = self.process_result(&result, &inline.from).await {
|
||||
responses.extend(items);
|
||||
}
|
||||
}
|
||||
|
||||
if responses.is_empty() && !inline.query.is_empty() {
|
||||
let article = self
|
||||
.get_fluent_bundle(inline.from.language_code.as_deref(), |bundle| {
|
||||
InlineQueryResult::article(
|
||||
generate_id(),
|
||||
utils::get_message(&bundle, "inline-no-results-title", None).unwrap(),
|
||||
utils::get_message(&bundle, "inline-no-results-body", None).unwrap(),
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
responses.push(article);
|
||||
}
|
||||
|
||||
let mut answer_inline = AnswerInlineQuery {
|
||||
inline_query_id: inline.id,
|
||||
results: responses,
|
||||
|
@ -508,7 +513,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_command(&mut self, message: Message) {
|
||||
async fn handle_command(&self, message: Message) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let command = match message.get_command() {
|
||||
|
@ -549,17 +554,22 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_welcome(&mut self, message: Message, command: &str) {
|
||||
async fn handle_welcome(&self, message: Message, command: &str) {
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
let from = message.from.clone().unwrap();
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
|
||||
let random_artwork = *STARTING_ARTWORK.choose(&mut rand::thread_rng()).unwrap();
|
||||
|
||||
let try_me = self
|
||||
.get_fluent_bundle(from.language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, "welcome-try-me", None).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let reply_markup = ReplyMarkup::InlineKeyboardMarkup(InlineKeyboardMarkup {
|
||||
inline_keyboard: vec![vec![InlineKeyboardButton {
|
||||
text: "Try Me!".to_string(),
|
||||
text: try_me,
|
||||
switch_inline_query_current_chat: Some(random_artwork.to_string()),
|
||||
..Default::default()
|
||||
}]],
|
||||
|
@ -571,9 +581,15 @@ impl MessageHandler {
|
|||
"welcome"
|
||||
};
|
||||
|
||||
let welcome = self
|
||||
.get_fluent_bundle(from.language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, &name, None).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
text: utils::get_message(&bundle, &name, None).unwrap(),
|
||||
text: welcome,
|
||||
reply_markup: Some(reply_markup),
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -626,7 +642,7 @@ impl MessageHandler {
|
|||
});
|
||||
}
|
||||
|
||||
async fn handle_source(&mut self, message: Message) {
|
||||
async fn handle_source(&self, message: Message) {
|
||||
let completed = Arc::new(AtomicBool::new(false));
|
||||
self.send_action(
|
||||
12,
|
||||
|
@ -685,8 +701,6 @@ impl MessageHandler {
|
|||
}
|
||||
};
|
||||
|
||||
let bundle = self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
|
||||
let name = if result.distance < 5 {
|
||||
"reverse-good-result"
|
||||
} else {
|
||||
|
@ -700,9 +714,16 @@ impl MessageHandler {
|
|||
fluent::FluentValue::from(format!("https://www.furaffinity.net/view/{}/", result.id)),
|
||||
);
|
||||
|
||||
let text = self
|
||||
.get_fluent_bundle(
|
||||
message.from.clone().unwrap().language_code.as_deref(),
|
||||
|bundle| utils::get_message(&bundle, name, Some(args)).unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat.id.into(),
|
||||
text: utils::get_message(&bundle, name, Some(args)).unwrap(),
|
||||
text,
|
||||
disable_web_page_preview: Some(result.distance > 5),
|
||||
reply_to_message_id: Some(reply_to_id),
|
||||
..Default::default()
|
||||
|
@ -718,7 +739,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_alts(&mut self, message: Message) {
|
||||
async fn handle_alts(&self, message: Message) {
|
||||
let completed = Arc::new(AtomicBool::new(false));
|
||||
self.send_action(
|
||||
12,
|
||||
|
@ -788,9 +809,17 @@ impl MessageHandler {
|
|||
.map(|item| (item.0, item.1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let bundle = self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
|
||||
let (text, used_hashes) = utils::build_alternate_response(&bundle, items);
|
||||
let ((text, used_hashes), alternate) = self
|
||||
.get_fluent_bundle(
|
||||
message.from.clone().unwrap().language_code.as_deref(),
|
||||
|bundle| {
|
||||
(
|
||||
utils::build_alternate_response(&bundle, items),
|
||||
utils::alternate_feedback_keyboard(&bundle),
|
||||
)
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
completed.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
|
@ -800,9 +829,7 @@ impl MessageHandler {
|
|||
disable_web_page_preview: Some(true),
|
||||
parse_mode: Some(ParseMode::Markdown),
|
||||
reply_to_message_id: Some(reply_to_id),
|
||||
reply_markup: Some(ReplyMarkup::InlineKeyboardMarkup(
|
||||
utils::alternate_feedback_keyboard(&bundle),
|
||||
)),
|
||||
reply_markup: Some(ReplyMarkup::InlineKeyboardMarkup(alternate)),
|
||||
};
|
||||
|
||||
let sent = match self.bot.make_request(&send_message).await {
|
||||
|
@ -868,9 +895,17 @@ impl MessageHandler {
|
|||
.map(|item| (item.0, item.1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let bundle = self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
|
||||
let (updated_text, _used_hashes) = utils::build_alternate_response(&bundle, items);
|
||||
let ((updated_text, _used_hashes), alternate) = self
|
||||
.get_fluent_bundle(
|
||||
message.from.clone().unwrap().language_code.as_deref(),
|
||||
|bundle| {
|
||||
(
|
||||
utils::build_alternate_response(&bundle, items),
|
||||
utils::alternate_feedback_keyboard(&bundle),
|
||||
)
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
completed.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
|
@ -884,9 +919,7 @@ impl MessageHandler {
|
|||
text: updated_text,
|
||||
disable_web_page_preview: Some(true),
|
||||
parse_mode: Some(ParseMode::Markdown),
|
||||
reply_markup: Some(ReplyMarkup::InlineKeyboardMarkup(
|
||||
utils::alternate_feedback_keyboard(&bundle),
|
||||
)),
|
||||
reply_markup: Some(ReplyMarkup::InlineKeyboardMarkup(alternate)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -898,17 +931,22 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_generic_reply(&mut self, message: &Message, name: &str) {
|
||||
async fn send_generic_reply(&self, message: &Message, name: &str) {
|
||||
let language_code = match &message.from {
|
||||
Some(from) => &from.language_code,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let bundle = self.get_fluent_bundle(language_code.as_deref());
|
||||
let text = self
|
||||
.get_fluent_bundle(language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, name, None).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
text: utils::get_message(&bundle, name, None).unwrap(),
|
||||
text,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -920,7 +958,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_mirror(&mut self, message: Message) {
|
||||
async fn handle_mirror(&self, message: Message) {
|
||||
let from = message.from.clone().unwrap();
|
||||
|
||||
let completed = Arc::new(AtomicBool::new(false));
|
||||
|
@ -952,10 +990,13 @@ impl MessageHandler {
|
|||
|
||||
let mut results: Vec<PostInfo> = Vec::with_capacity(links.len());
|
||||
|
||||
let missing = utils::find_images(&from, links, &mut self.sites, &mut |info| {
|
||||
results.extend(info.results);
|
||||
})
|
||||
.await;
|
||||
let missing = {
|
||||
let mut sites = self.sites.lock().await;
|
||||
utils::find_images(&from, links, &mut sites, &mut |info| {
|
||||
results.extend(info.results);
|
||||
})
|
||||
.await
|
||||
};
|
||||
|
||||
if results.is_empty() {
|
||||
self.send_generic_reply(&message, "mirror-no-results").await;
|
||||
|
@ -1030,15 +1071,20 @@ impl MessageHandler {
|
|||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
let links: Vec<String> = missing.iter().map(|item| format!("· {}", item)).collect();
|
||||
let mut args = fluent::FluentArgs::new();
|
||||
args.insert("links", fluent::FluentValue::from(links.join("\n")));
|
||||
|
||||
let text = self
|
||||
.get_fluent_bundle(from.language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, "mirror-missing", Some(args)).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
reply_to_message_id: Some(reply_to_id),
|
||||
text: utils::get_message(&bundle, "mirror-missing", Some(args)).unwrap(),
|
||||
text,
|
||||
disable_web_page_preview: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -1052,7 +1098,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_text(&mut self, message: Message) {
|
||||
async fn handle_text(&self, message: Message) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let text = message.text.clone().unwrap(); // only here because this existed
|
||||
|
@ -1065,9 +1111,13 @@ impl MessageHandler {
|
|||
|
||||
log::trace!("Checking if message was Twitter code");
|
||||
|
||||
let data: (String, String) = match self.db.get(&format!("authenticate:{}", from.id)) {
|
||||
Some(data) => data,
|
||||
None => return,
|
||||
let data: (String, String) = {
|
||||
let lock = self.db.read().await;
|
||||
|
||||
match lock.get(&format!("authenticate:{}", from.id)) {
|
||||
Some(data) => data,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!("We had waiting Twitter code");
|
||||
|
@ -1100,34 +1150,42 @@ impl MessageHandler {
|
|||
|
||||
log::trace!("Got access token");
|
||||
|
||||
if let Err(e) = self.db.set(
|
||||
&format!("credentials:{}", from.id),
|
||||
&(access.key, access.secret),
|
||||
) {
|
||||
log::warn!("Unable to save user credentials: {:?}", e);
|
||||
{
|
||||
let mut lock = self.db.write().await;
|
||||
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter_auth".to_string())]),
|
||||
|| {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
if let Err(e) = lock.set(
|
||||
&format!("credentials:{}", from.id),
|
||||
&(access.key, access.secret),
|
||||
) {
|
||||
log::warn!("Unable to save user credentials: {:?}", e);
|
||||
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter_auth".to_string())]),
|
||||
|| {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = fluent::FluentArgs::new();
|
||||
args.insert("userName", fluent::FluentValue::from(token.2));
|
||||
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
let text = self
|
||||
.get_fluent_bundle(from.language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, "twitter-welcome", Some(args)).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let message = SendMessage {
|
||||
chat_id: from.id.into(),
|
||||
text: utils::get_message(&bundle, "twitter-welcome", Some(args)).unwrap(),
|
||||
text,
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -1151,7 +1209,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_message(&mut self, update: Update) {
|
||||
async fn handle_message(&self, update: Update) {
|
||||
if let Some(inline) = update.inline_query {
|
||||
self.handle_inline(inline).await;
|
||||
} else if let Some(message) = update.message {
|
||||
|
@ -1182,7 +1240,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_callback(&mut self, callback_data: CallbackQuery) {
|
||||
async fn handle_callback(&self, callback_data: CallbackQuery) {
|
||||
let mut answer = AnswerCallbackQuery {
|
||||
callback_query_id: callback_data.id,
|
||||
..Default::default()
|
||||
|
@ -1225,7 +1283,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn authenticate_twitter(&mut self, message: Message) {
|
||||
async fn authenticate_twitter(&self, message: Message) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
if message.chat.chat_type != ChatType::Private {
|
||||
|
@ -1258,24 +1316,28 @@ impl MessageHandler {
|
|||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self.db.set(
|
||||
&format!("authenticate:{}", user.id),
|
||||
&(request_token.key.clone(), request_token.secret.clone()),
|
||||
) {
|
||||
log::warn!("Unable to save authenticate: {:?}", e);
|
||||
{
|
||||
let mut lock = self.db.write().await;
|
||||
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter".to_string())]),
|
||||
|| {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
if let Err(e) = lock.set(
|
||||
&format!("authenticate:{}", user.id),
|
||||
&(request_token.key.clone(), request_token.secret.clone()),
|
||||
) {
|
||||
log::warn!("Unable to save authenticate: {:?}", e);
|
||||
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter".to_string())]),
|
||||
|| {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let url = egg_mode::authorize_url(&request_token);
|
||||
|
@ -1283,11 +1345,15 @@ impl MessageHandler {
|
|||
let mut args = fluent::FluentArgs::new();
|
||||
args.insert("link", fluent::FluentValue::from(url));
|
||||
|
||||
let bundle = self.get_fluent_bundle(user.language_code.as_deref());
|
||||
let text = self
|
||||
.get_fluent_bundle(user.language_code.as_deref(), |bundle| {
|
||||
utils::get_message(&bundle, "twitter-oob", Some(args)).unwrap()
|
||||
})
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
text: utils::get_message(&bundle, "twitter-oob", Some(args)).unwrap(),
|
||||
text,
|
||||
reply_markup: Some(ReplyMarkup::ForceReply(ForceReply::selective())),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
|
@ -1312,11 +1378,22 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_result(&mut self, result: &PostInfo, from: &User) -> Option<Vec<InlineQueryResult>> {
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
async fn process_result(
|
||||
&self,
|
||||
result: &PostInfo,
|
||||
from: &User,
|
||||
) -> Option<Vec<InlineQueryResult>> {
|
||||
let (direct, source) = self
|
||||
.get_fluent_bundle(from.language_code.as_deref(), |bundle| {
|
||||
(
|
||||
utils::get_message(&bundle, "inline-direct", None).unwrap(),
|
||||
utils::get_message(&bundle, "inline-source", None).unwrap(),
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut row = vec![InlineKeyboardButton {
|
||||
text: utils::get_message(&bundle, "inline-direct", None).unwrap(),
|
||||
text: direct,
|
||||
url: Some(result.url.clone()),
|
||||
callback_data: None,
|
||||
..Default::default()
|
||||
|
@ -1324,7 +1401,7 @@ impl MessageHandler {
|
|||
|
||||
if let Some(source_link) = &result.source_link {
|
||||
row.push(InlineKeyboardButton {
|
||||
text: utils::get_message(&bundle, "inline-source", None).unwrap(),
|
||||
text: source,
|
||||
url: Some(source_link.clone()),
|
||||
callback_data: None,
|
||||
..Default::default()
|
||||
|
@ -1415,7 +1492,7 @@ impl MessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn process_photo(&mut self, message: Message) {
|
||||
async fn process_photo(&self, message: Message) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
if message.chat.chat_type != ChatType::Private {
|
||||
|
@ -1450,12 +1527,16 @@ impl MessageHandler {
|
|||
{
|
||||
Ok(matches) if !matches.is_empty() => matches,
|
||||
Ok(_matches) => {
|
||||
let bundle =
|
||||
self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
let text = self
|
||||
.get_fluent_bundle(
|
||||
message.from.clone().unwrap().language_code.as_deref(),
|
||||
|bundle| utils::get_message(&bundle, "reverse-no-results", None).unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
text: utils::get_message(&bundle, "reverse-no-results", None).unwrap(),
|
||||
text,
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -1495,8 +1576,6 @@ impl MessageHandler {
|
|||
let first = matches.get(0).unwrap();
|
||||
log::debug!("Match has distance of {}", first.distance);
|
||||
|
||||
let bundle = self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
|
||||
let name = if first.distance < 5 {
|
||||
"reverse-good-result"
|
||||
} else {
|
||||
|
@ -1510,9 +1589,16 @@ impl MessageHandler {
|
|||
fluent::FluentValue::from(format!("https://www.furaffinity.net/view/{}/", first.id)),
|
||||
);
|
||||
|
||||
let text = self
|
||||
.get_fluent_bundle(
|
||||
message.from.clone().unwrap().language_code.as_deref(),
|
||||
|bundle| utils::get_message(&bundle, name, Some(args)).unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
text: utils::get_message(&bundle, name, Some(args)).unwrap(),
|
||||
text,
|
||||
disable_web_page_preview: Some(first.distance > 5),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
|
|
|
@ -11,7 +11,7 @@ const USER_AGENT: &str = concat!(
|
|||
" developed by @Syfaro"
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PostInfo {
|
||||
/// File type, as a standard file extension (png, jpg, etc.)
|
||||
pub file_type: String,
|
||||
|
|
|
@ -544,6 +544,8 @@ pub struct EditMessageText {
|
|||
pub chat_id: ChatID,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub message_id: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub inline_message_id: Option<String>,
|
||||
pub text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub parse_mode: Option<ParseMode>,
|
||||
|
|
Loading…
Reference in New Issue