fix: unable to translate

This commit is contained in:
Vincent Yang 2024-11-01 00:43:57 -04:00
parent c678e87631
commit 62a993bb13
No known key found for this signature in database
GPG Key ID: 55F1635E821BF0E8
4 changed files with 337 additions and 406 deletions

112
main.go
View File

@ -1,8 +1,8 @@
/* /*
* @Author: Vincent Yang * @Author: Vincent Yang
* @Date: 2023-07-01 21:45:34 * @Date: 2023-07-01 21:45:34
* @LastEditors: Vincent Young * @LastEditors: Vincent Yang
* @LastEditTime: 2024-09-16 12:12:35 * @LastEditTime: 2024-11-01 00:42:58
* @FilePath: /DeepLX/main.go * @FilePath: /DeepLX/main.go
* @Telegram: https://t.me/missuo * @Telegram: https://t.me/missuo
* @GitHub: https://github.com/missuo * @GitHub: https://github.com/missuo
@ -156,68 +156,68 @@ func main() {
}) })
// Pro API endpoint, Pro Account required // Pro API endpoint, Pro Account required
r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) { // r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) {
req := PayloadFree{} // req := PayloadFree{}
c.BindJSON(&req) // c.BindJSON(&req)
sourceLang := req.SourceLang // sourceLang := req.SourceLang
targetLang := req.TargetLang // targetLang := req.TargetLang
translateText := req.TransText // translateText := req.TransText
tagHandling := req.TagHandling // tagHandling := req.TagHandling
proxyURL := cfg.Proxy // proxyURL := cfg.Proxy
dlSession := cfg.DlSession // dlSession := cfg.DlSession
if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { // if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" {
c.JSON(http.StatusBadRequest, gin.H{ // c.JSON(http.StatusBadRequest, gin.H{
"code": http.StatusBadRequest, // "code": http.StatusBadRequest,
"message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", // "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.",
}) // })
return // return
} // }
cookie := c.GetHeader("Cookie") // cookie := c.GetHeader("Cookie")
if cookie != "" { // if cookie != "" {
dlSession = strings.Replace(cookie, "dl_session=", "", -1) // dlSession = strings.Replace(cookie, "dl_session=", "", -1)
} // }
if dlSession == "" { // if dlSession == "" {
c.JSON(http.StatusUnauthorized, gin.H{ // c.JSON(http.StatusUnauthorized, gin.H{
"code": http.StatusUnauthorized, // "code": http.StatusUnauthorized,
"message": "No dl_session Found", // "message": "No dl_session Found",
}) // })
return // return
} else if strings.Contains(dlSession, ".") { // } else if strings.Contains(dlSession, ".") {
c.JSON(http.StatusUnauthorized, gin.H{ // c.JSON(http.StatusUnauthorized, gin.H{
"code": http.StatusUnauthorized, // "code": http.StatusUnauthorized,
"message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.", // "message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.",
}) // })
return // return
} // }
result, err := translate.TranslateByDeepLXPro(sourceLang, targetLang, translateText, tagHandling, dlSession, proxyURL) // result, err := translate.TranslateByDeepLXPro(sourceLang, targetLang, translateText, tagHandling, dlSession, proxyURL)
if err != nil { // if err != nil {
log.Fatalf("Translation failed: %s", err) // log.Fatalf("Translation failed: %s", err)
} // }
if result.Code == http.StatusOK { // if result.Code == http.StatusOK {
c.JSON(http.StatusOK, gin.H{ // c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK, // "code": http.StatusOK,
"id": result.ID, // "id": result.ID,
"data": result.Data, // "data": result.Data,
"alternatives": result.Alternatives, // "alternatives": result.Alternatives,
"source_lang": result.SourceLang, // "source_lang": result.SourceLang,
"target_lang": result.TargetLang, // "target_lang": result.TargetLang,
"method": result.Method, // "method": result.Method,
}) // })
} else { // } else {
c.JSON(result.Code, gin.H{ // c.JSON(result.Code, gin.H{
"code": result.Code, // "code": result.Code,
"message": result.Message, // "message": result.Message,
}) // })
} // }
}) // })
// Free API endpoint, Consistent with the official API format // Free API endpoint, Consistent with the official API format
r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) { r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) {

View File

@ -1,8 +1,8 @@
/* /*
* @Author: Vincent Young * @Author: Vincent Young
* @Date: 2024-09-16 11:59:24 * @Date: 2024-09-16 11:59:24
* @LastEditors: Vincent Young * @LastEditors: Vincent Yang
* @LastEditTime: 2024-09-16 12:09:37 * @LastEditTime: 2024-11-01 00:42:43
* @FilePath: /DeepLX/translate/translate.go * @FilePath: /DeepLX/translate/translate.go
* @Telegram: https://t.me/missuo * @Telegram: https://t.me/missuo
* @GitHub: https://github.com/missuo * @GitHub: https://github.com/missuo
@ -14,354 +14,224 @@ package translate
import ( import (
"bytes" "bytes"
"encoding/json" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"github.com/abadojack/whatlanggo"
"github.com/andybalholm/brotli" "github.com/andybalholm/brotli"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
func initDeepLXData(sourceLang string, targetLang string) *PostData { const baseURL = "https://www2.deepl.com/jsonrpc"
hasRegionalVariant := false
targetLangParts := strings.Split(targetLang, "-")
// targetLang can be "en", "pt", "pt-PT", "pt-BR" // makeRequest makes an HTTP request to DeepL API
// targetLangCode is the first part of the targetLang, e.g. "pt" in "pt-PT" func makeRequest(postData *PostData, urlMethod string, proxyURL string) (gjson.Result, error) {
targetLangCode := targetLangParts[0] urlFull := fmt.Sprintf("%s?client=chrome-extension,1.28.0&method=%s", baseURL, urlMethod)
if len(targetLangParts) > 1 {
hasRegionalVariant = true postStr := formatPostString(postData)
req, err := http.NewRequest("POST", urlFull, bytes.NewReader([]byte(postStr)))
if err != nil {
return gjson.Result{}, err
} }
commonJobParams := CommonJobParams{ // Set headers
WasSpoken: false, req.Header = http.Header{
TranscribeAS: "", "Accept": []string{"*/*"},
} "Accept-Language": []string{"en-US,en;q=0.9,zh-CN;q=0.8,zh-TW;q=0.7,zh-HK;q=0.6,zh;q=0.5"},
if hasRegionalVariant { "Authorization": []string{"None"},
commonJobParams.RegionalVariant = targetLang "Cache-Control": []string{"no-cache"},
"Content-Type": []string{"application/json"},
"DNT": []string{"1"},
"Origin": []string{"chrome-extension://cofdbpoegempjloogbagkncekinflcnj"},
"Pragma": []string{"no-cache"},
"Priority": []string{"u=1, i"},
"Referer": []string{"https://www.deepl.com/"},
"Sec-Fetch-Dest": []string{"empty"},
"Sec-Fetch-Mode": []string{"cors"},
"Sec-Fetch-Site": []string{"none"},
"Sec-GPC": []string{"1"},
"User-Agent": []string{"DeepLBrowserExtension/1.28.0 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"},
} }
return &PostData{ // Setup client with proxy if provided
var client *http.Client
if proxyURL != "" {
proxy, err := url.Parse(proxyURL)
if err != nil {
return gjson.Result{}, err
}
client = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxy)}}
} else {
client = &http.Client{}
}
resp, err := client.Do(req)
if err != nil {
return gjson.Result{}, err
}
defer resp.Body.Close()
var bodyReader io.Reader
if resp.Header.Get("Content-Encoding") == "br" {
bodyReader = brotli.NewReader(resp.Body)
} else {
bodyReader = resp.Body
}
body, err := io.ReadAll(bodyReader)
if err != nil {
return gjson.Result{}, err
}
return gjson.ParseBytes(body), nil
}
// splitText splits the input text for translation
func splitText(text string, tagHandling bool, proxyURL string) (gjson.Result, error) {
postData := &PostData{
Jsonrpc: "2.0", Jsonrpc: "2.0",
Method: "LMT_handle_texts", Method: "LMT_split_text",
ID: getRandomNumber(),
Params: Params{ Params: Params{
Splitting: "newlines", CommonJobParams: CommonJobParams{
Lang: Lang{ Mode: "translate",
SourceLangUserSelected: sourceLang,
TargetLang: targetLangCode,
}, },
CommonJobParams: commonJobParams, Lang: Lang{
LangUserSelected: "auto",
},
Texts: []string{text},
TextType: map[bool]string{true: "richtext", false: "plaintext"}[tagHandling || isRichText(text)],
}, },
} }
return makeRequest(postData, "LMT_split_text", proxyURL)
} }
func TranslateByDeepLX(sourceLang string, targetLang string, translateText string, tagHandling string, proxyURL string) (DeepLXTranslationResult, error) { // TranslateByDeepLX performs translation using DeepL API
id := getRandomNumber() func TranslateByDeepLX(sourceLang, targetLang, text string, tagHandling string, proxyURL string) (DeepLXTranslationResult, error) {
if sourceLang == "" { if text == "" {
lang := whatlanggo.DetectLang(translateText)
deepLLang := strings.ToUpper(lang.Iso6391())
sourceLang = deepLLang
}
// If target language is not specified, set it to English
if targetLang == "" {
targetLang = "EN"
}
// Handling empty translation text
if translateText == "" {
return DeepLXTranslationResult{ return DeepLXTranslationResult{
Code: http.StatusNotFound, Code: http.StatusNotFound,
Message: "No text to translate", Message: "No text to translate",
}, nil }, nil
} }
// Preparing the request data for the DeepL API // Split text first
www2URL := "https://www2.deepl.com/jsonrpc" splitResult, err := splitText(text, tagHandling == "html" || tagHandling == "xml", proxyURL)
id = id + 1
postData := initDeepLXData(sourceLang, targetLang)
text := Text{
Text: translateText,
RequestAlternatives: 3,
}
postData.ID = id
postData.Params.Texts = append(postData.Params.Texts, text)
postData.Params.Timestamp = getTimeStamp(getICount(translateText))
if tagHandling == "html" || tagHandling == "xml" {
postData.Params.TagHandling = tagHandling
}
// Marshalling the request data to JSON and making necessary string replacements
post_byte, _ := json.Marshal(postData)
postStr := string(post_byte)
// Adding spaces to the JSON string based on the ID to adhere to DeepL's request formatting rules
if (id+5)%29 == 0 || (id+3)%13 == 0 {
postStr = strings.Replace(postStr, "\"method\":\"", "\"method\" : \"", -1)
} else {
postStr = strings.Replace(postStr, "\"method\":\"", "\"method\": \"", -1)
}
// Creating a new HTTP POST request with the JSON data as the body
post_byte = []byte(postStr)
reader := bytes.NewReader(post_byte)
request, err := http.NewRequest("POST", www2URL, reader)
if err != nil { if err != nil {
log.Println(err)
return DeepLXTranslationResult{ return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable, Code: http.StatusServiceUnavailable,
Message: "Post request failed", Message: err.Error(),
}, nil }, nil
} }
// Setting HTTP headers to mimic a request from the DeepL iOS App // Get detected language if source language is auto
request.Header.Set("Content-Type", "application/json") if sourceLang == "auto" || sourceLang == "" {
request.Header.Set("Accept", "*/*") sourceLang = strings.ToLower(splitResult.Get("result.lang.detected").String())
request.Header.Set("x-app-os-name", "iOS") }
request.Header.Set("x-app-os-version", "16.3.0")
request.Header.Set("Accept-Language", "en-US,en;q=0.9")
request.Header.Set("Accept-Encoding", "gzip, deflate, br")
request.Header.Set("x-app-device", "iPhone13,2")
request.Header.Set("User-Agent", "DeepL-iOS/2.9.1 iOS 16.3.0 (iPhone13,2)")
request.Header.Set("x-app-build", "510265")
request.Header.Set("x-app-version", "2.9.1")
request.Header.Set("Connection", "keep-alive")
// Making the HTTP request to the DeepL API // Prepare jobs from split result
var client *http.Client var jobs []Job
if proxyURL != "" { chunks := splitResult.Get("result.texts.0.chunks").Array()
proxy, err := url.Parse(proxyURL) for idx, chunk := range chunks {
if err != nil { sentence := chunk.Get("sentences.0")
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable, // Handle context
Message: "Unknown error", contextBefore := []string{}
}, nil contextAfter := []string{}
if idx > 0 {
contextBefore = []string{chunks[idx-1].Get("sentences.0.text").String()}
} }
transport := &http.Transport{ if idx < len(chunks)-1 {
Proxy: http.ProxyURL(proxy), contextAfter = []string{chunks[idx+1].Get("sentences.0.text").String()}
} }
client = &http.Client{Transport: transport}
} else {
client = &http.Client{}
}
resp, err := client.Do(request) jobs = append(jobs, Job{
if err != nil { Kind: "default",
log.Println(err) PreferredNumBeams: 4,
return DeepLXTranslationResult{ RawEnContextBefore: contextBefore,
Code: http.StatusServiceUnavailable, RawEnContextAfter: contextAfter,
Message: "DeepL API request failed", Sentences: []Sentence{{
}, nil Prefix: sentence.Get("prefix").String(),
} Text: sentence.Get("text").String(),
defer resp.Body.Close() ID: idx + 1,
}},
// Handling potential Brotli compressed response body
var bodyReader io.Reader
switch resp.Header.Get("Content-Encoding") {
case "br":
bodyReader = brotli.NewReader(resp.Body)
default:
bodyReader = resp.Body
}
// Reading the response body and parsing it with gjson
body, _ := io.ReadAll(bodyReader)
// body, _ := io.ReadAll(resp.Body)
res := gjson.ParseBytes(body)
// Handling various response statuses and potential errors
if res.Get("error.code").String() == "-32600" {
log.Println(res.Get("error").String())
return DeepLXTranslationResult{
Code: http.StatusNotAcceptable,
Message: "Invalid target language",
}, nil
}
if resp.StatusCode == http.StatusTooManyRequests {
return DeepLXTranslationResult{
Code: http.StatusTooManyRequests,
Message: "Too Many Requests",
}, nil
}
var alternatives []string
res.Get("result.texts.0.alternatives").ForEach(func(key, value gjson.Result) bool {
alternatives = append(alternatives, value.Get("text").String())
return true
})
if res.Get("result.texts.0.text").String() == "" {
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "Translation failed, API returns an empty result.",
}, nil
} else {
return DeepLXTranslationResult{
Code: http.StatusOK,
ID: id,
Message: "Success",
Data: res.Get("result.texts.0.text").String(),
Alternatives: alternatives,
SourceLang: sourceLang,
TargetLang: targetLang,
Method: "Free",
}, nil
}
}
func TranslateByDeepLXPro(sourceLang string, targetLang string, translateText string, tagHandling string, dlSession string, proxyURL string) (DeepLXTranslationResult, error) {
id := getRandomNumber()
if sourceLang == "" {
lang := whatlanggo.DetectLang(translateText)
deepLLang := strings.ToUpper(lang.Iso6391())
sourceLang = deepLLang
}
// If target language is not specified, set it to English
if targetLang == "" {
targetLang = "EN"
}
// Handling empty translation text
if translateText == "" {
return DeepLXTranslationResult{
Code: http.StatusNotFound,
Message: "No text to translate",
}, nil
}
// Preparing the request data for the DeepL API
proURL := "https://api.deepl.com/jsonrpc"
id = id + 1
postData := initDeepLXData(sourceLang, targetLang)
text := Text{
Text: translateText,
RequestAlternatives: 3,
}
postData.ID = id
postData.Params.Texts = append(postData.Params.Texts, text)
postData.Params.Timestamp = getTimeStamp(getICount(translateText))
if tagHandling == "html" || tagHandling == "xml" {
postData.Params.TagHandling = tagHandling
}
// Marshalling the request data to JSON and making necessary string replacements
post_byte, _ := json.Marshal(postData)
postStr := string(post_byte)
// Adding spaces to the JSON string based on the ID to adhere to DeepL's request formatting rules
if (id+5)%29 == 0 || (id+3)%13 == 0 {
postStr = strings.Replace(postStr, "\"method\":\"", "\"method\" : \"", -1)
} else {
postStr = strings.Replace(postStr, "\"method\":\"", "\"method\": \"", -1)
}
// Creating a new HTTP POST request with the JSON data as the body
post_byte = []byte(postStr)
reader := bytes.NewReader(post_byte)
request, err := http.NewRequest("POST", proURL, reader)
if err != nil {
log.Println(err)
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "Post request failed",
}, nil
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "*/*")
request.Header.Set("Accept-Language", "en-US,en;q=0.9")
request.Header.Set("Accept-Encoding", "gzip, deflate, br")
request.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36")
request.Header.Set("Origin", "https://www.deepl.com")
request.Header.Set("Referer", "https://www.deepl.com")
request.Header.Set("Connection", "keep-alive")
request.Header.Set("Cookie", "dl_session="+dlSession)
// Making the HTTP request to the DeepL API
var client *http.Client
if proxyURL != "" {
proxy, err := url.Parse(proxyURL)
if err != nil {
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "DeepL API request failed",
}, nil
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxy),
}
client = &http.Client{Transport: transport}
} else {
client = &http.Client{}
}
resp, err := client.Do(request)
if err != nil {
log.Println(err)
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "DeepL API request failed",
}, nil
}
defer resp.Body.Close()
// Handling potential Brotli compressed response body
var bodyReader io.Reader
switch resp.Header.Get("Content-Encoding") {
case "br":
bodyReader = brotli.NewReader(resp.Body)
default:
bodyReader = resp.Body
}
// Reading the response body and parsing it with gjson
body, _ := io.ReadAll(bodyReader)
// body, _ := io.ReadAll(resp.Body)
res := gjson.ParseBytes(body)
if res.Get("error.code").String() == "-32600" {
log.Println(res.Get("error").String())
return DeepLXTranslationResult{
Code: http.StatusNotAcceptable,
Message: "Invalid target language",
}, nil
}
if resp.StatusCode == http.StatusTooManyRequests {
return DeepLXTranslationResult{
Code: http.StatusTooManyRequests,
Message: "Too Many Requests",
}, nil
} else if resp.StatusCode == http.StatusUnauthorized {
return DeepLXTranslationResult{
Code: http.StatusUnauthorized,
Message: "dlsession is invalid",
}, nil
} else {
var alternatives []string
res.Get("result.texts.0.alternatives").ForEach(func(key, value gjson.Result) bool {
alternatives = append(alternatives, value.Get("text").String())
return true
}) })
if res.Get("result.texts.0.text").String() == "" {
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "Translation failed, API returns an empty result.",
}, nil
} else {
return DeepLXTranslationResult{
Code: http.StatusOK,
ID: id,
Message: "Success",
Data: res.Get("result.texts.0.text").String(),
Alternatives: alternatives,
SourceLang: sourceLang,
TargetLang: targetLang,
Method: "Pro",
}, nil
}
} }
// Prepare translation request
id := getRandomNumber()
postData := &PostData{
Jsonrpc: "2.0",
Method: "LMT_handle_jobs",
ID: id,
Params: Params{
CommonJobParams: CommonJobParams{
Mode: "translate",
},
Lang: Lang{
SourceLangComputed: strings.ToUpper(sourceLang),
TargetLang: strings.ToUpper(targetLang),
},
Jobs: jobs,
Priority: 1,
Timestamp: getTimeStamp(getICount(text)),
},
}
// Make translation request
result, err := makeRequest(postData, "LMT_handle_jobs", proxyURL)
if err != nil {
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: err.Error(),
}, nil
}
// Process translation results
var alternatives []string
var translatedText string
translations := result.Get("result.translations").Array()
if len(translations) > 0 {
// Get alternatives
numBeams := len(translations[0].Get("beams").Array())
for i := 0; i < numBeams; i++ {
var altText string
for _, translation := range translations {
beams := translation.Get("beams").Array()
if i < len(beams) {
altText += beams[i].Get("sentences.0.text").String()
}
}
if altText != "" {
alternatives = append(alternatives, altText)
}
}
// Get main translation
for _, translation := range translations {
translatedText += translation.Get("beams.0.sentences.0.text").String() + " "
}
translatedText = strings.TrimSpace(translatedText)
}
if translatedText == "" {
return DeepLXTranslationResult{
Code: http.StatusServiceUnavailable,
Message: "Translation failed",
}, nil
}
return DeepLXTranslationResult{
Code: http.StatusOK,
ID: id,
Data: translatedText,
Alternatives: alternatives,
SourceLang: sourceLang,
TargetLang: targetLang,
Method: "Free",
}, nil
} }

View File

@ -1,8 +1,8 @@
/* /*
* @Author: Vincent Young * @Author: Vincent Young
* @Date: 2024-09-16 11:59:24 * @Date: 2024-09-16 11:59:24
* @LastEditors: Vincent Young * @LastEditors: Vincent Yang
* @LastEditTime: 2024-09-16 12:06:36 * @LastEditTime: 2024-11-01 00:39:49
* @FilePath: /DeepLX/translate/types.go * @FilePath: /DeepLX/translate/types.go
* @Telegram: https://t.me/missuo * @Telegram: https://t.me/missuo
* @GitHub: https://github.com/missuo * @GitHub: https://github.com/missuo
@ -12,31 +12,46 @@
package translate package translate
// Lang represents the language settings for translation
type Lang struct { type Lang struct {
SourceLangUserSelected string `json:"source_lang_user_selected"` SourceLangComputed string `json:"source_lang_computed,omitempty"`
TargetLang string `json:"target_lang"` TargetLang string `json:"target_lang"`
LangUserSelected string `json:"lang_user_selected,omitempty"`
} }
// CommonJobParams represents common parameters for translation jobs
type CommonJobParams struct { type CommonJobParams struct {
WasSpoken bool `json:"wasSpoken"` Mode string `json:"mode"`
TranscribeAS string `json:"transcribe_as"`
RegionalVariant string `json:"regionalVariant,omitempty"`
} }
// Sentence represents a sentence in the translation request
type Sentence struct {
Prefix string `json:"prefix"`
Text string `json:"text"`
ID int `json:"id"`
}
// Job represents a translation job
type Job struct {
Kind string `json:"kind"`
PreferredNumBeams int `json:"preferred_num_beams"`
RawEnContextBefore []string `json:"raw_en_context_before"`
RawEnContextAfter []string `json:"raw_en_context_after"`
Sentences []Sentence `json:"sentences"`
}
// Params represents parameters for translation requests
type Params struct { type Params struct {
Texts []Text `json:"texts"`
Splitting string `json:"splitting"`
Lang Lang `json:"lang"`
Timestamp int64 `json:"timestamp"`
CommonJobParams CommonJobParams `json:"commonJobParams"` CommonJobParams CommonJobParams `json:"commonJobParams"`
TagHandling string `json:"tag_handling"` Lang Lang `json:"lang"`
} Texts []string `json:"texts,omitempty"`
TextType string `json:"textType,omitempty"`
type Text struct { Jobs []Job `json:"jobs,omitempty"`
Text string `json:"text"` Priority int `json:"priority,omitempty"`
RequestAlternatives int `json:"requestAlternatives"` Timestamp int64 `json:"timestamp"`
} }
// PostData represents the complete translation request
type PostData struct { type PostData struct {
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"` Method string `json:"method"`
@ -44,26 +59,50 @@ type PostData struct {
Params Params `json:"params"` Params Params `json:"params"`
} }
type Translation struct { // SplitTextResponse represents the response from text splitting
Text string `json:"text"` type SplitTextResponse struct {
Jsonrpc string `json:"jsonrpc"`
ID int64 `json:"id"`
Result struct {
Lang struct {
Detected string `json:"detected"`
} `json:"lang"`
Texts []struct {
Chunks []struct {
Sentences []struct {
Prefix string `json:"prefix"`
Text string `json:"text"`
} `json:"sentences"`
} `json:"chunks"`
} `json:"texts"`
} `json:"result"`
} }
// TranslationResponse represents the response from translation
type TranslationResponse struct { type TranslationResponse struct {
Translations []Translation `json:"translations"` Jsonrpc string `json:"jsonrpc"`
} ID int64 `json:"id"`
Result struct {
type DeepLUsageResponse struct { Translations []struct {
CharacterCount int `json:"character_count"` Beams []struct {
CharacterLimit int `json:"character_limit"` Sentences []struct {
Text string `json:"text"`
} `json:"sentences"`
} `json:"beams"`
} `json:"translations"`
SourceLang string `json:"source_lang"`
TargetLang string `json:"target_lang"`
} `json:"result"`
} }
// DeepLXTranslationResult represents the final translation result
type DeepLXTranslationResult struct { type DeepLXTranslationResult struct {
Code int Code int `json:"code"`
ID int64 ID int64 `json:"id"`
Message string Message string `json:"message,omitempty"`
Data string Data string `json:"data"`
Alternatives []string Alternatives []string `json:"alternatives"`
SourceLang string SourceLang string `json:"source_lang"`
TargetLang string TargetLang string `json:"target_lang"`
Method string Method string `json:"method"`
} }

View File

@ -1,8 +1,8 @@
/* /*
* @Author: Vincent Young * @Author: Vincent Young
* @Date: 2024-09-16 11:59:24 * @Date: 2024-09-16 11:59:24
* @LastEditors: Vincent Young * @LastEditors: Vincent Yang
* @LastEditTime: 2024-09-16 12:06:44 * @LastEditTime: 2024-11-01 00:39:32
* @FilePath: /DeepLX/translate/utils.go * @FilePath: /DeepLX/translate/utils.go
* @Telegram: https://t.me/missuo * @Telegram: https://t.me/missuo
* @GitHub: https://github.com/missuo * @GitHub: https://github.com/missuo
@ -13,15 +13,18 @@
package translate package translate
import ( import (
"encoding/json"
"math/rand" "math/rand"
"strings" "strings"
"time" "time"
) )
// getICount returns the number of 'i' characters in the text
func getICount(translateText string) int64 { func getICount(translateText string) int64 {
return int64(strings.Count(translateText, "i")) return int64(strings.Count(translateText, "i"))
} }
// getRandomNumber generates a random number for request ID
func getRandomNumber() int64 { func getRandomNumber() int64 {
src := rand.NewSource(time.Now().UnixNano()) src := rand.NewSource(time.Now().UnixNano())
rng := rand.New(src) rng := rand.New(src)
@ -29,12 +32,31 @@ func getRandomNumber() int64 {
return num * 1000 return num * 1000
} }
// getTimeStamp generates timestamp for request based on i count
func getTimeStamp(iCount int64) int64 { func getTimeStamp(iCount int64) int64 {
ts := time.Now().UnixMilli() ts := time.Now().UnixMilli()
if iCount != 0 { if iCount != 0 {
iCount = iCount + 1 iCount = iCount + 1
return ts - ts%iCount + iCount return ts - ts%iCount + iCount
} else {
return ts
} }
return ts
}
// formatPostString formats the request JSON string with specific spacing rules
func formatPostString(postData *PostData) string {
postBytes, _ := json.Marshal(postData)
postStr := string(postBytes)
if (postData.ID+5)%29 == 0 || (postData.ID+3)%13 == 0 {
postStr = strings.Replace(postStr, `"method":"`, `"method" : "`, 1)
} else {
postStr = strings.Replace(postStr, `"method":"`, `"method": "`, 1)
}
return postStr
}
// isRichText checks if text contains HTML-like tags
func isRichText(text string) bool {
return strings.Contains(text, "<") && strings.Contains(text, ">")
} }