Compare commits

...
2 Commits
Author SHA1 Message Date
Bluemangoo b56f3fa40a add desc 2026-08-08 00:48:05 +08:00
Bluemangoo 1a645f9177 向nonebot对齐了一下日志输出 2026-07-05 18:39:57 +08:00
13 changed files with 423 additions and 207 deletions
+1
View File
@@ -1,2 +1,3 @@
target/ target/
.idea/ .idea/
/cross-build.bat
Generated
+97 -81
View File
@@ -338,6 +338,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colored"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@@ -454,12 +464,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.6" version = "0.2.6"
@@ -498,12 +502,15 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets-updater", "assets-updater",
"chrono",
"common", "common",
"fern",
"futures",
"log", "log",
"mimalloc",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"simplelog",
"tokio", "tokio",
] ]
@@ -579,6 +586,16 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "fern"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
dependencies = [
"colored",
"log",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.9" version = "0.1.9"
@@ -616,6 +633,21 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.32" version = "0.3.32"
@@ -623,6 +655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
@@ -631,6 +664,34 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-executor"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-macro"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.32" version = "0.3.32"
@@ -649,8 +710,13 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
] ]
@@ -1144,6 +1210,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "lebe" name = "lebe"
version = "0.5.3" version = "0.5.3"
@@ -1166,6 +1238,15 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "libmimalloc-sys"
version = "0.1.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "libyaml-rs" name = "libyaml-rs"
version = "0.3.0" version = "0.3.0"
@@ -1221,6 +1302,15 @@ version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]]
name = "mimalloc"
version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862"
dependencies = [
"libmimalloc-sys",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@@ -1298,12 +1388,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.4.2" version = "0.4.2"
@@ -1344,15 +1428,6 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@@ -1411,12 +1486,6 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@@ -2011,17 +2080,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "simplelog"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
dependencies = [
"log",
"termcolor",
"time",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.12" version = "0.4.12"
@@ -2121,21 +2179,11 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "test-main" name = "test-main"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"entry", "entry",
"simplelog",
] ]
[[package]] [[package]]
@@ -2172,38 +2220,6 @@ dependencies = [
"zune-jpeg", "zune-jpeg",
] ]
[[package]]
name = "time"
version = "0.3.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
dependencies = [
"deranged",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.3" version = "0.8.3"
+1 -1
View File
@@ -116,7 +116,7 @@ impl AssetExecutionContext {
.default_headers(headers) .default_headers(headers)
.connect_timeout(Duration::from_secs(10)) .connect_timeout(Duration::from_secs(10))
.timeout(Duration::from_secs(180)) .timeout(Duration::from_secs(180))
.pool_max_idle_per_host(100) .pool_max_idle_per_host(4)
.tcp_keepalive(Duration::from_secs(30)); .tcp_keepalive(Duration::from_secs(30));
if let Some(proxy) = &app_config.execution.proxy if let Some(proxy) = &app_config.execution.proxy
+9 -3
View File
@@ -2,8 +2,12 @@
resolver = "3" resolver = "3"
members = ["entry", "common", "assets-updater", "test-main"] members = ["entry", "common", "assets-updater", "test-main"]
#[profile.dev] [profile.minimum]
#codegen-units = 4 inherits = "release"
lto = true
opt-level = 'z'
codegen-units = 1
strip = true
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.102" anyhow = "1.0.102"
@@ -25,4 +29,6 @@ cridecoder = "0.1.1"
thiserror = "2.0.18" thiserror = "2.0.18"
cipher = "0.4.4" cipher = "0.4.4"
twox-hash = "2.1.2" twox-hash = "2.1.2"
fern = "0.7"
futures = "0.3.32"
mimalloc = { version = "0.1", default-features = false }
+5 -2
View File
@@ -11,10 +11,13 @@ crate-type = ["rlib", "cdylib"]
common = { path = "../common" } common = { path = "../common" }
assets-updater = { path = "../assets-updater" } assets-updater = { path = "../assets-updater" }
tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "sync"] } tokio = { workspace = true, features = ["macros", "rt", "sync", ] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] } reqwest = { workspace = true, features = ["json"] }
log = { workspace = true } log = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
simplelog = "0.12.2" fern = { workspace = true, features = ["colored"] }
futures = { workspace = true }
mimalloc = { workspace = true, default-features = false }
chrono = { workspace = true }
+120 -13
View File
@@ -1,5 +1,6 @@
use crate::CONFIG; use crate::CONFIG;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug)]
@@ -19,14 +20,18 @@ impl Config {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct OptConfig { pub struct OptConfig {
pub proxy: Option<String>, pub proxy: Option<String>,
pub pjsk_proxy: Option<String>,
pub ghp: Option<String>, pub ghp: Option<String>,
pub log_level: Option<String>,
} }
impl OptConfig { impl OptConfig {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
proxy: None, proxy: None,
pjsk_proxy: None,
ghp: None, ghp: None,
log_level: None,
} }
} }
} }
@@ -42,7 +47,6 @@ impl State {
serde_json::from_str(x).unwrap() serde_json::from_str(x).unwrap()
} }
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
git_hash: None, git_hash: None,
@@ -80,17 +84,6 @@ pub struct EventStory {
pub assetbundle_name: String, pub assetbundle_name: String,
} }
// 用这个找是因为这个文件小一点(((
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CardEpisode {
pub id: u64,
pub seq: u64,
#[serde(rename = "cardId")]
pub card_id: u64,
#[serde(rename = "assetbundleName")]
pub assetbundle_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventCard { pub struct EventCard {
pub id: u64, pub id: u64,
@@ -100,6 +93,114 @@ pub struct EventCard {
pub event_id: u64, pub event_id: u64,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Card {
pub id: u64,
#[serde(rename = "characterId")]
pub character_id: u64,
#[serde(rename = "cardRarityType")]
pub card_rarity_type: String,
pub attr: String,
#[serde(rename = "skillId")]
pub skill_id: u64,
pub prefix: String,
#[serde(rename = "assetbundleName")]
pub assetbundle_name: String,
}
impl PartialEq for Card {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
}
}
impl Eq for Card {}
impl PartialOrd for Card {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Card {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl Card {
pub fn rarity_short_text(&self) -> String {
match self.card_rarity_type.as_str() {
"rarity_1" => "".to_string(),
"rarity_2" => "⭐⭐".to_string(),
"rarity_3" => "⭐⭐⭐".to_string(),
"rarity_4" => "⭐⭐⭐⭐".to_string(),
"rarity_birthday" => "🎀".to_string(),
_ => self.card_rarity_type.clone(),
}
}
pub fn attr_short_text(&self) -> String {
match self.attr.as_str() {
"cute" => "粉花".to_string(),
"pure" => "绿草".to_string(),
"cool" => "蓝星".to_string(),
"mysterious" => "紫月".to_string(),
"happy" => "橙心".to_string(),
_ => self.attr.clone(),
}
}
pub fn skill_id_short(&self) -> Option<String> {
match self.skill_id {
1..=4 => Some("得分提高".to_string()),
5..=8 => Some("判定提升".to_string()),
9..=10 => Some("回复".to_string()),
11 => Some("P分".to_string()),
12 => Some("血分".to_string()),
13 => Some("P分(特殊)".to_string()),
14 => Some("回复P分(特殊)".to_string()),
15 => Some("LN团分".to_string()),
16 => Some("MMJ团分".to_string()),
17 => Some("VBS团分".to_string()),
18 => Some("WS团分".to_string()),
19 => Some("25团分".to_string()),
22 => Some("等级分(bfes)".to_string()),
23 => Some("队内抽分(bfes)".to_string()),
24 => Some("多团分".to_string()),
_ => None,
}
}
pub fn character_name(&self) -> String {
match self.character_id {
1 => "星乃一歌".to_string(),
2 => "天馬咲希".to_string(),
3 => "望月穂波".to_string(),
4 => "日野森志歩".to_string(),
5 => "花里みのり".to_string(),
6 => "桐谷遥".to_string(),
7 => "桃井愛莉".to_string(),
8 => "日野森雫".to_string(),
9 => "小豆沢こはね".to_string(),
10 => "白石杏".to_string(),
11 => "東雲彰人".to_string(),
12 => "青柳冬弥".to_string(),
13 => "天馬司".to_string(),
14 => "鳳えむ".to_string(),
15 => "草薙寧々".to_string(),
16 => "神代類".to_string(),
17 => "宵崎奏".to_string(),
18 => "朝比奈まふゆ".to_string(),
19 => "東雲絵名".to_string(),
20 => "暁山瑞希".to_string(),
21 => "初音ミク".to_string(),
22 => "鏡音リン".to_string(),
23 => "鏡音レン".to_string(),
24 => "巡音ルカ".to_string(),
25 => "MEIKO".to_string(),
26 => "KAITO".to_string(),
_ => "".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentVersion { pub struct CurrentVersion {
pub app_hash: String, pub app_hash: String,
@@ -109,8 +210,14 @@ pub struct CurrentVersion {
pub asset_hash: String, pub asset_hash: String,
} }
#[derive(Debug, Clone, Serialize)]
pub struct FetchResultCard {
pub text: String,
pub path: Vec<PathBuf>,
}
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct FetchResult { pub struct FetchResult {
pub event_id: u64, pub event_id: u64,
pub card_paths: Vec<PathBuf>, pub cards: Vec<FetchResultCard>,
} }
+5 -5
View File
@@ -14,11 +14,11 @@ impl Downloader {
pub async fn new() -> anyhow::Result<Downloader> { pub async fn new() -> anyhow::Result<Downloader> {
let master_data = get_master_data().await?; let master_data = get_master_data().await?;
let mut execution = ExecutionConfig::default(); let mut execution = ExecutionConfig::default();
// if let Ok(opt_conf) = &OPT_CONFIG.read() if let Ok(opt_conf) = &OPT_CONFIG.read()
// && let Some(proxy) = &opt_conf.proxy && let Some(proxy) = &opt_conf.pjsk_proxy
// { {
// execution.proxy = Some(proxy.to_string()); execution.proxy = Some(proxy.to_string());
// } }
let app_config = AppConfig { let app_config = AppConfig {
execution, execution,
tools: ToolsConfig{ tools: ToolsConfig{
+28 -19
View File
@@ -3,15 +3,19 @@ use crate::{ending_point, OPT_CONFIG, STORE, UA};
use anyhow::anyhow; use anyhow::anyhow;
use reqwest::Client; use reqwest::Client;
use serde::Deserialize; use serde::Deserialize;
use std::sync::OnceLock;
const COMMIT_URL: &str = "https://api.github.com/repos/kotori8823/sekai-master-db/commits?path=events.json&per_page=1"; const COMMIT_URL: &str =
"https://api.github.com/repos/kotori8823/sekai-master-db/commits?path=events.json&per_page=1";
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct CommitResponse { struct CommitResponse {
sha: String, sha: String,
} }
fn http_client() -> anyhow::Result<Client> { static HTTP_CLIENT: OnceLock<Client> = OnceLock::new();
fn new_http_client() -> anyhow::Result<Client> {
let mut builder = Client::builder(); let mut builder = Client::builder();
if let Ok(opt_conf) = &OPT_CONFIG.read() { if let Ok(opt_conf) = &OPT_CONFIG.read() {
if let Some(proxy) = &opt_conf.proxy { if let Some(proxy) = &opt_conf.proxy {
@@ -32,6 +36,17 @@ fn http_client() -> anyhow::Result<Client> {
Ok(builder.build()?) Ok(builder.build()?)
} }
fn http_client() -> anyhow::Result<&'static Client> {
let is_init = { HTTP_CLIENT.get().is_some() };
if !is_init {
let client = new_http_client()?;
HTTP_CLIENT
.set(client)
.map_err(|_| anyhow!("Failed to set HTTP client"))?;
}
Ok(HTTP_CLIENT.get().unwrap())
}
pub async fn fetch_last_hash() -> anyhow::Result<String> { pub async fn fetch_last_hash() -> anyhow::Result<String> {
let resp = http_client()?.get(COMMIT_URL).send().await?; let resp = http_client()?.get(COMMIT_URL).send().await?;
let text = resp.text().await?; let text = resp.text().await?;
@@ -46,30 +61,24 @@ pub fn raw_url(path: &str) -> String {
pub async fn sync_data() -> anyhow::Result<()> { pub async fn sync_data() -> anyhow::Result<()> {
let client = http_client()?; let client = http_client()?;
let events_cards = &client let (events_cards, event_stories, cards) = tokio::try_join!(
.get(raw_url("eventCards.json")) client.get(raw_url("eventCards.json")).send().await?.text(),
.send() client
.await?
.text()
.await?;
ending_point!(ok);
let event_stories = &client
.get(raw_url("eventStories.json")) .get(raw_url("eventStories.json"))
.send() .send()
.await? .await?
.text() .text(),
.await?; client
ending_point!(ok); .get(raw_url("cards.json"))
let card_episodes = &client
.get(raw_url("cardEpisodes.json"))
.send() .send()
.await? .await?
.text() .text()
.await?; )?;
ending_point!(ok);
let mut store = STORE.write().map_err(|e| anyhow!("{}", e))?; let mut store = STORE.write().map_err(|e| anyhow!("{}", e))?;
store.set_event_cards(serde_json::from_str(events_cards)?); store.set_event_cards(serde_json::from_str(&events_cards)?);
store.set_event_stories(serde_json::from_str(event_stories)?); store.set_event_stories(serde_json::from_str(&event_stories)?);
store.set_card_episodes(serde_json::from_str(card_episodes)?); store.set_cards(serde_json::from_str(&cards)?);
Ok(()) Ok(())
} }
+58 -13
View File
@@ -1,3 +1,8 @@
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
mod data; mod data;
mod download; mod download;
mod github; mod github;
@@ -7,14 +12,15 @@ mod store;
use crate::data::{Config, FetchResult, OptConfig, State}; use crate::data::{Config, FetchResult, OptConfig, State};
use crate::main_loop::main; use crate::main_loop::main;
use crate::store::Store; use crate::store::Store;
use log::{LevelFilter, debug, error}; use chrono::Local;
use simplelog::{ColorChoice, TermLogger, TerminalMode}; use fern::colors::{Color, ColoredLevelConfig};
use log::{LevelFilter, debug};
use std::ffi::{CStr, CString, c_char, c_int}; use std::ffi::{CStr, CString, c_char, c_int};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{OnceLock, RwLock}; use std::sync::{OnceLock, RwLock};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use tokio::runtime::Runtime;
use tokio::sync::Notify; use tokio::sync::Notify;
pub static CONFIG: OnceLock<Config> = OnceLock::new(); pub static CONFIG: OnceLock<Config> = OnceLock::new();
@@ -43,7 +49,7 @@ macro_rules! ending_point {
}; };
() => { () => {
ending_point!(()) ending_point!(())
} };
} }
pub const UA: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"; pub const UA: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
@@ -72,7 +78,9 @@ macro_rules! def_opt_str_setter {
} }
def_opt_str_setter!(proxy); def_opt_str_setter!(proxy);
def_opt_str_setter!(pjsk_proxy);
def_opt_str_setter!(ghp); def_opt_str_setter!(ghp);
def_opt_str_setter!(log_level);
/// # Safety /// # Safety
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@@ -96,17 +104,53 @@ pub unsafe extern "C" fn boot(
.init() .init()
.expect("Failed to initialize state"); .expect("Failed to initialize state");
*STORE.write().unwrap() = Store::load(); *STORE.write().unwrap() = Store::load();
// TermLogger::init(
// LevelFilter::Debug, if let Some(log_level) = &OPT_CONFIG.read().unwrap().log_level {
// simplelog::Config::default(), match LevelFilter::from_str(log_level) {
// TerminalMode::Mixed, Ok(level) => {
// ColorChoice::Auto, let name = CONFIG.get().unwrap().name.clone();
// ) let colors = ColoredLevelConfig::new()
// .unwrap(); .info(Color::BrightWhite)
.debug(Color::BrightBlue)
.warn(Color::Yellow)
.error(Color::Red);
const GREEN: &str = "\x1b[32m"; // 绿色
const CYAN_UNDERLINE: &str = "\x1b[36;4m"; // 青色 + 下划线
const RESET: &str = "\x1b[0m"; // 重置颜色
let _ = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{green}{time}{reset} [{level}] {cyan_ul}{name}{reset} | {message}",
green = GREEN,
time = Local::now().format("%m-%d %H:%M:%S"),
reset = RESET,
level = colors.color(record.level()), // fern 处理等级颜色
cyan_ul = CYAN_UNDERLINE,
name = name,
message = message
))
})
.level(level)
.chain(std::io::stdout())
.apply()
.is_err_and(|e| {
eprintln!("Failed to initialize logger: {}", e);
true
});
}
Err(err) => {
eprintln!("Invalid log level: '{}': {}", log_level, err);
}
}
}
let handle = thread::Builder::new() let handle = thread::Builder::new()
.name("sekai-sync-lib".to_string()) .name("sekai-sync-lib".to_string())
.spawn(|| { .spawn(|| {
let rt = Runtime::new().expect("Failed to create Tokio runtime"); let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create Tokio runtime");
rt.block_on(keep_loop()); rt.block_on(keep_loop());
}); });
@@ -117,12 +161,13 @@ pub unsafe extern "C" fn boot(
0 0
} }
Err(error) => { Err(error) => {
error!("{}", error); eprintln!("{}", error);
-1 -1
} }
} }
} }
/// # Safety
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn stop() -> c_int { pub unsafe extern "C" fn stop() -> c_int {
if let Some(thread) = THREAD.write().unwrap().take() { if let Some(thread) = THREAD.write().unwrap().take() {
+47 -11
View File
@@ -1,10 +1,12 @@
use crate::data::{FetchResult, State}; use crate::data::{FetchResult, FetchResultCard, State};
use crate::download::Downloader; use crate::download::Downloader;
use crate::github::{fetch_last_hash, sync_data}; use crate::github::{fetch_last_hash, sync_data};
use crate::{CONFIG, PROVIDE, STATE, STORE, ending_point}; use crate::{CONFIG, PROVIDE, STATE, STORE, ending_point};
use anyhow::anyhow; use anyhow::anyhow;
use assets_updater::core::export_pipeline::find_files; use assets_updater::core::export_pipeline::find_files;
use log::debug; use futures::StreamExt;
use futures::stream::FuturesUnordered;
use log::{debug, info};
use std::fs; use std::fs;
pub async fn main() -> anyhow::Result<()> { pub async fn main() -> anyhow::Result<()> {
@@ -55,18 +57,20 @@ pub async fn main() -> anyhow::Result<()> {
let event = event.unwrap(); let event = event.unwrap();
// event updated // event updated
info!("Event updated to {}", event.event_id);
let cards = store.find_cards_by_event(event.event_id); let cards = store.find_cards_by_event(event.event_id);
drop(store); drop(store);
ending_point!(ok); ending_point!(ok);
let downloader = Downloader::new().await?; let downloader = Downloader::new().await?;
ending_point!(ok); ending_point!(ok);
let mut images = Vec::new();
let cache_dir = CONFIG.get().unwrap().cache_dir(); let cache_dir = CONFIG.get().unwrap().cache_dir();
for card in cards { let mut futures = FuturesUnordered::new();
for card in &cards {
futures.push(async {
ending_point!(ok); ending_point!(ok);
debug!( debug!(
"Pulling card {} from bundle {}", "Pulling card {} from bundle {}",
card.card_id, card.assetbundle_name card.id, card.assetbundle_name
); );
let dir = downloader let dir = downloader
.pull(&format!("character/member/{}", card.assetbundle_name)) .pull(&format!("character/member/{}", card.assetbundle_name))
@@ -75,6 +79,7 @@ pub async fn main() -> anyhow::Result<()> {
fs::remove_dir_all(dir.0)?; fs::remove_dir_all(dir.0)?;
}); });
let files = find_files(&dir.0)?; let files = find_files(&dir.0)?;
let mut local_images = Vec::new();
for file in files { for file in files {
ending_point!(ok_with { ending_point!(ok_with {
fs::remove_dir_all(dir.0)?; fs::remove_dir_all(dir.0)?;
@@ -88,16 +93,47 @@ pub async fn main() -> anyhow::Result<()> {
let new_file = cache_dir.join(new_filename); let new_file = cache_dir.join(new_filename);
fs::copy(&file, &new_file)?; fs::copy(&file, &new_file)?;
fs::remove_file(&file)?; fs::remove_file(&file)?;
images.push(new_file); local_images.push(new_file);
} }
} }
fs::remove_dir_all(dir.0)?; fs::remove_dir_all(dir.0)?;
} Ok::<_, anyhow::Error>((card.clone(), local_images))
*PROVIDE.write().map_err(|e| anyhow!("{}", e))? = Some(FetchResult {
event_id: event.event_id,
card_paths: images,
}); });
}
let mut images = Vec::new();
while let Some(res) = futures.next().await {
images.push(res?);
}
images.sort_by(|a, b| a.0.id.cmp(&b.0.id)); // 小在前
let images: Vec<_> = images
.into_iter()
.map(|(card, path)| FetchResultCard {
text: format!(
"#{id} {attr}{rarity} {skill} [{prefix}]{name}",
id = card.id,
attr = card.attr_short_text(),
rarity = card.rarity_short_text(),
skill = card
.skill_id_short().map(|s| format!("({})", s))
.unwrap_or_default(),
prefix = card.prefix,
name = card.character_name()
),
path,
})
.collect();
info!(
"In event {} collected {} cards",
event.event_id,
images.len()
);
let result = FetchResult {
event_id: event.event_id,
cards: images,
};
debug!("Providing result: {:?}", result);
*PROVIDE.write().map_err(|e| anyhow!("{}", e))? = Some(result);
let mut state = STATE.write().map_err(|e| anyhow!("{}", e))?; let mut state = STATE.write().map_err(|e| anyhow!("{}", e))?;
state.git_hash = Some(current_hash); state.git_hash = Some(current_hash);
state.event_id = Some(event.event_id); state.event_id = Some(event.event_id);
+14 -14
View File
@@ -1,5 +1,5 @@
use crate::CONFIG; use crate::CONFIG;
use crate::data::{CardEpisode, EventCard, EventStory}; use crate::data::{Card, EventCard, EventStory};
use serde::Serialize; use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::fs; use std::fs;
@@ -8,16 +8,16 @@ use std::path::PathBuf;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Store { pub struct Store {
pub event_stories: Vec<EventStory>, pub event_stories: Vec<EventStory>,
pub card_episodes: Vec<CardEpisode>,
pub event_cards: Vec<EventCard>, pub event_cards: Vec<EventCard>,
pub cards: Vec<Card>,
} }
impl Store { impl Store {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
event_cards: Vec::new(), event_cards: Vec::new(),
card_episodes: Vec::new(),
event_stories: Vec::new(), event_stories: Vec::new(),
cards: Vec::new(),
} }
} }
@@ -31,8 +31,8 @@ impl Store {
Self { Self {
event_stories: Self::load_file(&cache_dir.join("event_stories.json")), event_stories: Self::load_file(&cache_dir.join("event_stories.json")),
card_episodes: Self::load_file(&cache_dir.join("card_episodes.json")),
event_cards: Self::load_file(&cache_dir.join("event_cards.json")), event_cards: Self::load_file(&cache_dir.join("event_cards.json")),
cards: Self::load_file(&cache_dir.join("cards.json")),
} }
} }
@@ -55,23 +55,23 @@ impl Store {
Self::save_file(&path, &self.event_stories); Self::save_file(&path, &self.event_stories);
} }
pub fn set_card_episodes(&mut self, data: Vec<CardEpisode>) {
self.card_episodes = data;
let path = CONFIG.get().unwrap().cache_dir().join("card_episodes.json");
Self::save_file(&path, &self.card_episodes);
}
pub fn set_event_cards(&mut self, data: Vec<EventCard>) { pub fn set_event_cards(&mut self, data: Vec<EventCard>) {
self.event_cards = data; self.event_cards = data;
let path = CONFIG.get().unwrap().cache_dir().join("event_cards.json"); let path = CONFIG.get().unwrap().cache_dir().join("event_cards.json");
Self::save_file(&path, &self.event_cards); Self::save_file(&path, &self.event_cards);
} }
pub fn set_cards(&mut self, data: Vec<Card>) {
self.cards = data;
let path = CONFIG.get().unwrap().cache_dir().join("cards.json");
Self::save_file(&path, &self.cards);
}
pub fn save_all(&self) { pub fn save_all(&self) {
let cache_dir = CONFIG.get().unwrap().cache_dir(); let cache_dir = CONFIG.get().unwrap().cache_dir();
Self::save_file(&cache_dir.join("event_stories.json"), &self.event_stories); Self::save_file(&cache_dir.join("event_stories.json"), &self.event_stories);
Self::save_file(&cache_dir.join("card_episodes.json"), &self.card_episodes);
Self::save_file(&cache_dir.join("event_cards.json"), &self.event_cards); Self::save_file(&cache_dir.join("event_cards.json"), &self.event_cards);
Self::save_file(&cache_dir.join("cards.json"), &self.cards);
} }
pub fn find_last_event(&self) -> Option<EventStory> { pub fn find_last_event(&self) -> Option<EventStory> {
@@ -81,15 +81,15 @@ impl Store {
.cloned() .cloned()
} }
pub fn find_cards_by_event(&self, event_id: u64) -> Vec<CardEpisode> { pub fn find_cards_by_event(&self, event_id: u64) -> Vec<Card> {
self.event_cards self.event_cards
.iter() .iter()
.filter(|e| e.event_id == event_id) .filter(|e| e.event_id == event_id)
.filter_map(|c| { .filter_map(|c| {
self.card_episodes self.cards
.iter() .iter()
.rev() .rev()
.find(|e| e.card_id == c.card_id) .find(|e| e.id == c.card_id)
}) })
.cloned() .cloned()
.collect() .collect()
-2
View File
@@ -5,5 +5,3 @@ edition = "2024"
[dependencies] [dependencies]
entry = { path = "../entry" } entry = { path = "../entry" }
simplelog = "0.12.2"
+5 -10
View File
@@ -1,19 +1,13 @@
use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode}; use sekai_sync_lib::{boot, ghp, log_level, proxy};
use sekai_sync_lib::{boot, ghp, proxy};
fn main() { fn main() {
let _proxy = "http://127.0.0.1:7890"; let _proxy = "http://127.0.0.1:7890";
let _cwd = "D:\\Workspace\\sekai-sync-lib\\target"; let _cwd = "D:\\Workspace\\sekai-sync-lib\\target";
let _name = "sekai-sync-lib"; let _name = "sekai-sync-lib";
let _as_path = "D:\\Workspace\\AssetStudio\\AssetStudioCLI\\bin\\Release\\net9.0\\AssetStudioModCLI.exe"; let _as_path =
"D:\\Workspace\\AssetStudio\\AssetStudioCLI\\bin\\Release\\net9.0\\AssetStudioModCLI.exe";
let _ghp = "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"; let _ghp = "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";
// let _log_level = "debug";
// TermLogger::init(
// LevelFilter::Debug,
// Config::default(),
// TerminalMode::Mixed,
// ColorChoice::Auto,
// ).unwrap();
macro_rules! cstr { macro_rules! cstr {
($v:expr) => { ($v:expr) => {
@@ -21,6 +15,7 @@ fn main() {
}; };
} }
unsafe { unsafe {
log_level(cstr!(_log_level));
proxy(cstr!(_proxy)); proxy(cstr!(_proxy));
ghp(cstr!(_ghp)); ghp(cstr!(_ghp));
boot(cstr!(_cwd), cstr!(_name), cstr!(_as_path)); boot(cstr!(_cwd), cstr!(_name), cstr!(_as_path));