Optimize several parts
This commit is contained in:
parent
55cbe57728
commit
452bd92eb1
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -1080,7 +1080,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kon"
|
||||
version = "0.6.10"
|
||||
version = "0.6.11"
|
||||
dependencies = [
|
||||
"kon_cmds",
|
||||
"kon_libs",
|
||||
@ -1091,7 +1091,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kon_cmds"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
dependencies = [
|
||||
"dashmap 6.1.0",
|
||||
"futures",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kon"
|
||||
version = "0.6.10"
|
||||
version = "0.6.11"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kon_cmds"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
@ -7,13 +7,23 @@ use {
|
||||
serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
},
|
||||
std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
LazyLock,
|
||||
RwLock
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static REQWEST_: LazyLock<reqwest::Client> = LazyLock::new(reqwest::Client::new);
|
||||
static LOCALE_CACHE: LazyLock<RwLock<HashMap<String, String>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct DeepLRequest {
|
||||
text: Vec<String>,
|
||||
target_lang: String
|
||||
target_lang: &'static str
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -21,12 +31,39 @@ struct DeepLResponse {
|
||||
translations: Vec<Translation>
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DeepLLanguage {
|
||||
language: String,
|
||||
name: String
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DeepLUsage {
|
||||
character_count: u64,
|
||||
character_limit: u64
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Translation {
|
||||
text: String,
|
||||
detected_source_language: String
|
||||
}
|
||||
|
||||
fn prettify_nums(num: u64) -> String {
|
||||
let mut s = String::new();
|
||||
let num_str = num.to_string();
|
||||
let len = num_str.len();
|
||||
|
||||
for (i, c) in num_str.chars().enumerate() {
|
||||
s.push(c);
|
||||
if (len - i - 1) % 3 == 0 && i < len - 1 {
|
||||
s.push(',');
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
/// Translate a given message using DeepL
|
||||
#[poise::command(
|
||||
context_menu_command = "Translate via DeepL",
|
||||
@ -55,6 +92,10 @@ pub async fn translate(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if LOCALE_CACHE.read().unwrap().is_empty() {
|
||||
update_locale_cache(&deepl_key).await.expect("Failed to update locale cache...");
|
||||
}
|
||||
|
||||
ctx.defer().await?;
|
||||
|
||||
let api_url = if deepl_key.ends_with(":fx") {
|
||||
@ -63,15 +104,14 @@ pub async fn translate(
|
||||
"https://api.deepl.com"
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let resp = match client
|
||||
let resp = match REQWEST_
|
||||
.post(format!("{api_url}/v2/translate"))
|
||||
.header("User-Agent", "kon/reqwest")
|
||||
.header("Authorization", format!("DeepL-Auth-Key {deepl_key}"))
|
||||
.header("Content-Type", "application/json")
|
||||
.json(&DeepLRequest {
|
||||
text: vec![content.to_string()],
|
||||
target_lang: "EN".to_string()
|
||||
text: vec![content.to_owned()],
|
||||
target_lang: "EN"
|
||||
})
|
||||
.send()
|
||||
.await
|
||||
@ -142,11 +182,17 @@ pub async fn translate(
|
||||
};
|
||||
|
||||
if let Some(translation) = translation.translations.first() {
|
||||
let quota_info = match get_quota(&deepl_key).await {
|
||||
Ok(u) => &format!("-# **Quota: {}/{}**", prettify_nums(u.character_count), prettify_nums(u.character_limit)),
|
||||
Err(_) => "-# *Failed to check the quota!*"
|
||||
};
|
||||
|
||||
ctx
|
||||
.send(
|
||||
CreateReply::new().content(
|
||||
[
|
||||
format!("**Translated from {}**", prettify_lang(translation.detected_source_language.as_str())),
|
||||
quota_info.to_string(),
|
||||
format!("```\n{}\n```", translation.text)
|
||||
]
|
||||
.join("\n")
|
||||
@ -162,38 +208,94 @@ pub async fn translate(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prettify_lang(code: &str) -> &str {
|
||||
match code {
|
||||
"AR" => "Arabic",
|
||||
"BG" => "Bulgarian",
|
||||
"CS" => "Czech",
|
||||
"DA" => "Danish",
|
||||
"DE" => "German",
|
||||
"EL" => "Greek",
|
||||
"EN" => "English",
|
||||
"ES" => "Spanish",
|
||||
"ET" => "Estonian",
|
||||
"FI" => "Finnish",
|
||||
"FR" => "French",
|
||||
"HU" => "Hungarian",
|
||||
"ID" => "Indonesian",
|
||||
"IT" => "Italian",
|
||||
"JA" => "Japanese",
|
||||
"KO" => "Korean",
|
||||
"LT" => "Lithuanian",
|
||||
"LV" => "Latvian",
|
||||
"NB" => "Norwegian Bokmål",
|
||||
"NL" => "Dutch",
|
||||
"PL" => "Polish",
|
||||
"PT" => "Portuguese",
|
||||
"RO" => "Romanian",
|
||||
"RU" => "Russian",
|
||||
"SK" => "Slovak",
|
||||
"SL" => "Slovenian",
|
||||
"SV" => "Swedish",
|
||||
"TR" => "Turkish",
|
||||
"UK" => "Ukrainian",
|
||||
"ZH" => "Chinese",
|
||||
_ => code
|
||||
async fn update_locale_cache(api_key: &str) -> Result<(), reqwest::Error> {
|
||||
let api_url = if api_key.ends_with(":fx") {
|
||||
"https://api-free.deepl.com"
|
||||
} else {
|
||||
"https://api.deepl.com"
|
||||
};
|
||||
|
||||
let languages: Vec<DeepLLanguage> = REQWEST_
|
||||
.get(format!("{api_url}/v2/languages"))
|
||||
.header("User-Agent", "kon/reqwest")
|
||||
.header("Authorization", format!("DeepL-Auth-Key {api_key}"))
|
||||
.query(&[("type", "target")])
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
let mut c = LOCALE_CACHE.write().unwrap();
|
||||
for language in languages {
|
||||
c.insert(language.language, language.name);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List of languages that DeepL supports for translation
|
||||
static LOCALE_LOOKUP: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
|
||||
let mut c = HashMap::new();
|
||||
|
||||
c.insert("AR", "Arabic");
|
||||
c.insert("BG", "Bulgarian");
|
||||
c.insert("CS", "Czech");
|
||||
c.insert("DA", "Danish");
|
||||
c.insert("DE", "German");
|
||||
c.insert("EL", "Greek");
|
||||
c.insert("EN", "English");
|
||||
c.insert("ES", "Spanish");
|
||||
c.insert("ET", "Estonian");
|
||||
c.insert("FI", "Finnish");
|
||||
c.insert("FR", "French");
|
||||
c.insert("HU", "Hungarian");
|
||||
c.insert("ID", "Indonesian");
|
||||
c.insert("IT", "Italian");
|
||||
c.insert("JA", "Japanese");
|
||||
c.insert("KO", "Korean");
|
||||
c.insert("LT", "Lithuanian");
|
||||
c.insert("LV", "Latvian");
|
||||
c.insert("NB", "Norwegian Bokmål");
|
||||
c.insert("NL", "Dutch");
|
||||
c.insert("PL", "Polish");
|
||||
c.insert("PT", "Portuguese");
|
||||
c.insert("RO", "Romanian");
|
||||
c.insert("RU", "Russian");
|
||||
c.insert("SK", "Slovak");
|
||||
c.insert("SL", "Slovenian");
|
||||
c.insert("SV", "Swedish");
|
||||
c.insert("TR", "Turkish");
|
||||
c.insert("UK", "Ukrainian");
|
||||
c.insert("ZH", "Chinese");
|
||||
|
||||
c
|
||||
});
|
||||
|
||||
fn prettify_lang(code: &str) -> String {
|
||||
if let Ok(cache) = LOCALE_CACHE.read() {
|
||||
if let Some(name) = cache.get(code) {
|
||||
return name.clone();
|
||||
}
|
||||
}
|
||||
|
||||
LOCALE_LOOKUP.get(code).map(|&s| s.to_string()).unwrap_or_else(|| code.to_string())
|
||||
}
|
||||
|
||||
async fn get_quota(api_key: &str) -> Result<DeepLUsage, reqwest::Error> {
|
||||
let api_url = if api_key.ends_with(":fx") {
|
||||
"https://api-free.deepl.com"
|
||||
} else {
|
||||
"https://api.deepl.com"
|
||||
};
|
||||
|
||||
let usage: DeepLUsage = REQWEST_
|
||||
.get(format!("{api_url}/v2/usage"))
|
||||
.header("User-Agent", "kon/reqwest")
|
||||
.header("Authorization", format!("DeepL-Auth-Key {api_key}"))
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
Ok(usage)
|
||||
}
|
||||
|
@ -13,14 +13,25 @@ use {
|
||||
serde_json::Value,
|
||||
std::{
|
||||
collections::HashMap,
|
||||
sync::OnceLock,
|
||||
time::Duration
|
||||
}
|
||||
};
|
||||
|
||||
async fn pms_serverstatus(url: String) -> Result<Vec<(String, Vec<Value>)>, String> {
|
||||
let client = HttpClient::new();
|
||||
let duration = Duration::from_secs(5);
|
||||
let req = match tokio::time::timeout(duration, client.get(url.as_str(), "PMS-Status")).await {
|
||||
type IdNameHashmap = HashMap<&'static str, &'static str>;
|
||||
|
||||
const HTTP_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
fn id_name_map() -> &'static IdNameHashmap {
|
||||
static ID_NAME_MAP: OnceLock<IdNameHashmap> = OnceLock::new();
|
||||
ID_NAME_MAP.get_or_init(|| [("wotbsg", "ASIA"), ("wowssg", "ASIA"), ("wowseu", "EU")].iter().cloned().collect())
|
||||
}
|
||||
|
||||
async fn pms_serverstatus(
|
||||
http: &HttpClient,
|
||||
url: String
|
||||
) -> Result<Vec<(String, Vec<Value>)>, String> {
|
||||
let req = match tokio::time::timeout(HTTP_TIMEOUT, http.get(url.as_str(), "PMS-Status")).await {
|
||||
Ok(result) => match result {
|
||||
Ok(req) => req,
|
||||
Err(e) => return Err(format!("Failed to connect: {e}"))
|
||||
@ -28,7 +39,7 @@ async fn pms_serverstatus(url: String) -> Result<Vec<(String, Vec<Value>)>, Stri
|
||||
Err(_) => return Err("Request timed out".to_string())
|
||||
};
|
||||
|
||||
let response = match tokio::time::timeout(duration, req.json::<HashMap<String, Value>>()).await {
|
||||
let response = match tokio::time::timeout(HTTP_TIMEOUT, req.json::<HashMap<String, Value>>()).await {
|
||||
Ok(result) => match result {
|
||||
Ok(data) => data,
|
||||
Err(e) => return Err(format!("Failed to parse response: {e}"))
|
||||
@ -57,7 +68,6 @@ async fn pms_serverstatus(url: String) -> Result<Vec<(String, Vec<Value>)>, Stri
|
||||
|
||||
fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, String, bool)> {
|
||||
let mut server_map: HashMap<String, Vec<(String, String)>> = HashMap::new();
|
||||
let id_name_map: HashMap<&str, &str> = [("wotbsg", "ASIA"), ("wowssg", "ASIA"), ("wowseu", "EU")].iter().cloned().collect();
|
||||
|
||||
for (title, mapped_servers) in servers {
|
||||
for server in mapped_servers {
|
||||
@ -68,7 +78,7 @@ fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, Stri
|
||||
"-1" => "Offline",
|
||||
_ => "Unknown"
|
||||
};
|
||||
let name = id_name_map.get(id).unwrap_or(&name);
|
||||
let name = id_name_map().get(id).unwrap_or(&name);
|
||||
server_map
|
||||
.entry(title.clone())
|
||||
.or_default()
|
||||
@ -93,7 +103,7 @@ fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, Stri
|
||||
pub async fn wargaming(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
|
||||
ctx.defer().await?;
|
||||
|
||||
let regions = [
|
||||
static REGIONS: [(&str, &str); 4] = [
|
||||
("asia", "Asia (WoT)"),
|
||||
("eu", "Europe (WoT)"),
|
||||
("wgcb", "Console (WoTX)"),
|
||||
@ -103,17 +113,18 @@ pub async fn wargaming(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
|
||||
let pms_base = token_path().await.wg_pms;
|
||||
let mut embed = CreateEmbed::new().color(BINARY_PROPERTIES.embed_color);
|
||||
|
||||
let mut futures = Vec::new();
|
||||
let mut region_names = Vec::new();
|
||||
let http = HttpClient::new();
|
||||
let mut futures = Vec::with_capacity(REGIONS.len());
|
||||
let mut region_names = Vec::with_capacity(REGIONS.len());
|
||||
|
||||
for (region, name) in ®ions {
|
||||
for (region, name) in ®IONS {
|
||||
let url = if *region == "asia" {
|
||||
pms_base.clone()
|
||||
} else {
|
||||
pms_base.replace("asia", region)
|
||||
};
|
||||
|
||||
futures.push(pms_serverstatus(url));
|
||||
futures.push(pms_serverstatus(&http, url));
|
||||
region_names.push(name);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user