Drone improvements, reply for Sentry feedback.
This commit is contained in:
parent
9e25205405
commit
1404d09436
|
@ -14,7 +14,9 @@ steps:
|
||||||
- apt-get update -y
|
- apt-get update -y
|
||||||
- apt-get install pkg-config libssl-dev ca-certificates python3 python3-pip nodejs -y
|
- apt-get install pkg-config libssl-dev ca-certificates python3 python3-pip nodejs -y
|
||||||
- pip3 install cfscrape
|
- pip3 install cfscrape
|
||||||
|
- rustup component add clippy
|
||||||
- cargo test
|
- cargo test
|
||||||
|
- cargo clippy
|
||||||
|
|
||||||
- name: sentry-release
|
- name: sentry-release
|
||||||
image: getsentry/sentry-cli
|
image: getsentry/sentry-cli
|
||||||
|
@ -35,6 +37,8 @@ steps:
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
auto_tag: true
|
auto_tag: true
|
||||||
|
build_args_from_env:
|
||||||
|
- DRONE_COMMIT_SHA
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
registry: registry.huefox.com
|
registry: registry.huefox.com
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
FROM rustlang/rust:nightly-slim AS builder
|
FROM rustlang/rust:nightly-slim AS builder
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
RUN apt-get update -y && apt-get install pkg-config libssl-dev python3 python3-dev -y
|
RUN apt-get update -y && apt-get install pkg-config libssl-dev python3 python3-dev -y
|
||||||
|
ARG DRONE_COMMIT_SHA
|
||||||
|
ENV RELEASE $DRONE_COMMIT_SHA
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo install --root / --path .
|
RUN cargo install --root / --path .
|
||||||
|
|
||||||
|
|
|
@ -62,5 +62,6 @@ alternate-distance = · { $link } (distance of { $distance })
|
||||||
alternate-multiple-photo = I can only find alternates for a single photo, sorry.
|
alternate-multiple-photo = I can only find alternates for a single photo, sorry.
|
||||||
|
|
||||||
# Error Messages
|
# Error Messages
|
||||||
error-generic = Oh no, something went wrong! Please send a message to my creator { -creatorName } if you continue having issues.
|
error-generic = Oh no, something went wrong! Please send a message to my creator, { -creatorName }, saying what happened.
|
||||||
error-uuid = Oh no, something went wrong! Please send a message to my creator { -creatorName } with this ID if you continue having issues: { $uuid }
|
error-uuid = Oh no, something went wrong! Please reply to this message saying what happened. You may also send a message to my creator, { -creatorName }, with this ID if you continue having issues: { $uuid }
|
||||||
|
error-feedback = Thank you for the feedback, hopefully we can get this issue resolved soon.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit dc628de3342203b71cdedd4ff3c8a86eb7acefa4
|
|
@ -1,4 +1,5 @@
|
||||||
use super::Status::*;
|
use super::Status::*;
|
||||||
|
use crate::needs_field;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use telegram::*;
|
use telegram::*;
|
||||||
|
|
||||||
|
@ -16,15 +17,13 @@ impl super::Handler for ChosenInlineHandler {
|
||||||
update: &Update,
|
update: &Update,
|
||||||
_command: Option<&Command>,
|
_command: Option<&Command>,
|
||||||
) -> Result<super::Status, failure::Error> {
|
) -> Result<super::Status, failure::Error> {
|
||||||
Ok(if let Some(chosen_result) = &update.chosen_inline_result {
|
let chosen_result = needs_field!(update, chosen_inline_result);
|
||||||
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "chosen")
|
|
||||||
.add_field("user_id", chosen_result.from.id);
|
|
||||||
|
|
||||||
let _ = handler.influx.query(&point).await;
|
let point = influxdb::Query::write_query(influxdb::Timestamp::Now, "chosen")
|
||||||
|
.add_field("user_id", chosen_result.from.id);
|
||||||
|
|
||||||
Completed
|
let _ = handler.influx.query(&point).await;
|
||||||
} else {
|
|
||||||
Ignored
|
Ok(Completed)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ impl super::Handler for CommandHandler {
|
||||||
"/mirror" => self.handle_mirror(&handler, message).await,
|
"/mirror" => self.handle_mirror(&handler, message).await,
|
||||||
"/source" => self.handle_source(&handler, message).await,
|
"/source" => self.handle_source(&handler, message).await,
|
||||||
"/alts" => self.handle_alts(&handler, message).await,
|
"/alts" => self.handle_alts(&handler, message).await,
|
||||||
|
"/error" => Err(failure::format_err!("a test error message")),
|
||||||
_ => {
|
_ => {
|
||||||
tracing::info!("unknown command: {}", command.name);
|
tracing::info!("unknown command: {}", command.name);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
use super::Status::*;
|
||||||
|
use crate::needs_field;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use telegram::*;
|
||||||
|
|
||||||
|
pub struct ErrorReplyHandler {
|
||||||
|
client: reqwest::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorReplyHandler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
client: reqwest::Client::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::Handler for ErrorReplyHandler {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"error_reply"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(
|
||||||
|
&self,
|
||||||
|
handler: &crate::MessageHandler,
|
||||||
|
update: &Update,
|
||||||
|
_command: Option<&Command>,
|
||||||
|
) -> Result<super::Status, failure::Error> {
|
||||||
|
let message = needs_field!(update, message);
|
||||||
|
let text = needs_field!(message, text);
|
||||||
|
let reply_message = needs_field!(message, reply_to_message);
|
||||||
|
let reply_message_from = needs_field!(reply_message, from);
|
||||||
|
let reply_message_text = needs_field!(reply_message, text);
|
||||||
|
let entities = needs_field!(reply_message, entities);
|
||||||
|
|
||||||
|
// Only want to look at messages that are replies to this bot
|
||||||
|
if reply_message_from.id != handler.bot_user.id {
|
||||||
|
return Ok(Ignored);
|
||||||
|
}
|
||||||
|
|
||||||
|
let code = match get_code_block(&entities, &reply_message_text) {
|
||||||
|
Some(code) => code,
|
||||||
|
_ => return Ok(Ignored),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dsn = match &handler.config.sentry_dsn {
|
||||||
|
Some(dsn) => dsn,
|
||||||
|
_ => return Ok(Completed),
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth = format!("DSN {}", dsn);
|
||||||
|
|
||||||
|
let data = SentryFeedback {
|
||||||
|
comments: text.to_string(),
|
||||||
|
event_id: code,
|
||||||
|
// This field is required, but Telegram doesn't give us emails...
|
||||||
|
email: "telegram-user@example.com".to_string(),
|
||||||
|
name: message
|
||||||
|
.from
|
||||||
|
.as_ref()
|
||||||
|
.map(|from| from.username.clone().unwrap_or_else(|| from.id.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.post(&format!(
|
||||||
|
"https://sentry.io/api/0/projects/{}/{}/user-feedback/",
|
||||||
|
handler.config.sentry_organization_slug.as_ref().unwrap(),
|
||||||
|
handler.config.sentry_project_slug.as_ref().unwrap()
|
||||||
|
))
|
||||||
|
.json(&data)
|
||||||
|
.header(reqwest::header::AUTHORIZATION, auth)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
handler
|
||||||
|
.send_generic_reply(&message, "error-feedback")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Completed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct SentryFeedback {
|
||||||
|
comments: String,
|
||||||
|
event_id: String,
|
||||||
|
name: Option<String>,
|
||||||
|
email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_code_block(entities: &[MessageEntity], text: &str) -> Option<String> {
|
||||||
|
// Find any code blocks, ignore if there's more than one
|
||||||
|
let code_blocks = entities
|
||||||
|
.iter()
|
||||||
|
.filter(|entity| entity.entity_type == MessageEntityType::Code)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if code_blocks.len() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the code block is the correct length
|
||||||
|
let entity = code_blocks[0];
|
||||||
|
if entity.length != 36 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate the text of the message this is replying to in order to
|
||||||
|
// get the event ID
|
||||||
|
let code = text
|
||||||
|
.chars()
|
||||||
|
.skip(entity.offset as usize)
|
||||||
|
.take(entity.length as usize)
|
||||||
|
.filter(|c| *c != '-')
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn test_get_code_block() {
|
||||||
|
let entities = vec![telegram::MessageEntity {
|
||||||
|
entity_type: telegram::MessageEntityType::Code,
|
||||||
|
offset: 0,
|
||||||
|
length: 36,
|
||||||
|
url: None,
|
||||||
|
user: None,
|
||||||
|
}];
|
||||||
|
let text = "e52569fa-99a0-44fc-ae9d-2477177b550b";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Some("e52569fa99a044fcae9d2477177b550b".to_string()),
|
||||||
|
super::get_code_block(&entities, &text)
|
||||||
|
);
|
||||||
|
|
||||||
|
let entities = vec![];
|
||||||
|
assert_eq!(None, super::get_code_block(&entities, text));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use async_trait::async_trait;
|
||||||
mod channel_photo;
|
mod channel_photo;
|
||||||
mod chosen_inline_handler;
|
mod chosen_inline_handler;
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod error_reply;
|
||||||
mod group_add;
|
mod group_add;
|
||||||
mod inline_handler;
|
mod inline_handler;
|
||||||
mod photo;
|
mod photo;
|
||||||
|
@ -11,6 +12,7 @@ mod text;
|
||||||
pub use channel_photo::ChannelPhotoHandler;
|
pub use channel_photo::ChannelPhotoHandler;
|
||||||
pub use chosen_inline_handler::ChosenInlineHandler;
|
pub use chosen_inline_handler::ChosenInlineHandler;
|
||||||
pub use commands::CommandHandler;
|
pub use commands::CommandHandler;
|
||||||
|
pub use error_reply::ErrorReplyHandler;
|
||||||
pub use group_add::GroupAddHandler;
|
pub use group_add::GroupAddHandler;
|
||||||
pub use inline_handler::InlineHandler;
|
pub use inline_handler::InlineHandler;
|
||||||
pub use photo::PhotoHandler;
|
pub use photo::PhotoHandler;
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -67,7 +67,9 @@ pub struct Config {
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
jaeger_collector: Option<String>,
|
jaeger_collector: Option<String>,
|
||||||
sentry_dsn: Option<String>,
|
pub sentry_dsn: Option<String>,
|
||||||
|
pub sentry_organization_slug: Option<String>,
|
||||||
|
pub sentry_project_slug: Option<String>,
|
||||||
|
|
||||||
// Telegram config
|
// Telegram config
|
||||||
telegram_apitoken: String,
|
telegram_apitoken: String,
|
||||||
|
@ -236,6 +238,7 @@ async fn main() {
|
||||||
Box::new(handlers::PhotoHandler),
|
Box::new(handlers::PhotoHandler),
|
||||||
Box::new(handlers::CommandHandler),
|
Box::new(handlers::CommandHandler),
|
||||||
Box::new(handlers::TextHandler),
|
Box::new(handlers::TextHandler),
|
||||||
|
Box::new(handlers::ErrorReplyHandler::new()),
|
||||||
];
|
];
|
||||||
|
|
||||||
let handler = Arc::new(MessageHandler {
|
let handler = Arc::new(MessageHandler {
|
||||||
|
@ -256,7 +259,11 @@ async fn main() {
|
||||||
|
|
||||||
let _guard = if let Some(dsn) = config.sentry_dsn {
|
let _guard = if let Some(dsn) = config.sentry_dsn {
|
||||||
sentry::integrations::panic::register_panic_handler();
|
sentry::integrations::panic::register_panic_handler();
|
||||||
Some(sentry::init(dsn))
|
Some(sentry::init(sentry::ClientOptions {
|
||||||
|
dsn: Some(dsn.parse().unwrap()),
|
||||||
|
release: option_env!("RELEASE").map(std::borrow::Cow::from),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -432,7 +439,6 @@ pub struct MessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler {
|
impl MessageHandler {
|
||||||
#[tracing::instrument(skip(self, callback))]
|
|
||||||
async fn get_fluent_bundle<C, R>(&self, requested: Option<&str>, callback: C) -> R
|
async fn get_fluent_bundle<C, R>(&self, requested: Option<&str>, callback: C) -> R
|
||||||
where
|
where
|
||||||
C: FnOnce(&fluent::FluentBundle<fluent::FluentResource>) -> R,
|
C: FnOnce(&fluent::FluentBundle<fluent::FluentResource>) -> R,
|
||||||
|
@ -643,14 +649,23 @@ impl MessageHandler {
|
||||||
Ok(status) if status == handlers::Status::Completed => break,
|
Ok(status) if status == handlers::Status::Completed => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Error handling update: {:#?}", e);
|
log::error!("Error handling update: {:#?}", e);
|
||||||
|
|
||||||
let mut tags = vec![("handler", handler.name().to_string())];
|
let mut tags = vec![("handler", handler.name().to_string())];
|
||||||
if let Some(user) = user {
|
if let Some(user) = user {
|
||||||
tags.push(("user_id", user.id.to_string()));
|
tags.push(("user_id", user.id.to_string()));
|
||||||
}
|
}
|
||||||
|
if let Some(command) = command {
|
||||||
|
tags.push(("command", command.name));
|
||||||
|
}
|
||||||
|
|
||||||
utils::with_user_scope(user, Some(tags), || {
|
if let Some(msg) = &update.message {
|
||||||
|
self.report_error(&msg, Some(tags), || capture_error(&e))
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
capture_error(&e);
|
capture_error(&e);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue