diff --git a/STranslateDLL/LocalMode.cs b/STranslateDLL/LocalMode.cs index b2127f4..4e59c1a 100644 --- a/STranslateDLL/LocalMode.cs +++ b/STranslateDLL/LocalMode.cs @@ -1,9 +1,255 @@ -namespace STranslateDLL; +using System.IO.Compression; +using System.Text.Json; +using System.Text; +using Newtonsoft.Json; + +namespace STranslateDLL; public class LocalMode { - public async Task ExecuteAsync() + private static long NextId; + + private static bool HasInit = false; + + private static void Initial() + { + var rand = new Random(); + var num = rand.NextInt64(99999) + 8300000; + NextId = num * 1000; + + HasInit = true; + } + + public static async Task ExecuteAsync(string content, string? sourceLang = null, string targetLang = "ZH", CancellationToken? token = null) + { + if (!HasInit) + { + Initial(); + } + var getToken = token ?? CancellationToken.None; + long timeSpan = GenerateTimestamp(content); + long id = CreateId(); + string reqStr = GenerateRequestStr(content, sourceLang, targetLang, timeSpan, id); + + using var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://www2.deepl.com/jsonrpc") + { + Content = new StringContent(reqStr, Encoding.UTF8, "application/json") + }; + + // Add headers to the request + request.Headers.Add("User-Agent", "DeepL-iOS/2.4.0 iOS 15.7.1 (iPhone14,2)"); + request.Headers.Add("Accept", "*/*"); + request.Headers.Add("x-app-os-name", "IOS"); + request.Headers.Add("x-app-os-version", "15.7.1"); + request.Headers.Add("Accept-Language", "en-US,en;q=0.9"); + request.Headers.Add("Accept-Encoding", "gzip,deflate,br"); + //request.Headers.Add("Content-Type", "application/json"); + request.Headers.Add("x-app-device", "iPhone14,2"); + request.Headers.Add("x-app-build", "353"); + request.Headers.Add("x-app-version", "2.4"); + request.Headers.Add("Referer", "https://www.deepl.com/"); + request.Headers.Add("Connection", "keep-alive"); + + HttpResponseMessage resp = await client.SendAsync(request, getToken); + //resp.EnsureSuccessStatusCode(); + + string responseBody; + + if (resp.Content.Headers.ContentEncoding.Contains("br")) + { + using var responseStream = await resp.Content.ReadAsStreamAsync(getToken); + using var decompressionStream = new BrotliStream(responseStream, CompressionMode.Decompress); + using var streamReader = new StreamReader(decompressionStream); + responseBody = await streamReader.ReadToEndAsync(getToken); + } + else + { + responseBody = await resp.Content.ReadAsStringAsync(getToken); + } + + var deeplResp = JsonConvert.DeserializeObject(responseBody); + var response = new Response + { + Code = resp.StatusCode.GetHashCode(), + Data = deeplResp?.Result?.Texts?.FirstOrDefault()?.Text ?? deeplResp?.Error?.Message ?? "Empty Response" + }; + + return JsonConvert.SerializeObject(response); + } + + private static long GenerateTimestamp(string texts) + { + long iCount = texts.Split('i').Length - 1; + long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + return iCount != 0 ? ts - ts % (iCount + 1) + (iCount + 1) : ts; + } + + private static long CreateId() + { + return Interlocked.Increment(ref NextId); + } + + private static string GenerateRequestStr(string text, string? sourceLang, string targetLang, long timeSpan, long id) { - await Task.Delay(3000); + var req = new DeepLRequest + { + Jsonrpc = "2.0", + Method = "LMT_handle_texts", + Params = new ReqParams + { + Texts = + [ + new ReqParamsTexts + { + Text = text, + RequestAlternatives = 0 + } + ], + Splitting = "newlines", + Lang = new ReqParamsLang + { + SourceLangUserSelected = sourceLang, + TargetLang = targetLang + }, + Timestamp = timeSpan, + CommonJobParams = new ReqParamsCommonJobParams + { + WasSpoken = false, + TranscribeAS = null + } + }, + Id = id + }; + + string json = JsonConvert.SerializeObject(req); + return json; } +} + +public class Response +{ + public int Code { get; set; } + public string Data { get; set; } = ""; +} + +public class DeepLRequest +{ + [JsonProperty("jsonrpc")] + public string Jsonrpc { get; set; } = ""; + [JsonProperty("method")] + public string Method { get; set; } = ""; + [JsonProperty("params")] + public ReqParams? Params { get; set; } + [JsonProperty("id")] + public long Id { get; set; } +} + +public class ReqParams +{ + [JsonProperty("texts")] + public ReqParamsTexts[]? Texts { get; set; } + [JsonProperty("splitting")] + public string Splitting { get; set; } = ""; + [JsonProperty("lang")] + public ReqParamsLang? Lang { get; set; } + [JsonProperty("timestamp")] + public long Timestamp { get; set; } + [JsonProperty("commonJobParams")] + public ReqParamsCommonJobParams? CommonJobParams { get; set; } +} + +public class ReqParamsTexts +{ + [JsonProperty("text")] + public string Text { get; set; } = ""; + [JsonProperty("requestAlternatives")] + public int RequestAlternatives { get; set; } +} + +public class ReqParamsLang +{ + [JsonProperty("source_lang_user_selected", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SourceLangUserSelected { get; set; } + [JsonProperty("target_lang")] + public string TargetLang { get; set; } = ""; +} + +public class ReqParamsCommonJobParams +{ + [JsonProperty("wasSpoken")] + public bool WasSpoken { get; set; } + [JsonProperty("transcribe_as", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? TranscribeAS { get; set; } +} + +public class DeepLResponse +{ + [JsonProperty("jsonrpc")] + public string Jsonrpc { get; set; } = ""; + [JsonProperty("id")] + public int Id { get; set; } + [JsonProperty("result")] + public RespResult? Result { get; set; } + [JsonProperty("error")] + public RespError? Error { get; set; } +} + +public class RespResult +{ + [JsonProperty("texts")] + public RespResultText[]? Texts { get; set; } + [JsonProperty("lang")] + public string Lang { get; set; } = ""; + [JsonProperty("lang_is_confident")] + public bool LangIsConfident { get; set; } + [JsonProperty("detectedLanguages")] + public RespResultDetectedLanguages? DetectedLanguages { get; set; } +} + +public class RespResultText +{ + [JsonProperty("alternatives")] + public object[]? Alternatives { get; set; } + [JsonProperty("text")] + public string Text { get; set; } = ""; +} + +public class RespResultDetectedLanguages +{ + public float EN { get; set; } + public float DE { get; set; } + public float FR { get; set; } + public float ES { get; set; } + public float PT { get; set; } + public float IT { get; set; } + public float NL { get; set; } + public float PL { get; set; } + public float RU { get; set; } + public float ZH { get; set; } + public float JA { get; set; } + public float BG { get; set; } + public float CS { get; set; } + public float DA { get; set; } + public float EL { get; set; } + public float ET { get; set; } + public float FI { get; set; } + public float HU { get; set; } + public float LT { get; set; } + public float LV { get; set; } + public float RO { get; set; } + public float SK { get; set; } + public float SL { get; set; } + public float SV { get; set; } + public float TR { get; set; } + public float ID { get; set; } + public float Unsupported { get; set; } +} + +public class RespError +{ + [JsonProperty("code")] + public int Code { get; set; } + [JsonProperty("message")] + public string Message { get; set; } = ""; } \ No newline at end of file diff --git a/STranslateDLL/STranslateDLL.csproj b/STranslateDLL/STranslateDLL.csproj index 5e4cb68..6e9d6c0 100644 --- a/STranslateDLL/STranslateDLL.csproj +++ b/STranslateDLL/STranslateDLL.csproj @@ -12,4 +12,8 @@ none + + + + diff --git a/STranslateDLLTests/LocalModeTests.cs b/STranslateDLLTests/LocalModeTests.cs index 7426e5c..bd9850e 100644 --- a/STranslateDLLTests/LocalModeTests.cs +++ b/STranslateDLLTests/LocalModeTests.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using STranslateDLL; +using System.Diagnostics; using Xunit; namespace STranslateDLL.Tests; @@ -6,11 +7,17 @@ namespace STranslateDLL.Tests; public class LocalModeTests { [Fact()] - public async Task ExecuteAsyncTest() + public async Task ExecuteAsyncTestAsync() { - var model = new LocalMode(); - Debug.WriteLine(DateTime.Now.ToLongTimeString() + "Start"); - await model.ExecuteAsync(); - Debug.WriteLine(DateTime.Now.ToLongTimeString() + "Stop"); + try + { + var cts = new CancellationTokenSource(); + var ret = await LocalMode.ExecuteAsync("Hello World", "auto", "ZH", cts.Token); + Debug.WriteLine(ret); + } + catch (Exception ex) + { + Debug.WriteLine("error " + ex.Message); + } } }