Significant improvement in error handling.
This commit is contained in:
parent
d8067e3e54
commit
bce2144ff6
|
@ -535,6 +535,7 @@ dependencies = [
|
|||
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unic-langid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -19,6 +19,7 @@ hyper = "0.13.1"
|
|||
log = "0.4.8"
|
||||
pretty_env_logger = "0.3.1"
|
||||
sentry = "0.17.0"
|
||||
uuid = "0.7.4"
|
||||
failure = "0.1.6"
|
||||
influxdb = { git = "https://github.com/Empty2k12/influxdb-rust.git", rev = "1b0cae12d8b1b0e2bd29129e6757a9df8a8faf50" }
|
||||
|
||||
|
|
|
@ -36,3 +36,4 @@ Env Name | Description
|
|||
`INFLUX_USER` | InfluxDB username
|
||||
`INFLUX_PASS` | InfluxDB password
|
||||
`USE_PROXY` | If should use images.weserv.nl to proxy images
|
||||
`SENTRY_DSN` | Sentry DSN to report errors
|
||||
|
|
|
@ -45,4 +45,5 @@ mirror-missing =
|
|||
source-no-photo = Sorry, I can't seem to find the photo here.
|
||||
|
||||
# Error Messages
|
||||
error-generic = Something went wrong, please try again later.
|
||||
error-generic = Oh no, something went wrong! Please send a message to my creator { -creatorName } if you continue having issues.
|
||||
error-uuid = Oh no, something went wrong! Please send a message to my creator { -creatorName } with this ID if you continue having issues: { $uuid }
|
||||
|
|
295
src/main.rs
295
src/main.rs
|
@ -333,12 +333,11 @@ fn get_message(
|
|||
}
|
||||
}
|
||||
|
||||
fn with_user_scope<C>(
|
||||
from: Option<&User>,
|
||||
tags: Option<std::collections::HashMap<&'static str, String>>,
|
||||
callback: C,
|
||||
) where
|
||||
C: FnOnce(),
|
||||
type SentryTags<'a> = Option<Vec<(&'a str, String)>>;
|
||||
|
||||
fn with_user_scope<C, R>(from: Option<&User>, tags: SentryTags, callback: C) -> R
|
||||
where
|
||||
C: FnOnce() -> R,
|
||||
{
|
||||
sentry::with_scope(
|
||||
|scope| {
|
||||
|
@ -357,7 +356,7 @@ fn with_user_scope<C>(
|
|||
}
|
||||
},
|
||||
callback,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
impl MessageHandler {
|
||||
|
@ -414,12 +413,58 @@ impl MessageHandler {
|
|||
.expect("Unable to add resource");
|
||||
}
|
||||
|
||||
bundle.set_use_isolating(false);
|
||||
|
||||
self.best_lang.insert(requested.to_string(), bundle);
|
||||
self.best_lang
|
||||
.get(requested)
|
||||
.expect("Value just inserted is missing")
|
||||
}
|
||||
|
||||
async fn report_error<C>(
|
||||
&mut self,
|
||||
message: &Message,
|
||||
tags: Option<Vec<(&str, String)>>,
|
||||
callback: C,
|
||||
) where
|
||||
C: FnOnce() -> uuid::Uuid,
|
||||
{
|
||||
let u = with_user_scope(message.from.as_ref(), tags, callback);
|
||||
|
||||
let lang_code = message
|
||||
.from
|
||||
.clone()
|
||||
.map(|from| from.language_code)
|
||||
.flatten();
|
||||
let bundle = self.get_fluent_bundle(lang_code.as_deref());
|
||||
|
||||
let msg = if u.is_nil() {
|
||||
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));
|
||||
|
||||
get_message(&bundle, "error-uuid", Some(args))
|
||||
};
|
||||
|
||||
let send_message = SendMessage {
|
||||
chat_id: message.chat_id(),
|
||||
text: msg.unwrap(),
|
||||
parse_mode: Some(ParseMode::Markdown),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&send_message).await {
|
||||
log::error!("Unable to send error message to user: {:?}", e);
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_inline(&mut self, inline: InlineQuery) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
|
@ -441,19 +486,14 @@ impl MessageHandler {
|
|||
let images = match site.get_images(inline.from.id, link_str).await {
|
||||
Ok(images) => images,
|
||||
Err(e) => {
|
||||
log::warn!("Unable to get image: {:?}", e);
|
||||
with_user_scope(
|
||||
Some(&inline.from),
|
||||
Some(
|
||||
[("site", site.name().to_string())]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
Some(vec![("site", site.name().to_string())]),
|
||||
|| {
|
||||
capture_fail(&e);
|
||||
},
|
||||
);
|
||||
log::warn!("Unable to get image: {:?}", e);
|
||||
continue 'link;
|
||||
}
|
||||
};
|
||||
|
@ -517,10 +557,8 @@ impl MessageHandler {
|
|||
|
||||
point = point.add_field("duration", now.elapsed().as_millis() as i64);
|
||||
if let Err(e) = self.influx.query(&point).await {
|
||||
with_user_scope(Some(&inline.from), None, || {
|
||||
let uuid = capture_fail(&e);
|
||||
log::error!("[{}] Unable to log inline to InfluxDB: {:?}", uuid, e);
|
||||
});
|
||||
log::error!("Unable to log inline to InfluxDB: {:?}", e);
|
||||
with_user_scope(Some(&inline.from), None, || capture_fail(&e));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -587,18 +625,11 @@ impl MessageHandler {
|
|||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&send_message).await {
|
||||
log::error!("Unable to send help message: {:?}", e);
|
||||
with_user_scope(
|
||||
message.from.as_ref(),
|
||||
Some(
|
||||
[("command", command_text.to_string())]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
|| {
|
||||
let uuid = capture_fail(&e);
|
||||
log::error!("[{}] Unable to send help message: {:?}", uuid, e);
|
||||
},
|
||||
Some(vec![("command", command_text.to_string())]),
|
||||
|| capture_fail(&e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -613,10 +644,10 @@ impl MessageHandler {
|
|||
.add_field("duration", now.elapsed().as_millis() as i64);
|
||||
|
||||
if let Err(e) = self.influx.query(&point).await {
|
||||
log::error!("Unable to send command to InfluxDB: {:?}", e);
|
||||
with_user_scope(from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
log::error!("Unable to send command to InfluxDB: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,10 +682,10 @@ impl MessageHandler {
|
|||
})
|
||||
.for_each(|_| async {
|
||||
if let Err(e) = bot.make_request(&chat_action).await {
|
||||
log::warn!("Unable to send chat action: {:?}", e);
|
||||
with_user_scope(user.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
log::warn!("Unable to send chat action: {:?}", e);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
@ -697,11 +728,13 @@ impl MessageHandler {
|
|||
let file = match self.bot.make_request(&get_file).await {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
log::error!("Unable to get file: {:?}", e);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "source".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -709,11 +742,13 @@ impl MessageHandler {
|
|||
let bytes = match self.bot.download_file(file.file_path.unwrap()).await {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
log::error!("Unable to download file: {:?}", e);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "source".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -721,11 +756,13 @@ impl MessageHandler {
|
|||
let matches = match self.fapi.image_search(bytes, false).await {
|
||||
Ok(matches) => matches,
|
||||
Err(e) => {
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
log::error!("Unable to find matches: {:?}", e);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "source".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -888,9 +925,13 @@ impl MessageHandler {
|
|||
|
||||
if let Err(e) = self.bot.make_request(&photo).await {
|
||||
log::error!("Unable to make request: {:?}", e);
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "mirror".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for chunk in results.chunks(10) {
|
||||
|
@ -918,9 +959,13 @@ impl MessageHandler {
|
|||
|
||||
if let Err(e) = self.bot.make_request(&media_group).await {
|
||||
log::error!("Unable to make request: {:?}", e);
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "mirror".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -953,8 +998,8 @@ impl MessageHandler {
|
|||
async fn handle_text(&mut self, message: Message) {
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
let text = message.text.unwrap(); // only here because this existed
|
||||
let from = message.from.unwrap();
|
||||
let text = message.text.clone().unwrap(); // only here because this existed
|
||||
let from = message.from.clone().unwrap();
|
||||
|
||||
if text.trim().parse::<i32>().is_err() {
|
||||
log::trace!("Got text that wasn't OOB, ignoring");
|
||||
|
@ -978,18 +1023,12 @@ impl MessageHandler {
|
|||
Err(e) => {
|
||||
log::warn!("User was unable to verify OOB: {:?}", e);
|
||||
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
|
||||
let message = SendMessage {
|
||||
chat_id: from.id.into(),
|
||||
text: get_message(&bundle, "error-generic", None).unwrap(),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&message).await {
|
||||
log::warn!("Unable to send message: {:?}", e);
|
||||
}
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter_auth".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
Ok(token) => token,
|
||||
|
@ -1010,28 +1049,17 @@ impl MessageHandler {
|
|||
) {
|
||||
log::warn!("Unable to save user credentials: {:?}", e);
|
||||
|
||||
with_user_scope(Some(&from), None, || {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
));
|
||||
});
|
||||
|
||||
let bundle = self.get_fluent_bundle(from.language_code.as_deref());
|
||||
|
||||
let message = SendMessage {
|
||||
chat_id: from.id.into(),
|
||||
text: get_message(&bundle, "error-generic", None).unwrap(),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&message).await {
|
||||
log::warn!("Unable to send message: {:?}", e);
|
||||
with_user_scope(Some(&from), None, || {
|
||||
capture_fail(&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;
|
||||
}
|
||||
|
||||
|
@ -1106,25 +1134,17 @@ impl MessageHandler {
|
|||
Err(e) => {
|
||||
log::warn!("Unable to get request token: {:?}", e);
|
||||
|
||||
with_user_scope(Some(&user), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
|
||||
let bundle = self.get_fluent_bundle(user.language_code.as_deref());
|
||||
|
||||
let message = SendMessage {
|
||||
chat_id: user.id.into(),
|
||||
text: get_message(&bundle, "error-generic", None).unwrap(),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&message).await {
|
||||
log::warn!("Unable to send message: {:?}", e);
|
||||
with_user_scope(Some(&user), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
}
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "twitter".to_string())]),
|
||||
|| {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to get request token: {}",
|
||||
e
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -1135,28 +1155,17 @@ impl MessageHandler {
|
|||
) {
|
||||
log::warn!("Unable to save authenticate: {:?}", e);
|
||||
|
||||
with_user_scope(Some(&user), None, || {
|
||||
sentry::integrations::failure::capture_error(&format_err!(
|
||||
"Unable to save to Twitter database: {}",
|
||||
e
|
||||
));
|
||||
});
|
||||
|
||||
let bundle = self.get_fluent_bundle(user.language_code.as_deref());
|
||||
|
||||
let message = SendMessage {
|
||||
chat_id: user.id.into(),
|
||||
text: get_message(&bundle, "error-generic", None).unwrap(),
|
||||
reply_to_message_id: Some(message.message_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Err(e) = self.bot.make_request(&message).await {
|
||||
log::warn!("Unable to send message: {:?}", e);
|
||||
with_user_scope(Some(&user), None, || {
|
||||
capture_fail(&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;
|
||||
}
|
||||
|
||||
|
@ -1325,10 +1334,13 @@ impl MessageHandler {
|
|||
let file = match self.bot.make_request(&get_file).await {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
log::error!("Unable to get file: {:?}", e);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "photo".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -1336,16 +1348,30 @@ impl MessageHandler {
|
|||
let photo = match self.bot.download_file(file.file_path.unwrap()).await {
|
||||
Ok(photo) => photo,
|
||||
Err(e) => {
|
||||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
log::error!("Unable to download file: {:?}", e);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "photo".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let matches = match self.fapi.image_search(photo, false).await {
|
||||
Ok(matches) if !matches.is_empty() => matches,
|
||||
Err(e) => {
|
||||
log::error!("Unable to reverse search image file: {:?}", e);
|
||||
completed.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
self.report_error(
|
||||
&message,
|
||||
Some(vec![("command", "photo".to_string())]),
|
||||
|| capture_fail(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
let bundle =
|
||||
self.get_fluent_bundle(message.from.clone().unwrap().language_code.as_deref());
|
||||
|
@ -1376,7 +1402,6 @@ impl MessageHandler {
|
|||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1416,7 +1441,6 @@ impl MessageHandler {
|
|||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
}
|
||||
|
||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "source")
|
||||
|
@ -1429,7 +1453,6 @@ impl MessageHandler {
|
|||
with_user_scope(message.from.as_ref(), None, || {
|
||||
capture_fail(&e);
|
||||
});
|
||||
self.send_generic_reply(&message, "error-generic").await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,6 +375,14 @@ pub enum ReplyMarkup {
|
|||
ForceReply(ForceReply),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum ParseMode {
|
||||
HTML,
|
||||
Markdown,
|
||||
MarkdownV2,
|
||||
}
|
||||
|
||||
/// SendMessage sends a message.
|
||||
#[derive(Serialize, Default, Debug)]
|
||||
pub struct SendMessage {
|
||||
|
@ -382,6 +390,9 @@ pub struct SendMessage {
|
|||
pub chat_id: ChatID,
|
||||
/// The text of the message.
|
||||
pub text: String,
|
||||
/// The mode used to parse the provided text.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub parse_mode: Option<ParseMode>,
|
||||
/// The ID of the [Message] this Message is in reply to.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reply_to_message_id: Option<i32>,
|
||||
|
|
Loading…
Reference in New Issue