Skip to main content

btcli_lib/
fycore.rs

1// Copyright (C) 2026 S.A. (@snoware)
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use crate::conf::AppConfig;
8use crate::fancy_egg::{EGG_CODE, decrypt};
9use crate::fyerrcodes::query_msg;
10use md5;
11use rand;
12use serde::Deserialize;
13use serde_json;
14use std::collections::HashMap;
15use std::str::FromStr;
16use ureq;
17
18/// 提高程序可读性,避免硬编码host
19const HOST: &str = "https://fanyi-api.baidu.com/api/trans/vip/translate";
20
21/// 定义响应体数据结构
22// 成功
23#[derive(Deserialize, Debug)]
24#[allow(dead_code)]
25struct TranslationResponse {
26    from: String,
27    to: String,
28    trans_result: Vec<TranslationItem>,
29}
30
31#[derive(Deserialize, Debug)]
32#[allow(dead_code)]
33struct TranslationItem {
34    src: String,
35    dst: String,
36}
37
38//失败
39#[derive(Deserialize, Debug)]
40#[allow(dead_code)]
41struct ErrorResponse {
42    error_code: serde_json::Value,
43    error_msg: String,
44}
45
46fn calculate_sign(appid: &str, q: &str, salt: &str, key: &str) -> String {
47    let sign_str = format!("{}{}{}{}", appid, q, salt, key);
48    format!("{:x}", md5::compute(sign_str.as_bytes()))
49}
50
51fn send_response(
52    appid: &str,
53    from: &str,
54    to: &str,
55    q: &str,
56    app_config: &AppConfig,
57) -> Result<String, Box<dyn std::error::Error>> {
58    let salt = rand::random::<u32>().to_string();
59    let sign = calculate_sign(appid, q, &salt, &app_config.key);
60
61    let mut params = HashMap::new();
62    params.insert("appid", appid);
63    params.insert("from", from);
64    params.insert("to", to);
65    params.insert("q", q);
66    params.insert("salt", &salt);
67    params.insert("sign", &sign);
68
69    let response = ureq::post(HOST)
70        .header("Content-Type", "application/x-www-form-urlencoded")
71        .send_form(&params)?;
72
73    let response_text = response.into_body().read_to_string()?;
74    Ok(response_text)
75}
76
77///已经确定成功,从body里获取返回结果
78fn patch_raw(content: String) -> Result<String, String> {
79    let parsed_response = serde_json::from_str::<TranslationResponse>(&content)
80        .map_err(|e| format!(":( Failed to parse successful response: {}", e))?;
81
82    if !parsed_response.trans_result.is_empty() {
83        Ok(parsed_response.trans_result[0].dst.clone())
84    } else {
85        Err(":( Response contains no translation results".to_string())
86    }
87}
88
89///解析错误响应
90fn patch_error(body_content: String) -> Result<ErrorResponse, String> {
91    serde_json::from_str::<ErrorResponse>(&body_content)
92        .map_err(|e| format!(":( Failed to parse error response: {}", e))
93}
94
95/// 核心翻译函数 - 单次翻译
96pub fn translate(
97    appid: &str,
98    from: &str,
99    to: &str,
100    q: &str,
101    app_config: AppConfig,
102) -> Result<String, String> {
103    // 特殊功能:如果翻译内容为 QAS,则直接返回彩蛋
104    if q.trim().eq_ignore_ascii_case("QAS") {
105        let egg: String = decrypt(EGG_CODE);
106        return Ok(egg);
107    }
108
109    // 控制请求频率
110    std::thread::sleep(std::time::Duration::from_millis(100));
111
112    // 发送请求并获取响应
113    match send_response(appid, from, to, q, &app_config) {
114        Err(e) => {
115            return Err(format!(":( error sending request: {}", e.to_string()));
116        }
117        Ok(response_body) => {
118            // 检查是否包含错误信息
119            if response_body.contains("error_msg") {
120                // 解析错误响应
121                match patch_error(response_body) {
122                    Ok(err_resp) => {
123                        // 处理 error_code 可能是字符串或数字的情况
124                        let errcode = match err_resp.error_code.as_u64() {
125                            Some(code) => code as usize,
126                            None => {
127                                // 如果是字符串,则尝试转换
128                                match err_resp.error_code.as_str() {
129                                    Some(code_str) => match usize::from_str(code_str) {
130                                        Ok(code) => code,
131                                        Err(_) => {
132                                            return Err(":( failed to parse error code".to_string());
133                                        }
134                                    },
135                                    None => return Err(":( failed to parse error code".to_string()),
136                                }
137                            }
138                        };
139
140                        // 返回错误信息
141                        return Err(format!(
142                            ":( We asked, but server said: {}",
143                            query_msg(errcode)
144                        ));
145                    }
146                    Err(e) => return Err(e),
147                }
148            } else {
149                // 翻译成功,返回解析后的结果
150                return patch_raw(response_body);
151            }
152        }
153    }
154}