108 lines
3.2 KiB
Rust
108 lines
3.2 KiB
Rust
use crate::util::requester;
|
|
use dashmap::DashMap;
|
|
use once_cell::sync::Lazy;
|
|
use serde_json::json;
|
|
use std::error::Error;
|
|
use std::fmt::Write as _;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
// Global cache: Map<ErrorSignature, LastSentTimestamp>
|
|
static ERROR_CACHE: Lazy<DashMap<String, u64>> = Lazy::new(DashMap::new);
|
|
// const COOLDOWN_SECONDS: u64 = 3600; // 1 Hour cooldown
|
|
|
|
pub fn format_error_chain(err: &dyn Error) -> String {
|
|
let mut chain_str = String::new();
|
|
let mut current_err: Option<&dyn Error> = Some(err);
|
|
let mut index = 1;
|
|
while let Some(e) = current_err {
|
|
let _ = writeln!(chain_str, "{}. {}", index, e);
|
|
current_err = e.source();
|
|
index += 1;
|
|
}
|
|
chain_str
|
|
}
|
|
|
|
pub async fn send_discord_error_report(
|
|
error_msg: String,
|
|
error_chain: Option<String>,
|
|
context: Option<&str>,
|
|
extra_info: Option<&str>,
|
|
file: &str,
|
|
line: u32,
|
|
module: &str,
|
|
) {
|
|
let now = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map(|d| d.as_secs())
|
|
.unwrap_or(0);
|
|
|
|
// --- Deduplication Logic ---
|
|
// Create a unique key based on error content and location
|
|
let error_signature = format!("{}-{}-{}", error_msg, file, line);
|
|
|
|
if let Some(_) = ERROR_CACHE.get(&error_signature) {
|
|
// if now - *last_sent < COOLDOWN_SECONDS {
|
|
// Error is still in cooldown, skip sending
|
|
return;
|
|
// }
|
|
}
|
|
|
|
// Update the cache with the current timestamp
|
|
ERROR_CACHE.insert(error_signature, now);
|
|
// ---------------------------
|
|
|
|
let webhook_url = match std::env::var("DISCORD_WEBHOOK") {
|
|
Ok(url) => url,
|
|
Err(_) => return,
|
|
};
|
|
|
|
const MAX_FIELD: usize = 1024;
|
|
let truncate = |s: &str| {
|
|
if s.len() > MAX_FIELD {
|
|
format!("{}…", &s[..MAX_FIELD - 1])
|
|
} else {
|
|
s.to_string()
|
|
}
|
|
};
|
|
|
|
let payload = json!({
|
|
"embeds": [{
|
|
"title": "🚨 Rust Error Report",
|
|
"color": 0xE74C3C,
|
|
"fields": [
|
|
{
|
|
"name": "Error",
|
|
"value": format!("```{}```", truncate(&error_msg)),
|
|
"inline": false
|
|
},
|
|
{
|
|
"name": "Error Chain",
|
|
"value": truncate(&error_chain.unwrap_or_else(|| "No chain provided".to_string())),
|
|
"inline": false
|
|
},
|
|
{
|
|
"name": "Location",
|
|
"value": format!("`{}`:{}\n`{}`", file, line, module),
|
|
"inline": true
|
|
},
|
|
{
|
|
"name": "Context",
|
|
"value": truncate(context.unwrap_or("n/a")),
|
|
"inline": true
|
|
},
|
|
{
|
|
"name": "Extra Info",
|
|
"value": truncate(extra_info.unwrap_or("n/a")),
|
|
"inline": false
|
|
}
|
|
],
|
|
"footer": {
|
|
"text": format!("Unix time: {} | Cooldown active", now)
|
|
}
|
|
}]
|
|
});
|
|
|
|
let mut requester = requester::Requester::new();
|
|
let _ = requester.post_json(&webhook_url, &payload, vec![]).await;
|
|
}
|