├── .cross_compile.sh ├── .github ├── FUNDING.yaml └── workflows │ ├── ci.yaml │ ├── docker.yaml │ └── release.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── compose.yaml ├── deeplx.service ├── go.mod ├── go.sum ├── img ├── 6a48ba28621f2465028f0.png └── c5c19dd89df6fae1a256d.png ├── install.sh ├── main.go ├── me.missuo.deeplx.plist ├── service ├── config.go └── service.go └── translate ├── translate.go ├── types.go └── utils.go /.cross_compile.sh: -------------------------------------------------------------------------------- 1 | 2 | ### 3 | # @Author: Vincent Young 4 | # @Date: 2022-10-20 02:19:06 5 | # @LastEditors: Vincent Yang 6 | # @LastEditTime: 2024-03-20 16:52:40 7 | # @FilePath: /DeepLX/.cross_compile.sh 8 | # @Telegram: https://t.me/missuo 9 | # 10 | # Copyright © 2022 by Vincent, All Rights Reserved. 11 | ### 12 | set -e 13 | 14 | DIST_PREFIX="deeplx" 15 | DEBUG_MODE=${2} 16 | TARGET_DIR="dist" 17 | PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64 windows/386 windows/amd64" 18 | 19 | rm -rf ${TARGET_DIR} 20 | mkdir ${TARGET_DIR} 21 | 22 | for pl in ${PLATFORMS}; do 23 | export GOOS=$(echo ${pl} | cut -d'/' -f1) 24 | export GOARCH=$(echo ${pl} | cut -d'/' -f2) 25 | export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH} 26 | if [ "${GOOS}" == "windows" ]; then 27 | export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}.exe 28 | fi 29 | 30 | echo "build => ${TARGET}" 31 | if [ "${DEBUG_MODE}" == "debug" ]; then 32 | CGO_ENABLED=0 go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \ 33 | -ldflags "-w -s" . 34 | else 35 | CGO_ENABLED=0 go build -trimpath -o ${TARGET} \ 36 | -ldflags "-w -s" . 37 | fi 38 | done 39 | -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: [missuo] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | 5 | name: CI 6 | 7 | jobs: 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v3 14 | 15 | - name: Set up Go 16 | uses: actions/setup-go@v4 17 | with: 18 | go-version: '1.24.2' 19 | 20 | - name: Install golint 21 | run: go install golang.org/x/lint/golint@latest 22 | 23 | - name: Build 24 | run: go build ./... 25 | 26 | - name: Test 27 | run: go test ./... 28 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | tags: 8 | - 'v*' 9 | 10 | env: 11 | DOCKER_IMAGE_NAME: missuo/deeplx 12 | GHCR_IMAGE_NAME: ${{ github.repository }} 13 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 14 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 15 | GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | GHCR_USERNAME: ${{ github.repository_owner }} 17 | 18 | jobs: 19 | docker_build: 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | with: 26 | fetch-depth: 0 27 | 28 | - name: Set up QEMU 29 | uses: docker/setup-qemu-action@v2 30 | with: 31 | platforms: all 32 | 33 | - name: Set up docker buildx 34 | id: buildx 35 | uses: docker/setup-buildx-action@v2 36 | with: 37 | version: latest 38 | 39 | - name: Login to DockerHub 40 | uses: docker/login-action@v2 41 | with: 42 | registry: docker.io 43 | username: ${{ env.DOCKER_USERNAME }} 44 | password: ${{ env.DOCKER_PASSWORD }} 45 | 46 | - name: Login to GHCR 47 | uses: docker/login-action@v2 48 | with: 49 | registry: ghcr.io 50 | username: ${{ env.GHCR_USERNAME }} 51 | password: ${{ env.GHCR_TOKEN }} 52 | 53 | - name: Docker meta 54 | id: meta 55 | uses: docker/metadata-action@v4 56 | with: 57 | # list of Docker images to use as base name for tags 58 | images: | 59 | docker.io/${{ env.DOCKER_IMAGE_NAME }} 60 | ghcr.io/${{ env.GHCR_IMAGE_NAME }} 61 | # generate Docker tags based on the following events/attributes 62 | tags: | 63 | type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }} 64 | type=pep440,pattern={{raw}},enable=${{ startsWith(github.ref, 'refs/tags/') }} 65 | type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }} 66 | 67 | - name: Build and push 68 | uses: docker/build-push-action@v3 69 | with: 70 | context: . 71 | platforms: linux/amd64,linux/arm64 72 | push: true 73 | tags: ${{ steps.meta.outputs.tags }} 74 | labels: ${{ steps.meta.outputs.labels }} 75 | cache-from: type=gha 76 | cache-to: type=gha,mode=max 77 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | pull_request: 6 | 7 | name: Release 8 | jobs: 9 | 10 | Build: 11 | if: startsWith(github.ref, 'refs/tags/v') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: actions/setup-go@v4 17 | with: 18 | go-version: "1.24.2" 19 | 20 | - run: bash .cross_compile.sh 21 | 22 | - name: Release 23 | uses: softprops/action-gh-release@v1 24 | with: 25 | draft: false 26 | generate_release_notes: true 27 | files: | 28 | dist/* 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | DeepLX -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24.2 AS builder 2 | WORKDIR /go/src/github.com/OwO-Network/DeepLX 3 | COPY . . 4 | RUN go get -d -v ./ 5 | RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o deeplx . 6 | 7 | FROM alpine:latest 8 | WORKDIR /app 9 | COPY --from=builder /go/src/github.com/OwO-Network/DeepLX/deeplx /app/deeplx 10 | CMD ["/app/deeplx"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 OwO Network Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | [![GitHub Workflow][1]](https://github.com/OwO-Network/DeepLX/actions) 13 | [![Go Version][2]](https://github.com/OwO-Network/DeepLX/blob/main/go.mod) 14 | [![Go Report][3]](https://goreportcard.com/badge/github.com/OwO-Network/DeepLX) 15 | [![GitHub License][4]](https://github.com/OwO-Network/DeepLX/blob/main/LICENSE) 16 | [![Docker Pulls][5]](https://hub.docker.com/r/missuo/deeplx) 17 | [![Releases][6]](https://github.com/OwO-Network/DeepLX/releases) 18 | 19 | [1]: https://img.shields.io/github/actions/workflow/status/OwO-Network/DeepLX/release.yaml?logo=github 20 | [2]: https://img.shields.io/github/go-mod/go-version/OwO-Network/DeepLX?logo=go 21 | [3]: https://goreportcard.com/badge/github.com/OwO-Network/DeepLX 22 | [4]: https://img.shields.io/github/license/OwO-Network/DeepLX 23 | [5]: https://img.shields.io/docker/pulls/missuo/deeplx?logo=docker 24 | [6]: https://img.shields.io/github/v/release/OwO-Network/DeepLX?logo=smartthings 25 | 26 | ## How to use 27 | 28 | > \[!TIP] 29 | > 30 | > Learn more about [📘 Using DeepLX](https://deeplx.owo.network) by checking it out. 31 | 32 | ## Discussion Group 33 | [Telegram Group](https://t.me/+8KDGHKJCxEVkNzll) 34 | 35 | ## Acknowledgements 36 | 37 | ### Contributors 38 | 39 | 40 | 41 | 42 | 43 | ## Activity 44 | ![Alt](https://repobeats.axiom.co/api/embed/5f473f85db27cb30028a2f3db7a560f3577a4860.svg "Repobeats analytics image") 45 | 46 | ## License 47 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FOwO-Network%2FDeepLX.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FOwO-Network%2FDeepLX?ref=badge_large) 48 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | deeplx: 3 | image: ghcr.io/owo-network/deeplx:latest 4 | restart: always 5 | ports: 6 | - "1188:1188" 7 | # environment: 8 | # - TOKEN=helloworld 9 | # - DL_SESSION=xxxxxx 10 | -------------------------------------------------------------------------------- /deeplx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=DeepLX Service 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | Restart=always 8 | WorkingDirectory=/usr/bin/ 9 | ExecStart=/usr/bin/deeplx 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/OwO-Network/DeepLX 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | github.com/abadojack/whatlanggo v1.0.1 9 | github.com/andybalholm/brotli v1.1.0 10 | github.com/gin-contrib/cors v1.6.0 11 | github.com/gin-gonic/gin v1.9.1 12 | github.com/imroc/req/v3 v3.48.0 13 | github.com/tidwall/gjson v1.14.3 14 | ) 15 | 16 | require ( 17 | github.com/bytedance/sonic v1.11.2 // indirect 18 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 19 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 20 | github.com/cloudflare/circl v1.5.0 // indirect 21 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 22 | github.com/gin-contrib/sse v0.1.0 // indirect 23 | github.com/go-playground/locales v0.14.1 // indirect 24 | github.com/go-playground/universal-translator v0.18.1 // indirect 25 | github.com/go-playground/validator/v10 v10.19.0 // indirect 26 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 27 | github.com/goccy/go-json v0.10.2 // indirect 28 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect 29 | github.com/hashicorp/errwrap v1.1.0 // indirect 30 | github.com/hashicorp/go-multierror v1.1.1 // indirect 31 | github.com/json-iterator/go v1.1.12 // indirect 32 | github.com/klauspost/compress v1.17.9 // indirect 33 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 34 | github.com/kr/text v0.2.0 // indirect 35 | github.com/leodido/go-urn v1.4.0 // indirect 36 | github.com/mattn/go-isatty v0.0.20 // indirect 37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 38 | github.com/modern-go/reflect2 v1.0.2 // indirect 39 | github.com/onsi/ginkgo/v2 v2.20.2 // indirect 40 | github.com/pelletier/go-toml/v2 v2.1.1 // indirect 41 | github.com/quic-go/qpack v0.5.1 // indirect 42 | github.com/quic-go/quic-go v0.48.2 // indirect 43 | github.com/refraction-networking/utls v1.7.0 // indirect 44 | github.com/tidwall/match v1.1.1 // indirect 45 | github.com/tidwall/pretty v1.2.0 // indirect 46 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 47 | github.com/ugorji/go/codec v1.2.12 // indirect 48 | go.uber.org/mock v0.4.0 // indirect 49 | golang.org/x/arch v0.7.0 // indirect 50 | golang.org/x/crypto v0.36.0 // indirect 51 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect 52 | golang.org/x/mod v0.21.0 // indirect 53 | golang.org/x/net v0.38.0 // indirect 54 | golang.org/x/sync v0.12.0 // indirect 55 | golang.org/x/sys v0.31.0 // indirect 56 | golang.org/x/text v0.23.0 // indirect 57 | golang.org/x/tools v0.25.0 // indirect 58 | google.golang.org/protobuf v1.34.1 // indirect 59 | gopkg.in/yaml.v3 v3.0.1 // indirect 60 | ) 61 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4= 2 | github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc= 3 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 4 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 5 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 6 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= 7 | github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= 8 | github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 9 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 10 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 11 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 12 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 13 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 14 | github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= 15 | github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 16 | github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= 17 | github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 18 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 21 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 23 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 24 | github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg= 25 | github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro= 26 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 27 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 28 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 29 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 30 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 31 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 32 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 33 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 34 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 35 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 36 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 37 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 38 | github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= 39 | github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 40 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 41 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 42 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 43 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 44 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 45 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 46 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 47 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= 48 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 49 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 50 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 51 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 52 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 53 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 54 | github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg= 55 | github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU= 56 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 57 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 58 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 59 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 60 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 61 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 62 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 63 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 64 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 65 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 66 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 67 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 68 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 69 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 70 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 71 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 72 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 73 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 74 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 75 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 76 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 77 | github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= 78 | github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= 79 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 80 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 81 | github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= 82 | github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 83 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 85 | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= 86 | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= 87 | github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= 88 | github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= 89 | github.com/refraction-networking/utls v1.7.0 h1:9JTnze/Md74uS3ZWiRAabityY0un69rOLXsBf8LGgTs= 90 | github.com/refraction-networking/utls v1.7.0/go.mod h1:lV0Gwc1/Fi+HYH8hOtgFRdHfKo4FKSn6+FdyOz9hRms= 91 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 92 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 93 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 94 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 95 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 96 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 97 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 98 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 99 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 100 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 101 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 102 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 103 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 104 | github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= 105 | github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 106 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 107 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 108 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 109 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 110 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 111 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 112 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 113 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 114 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 115 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 116 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 117 | golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= 118 | golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 119 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= 120 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 121 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= 122 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= 123 | golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= 124 | golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 125 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 126 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 127 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 128 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 129 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 130 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 131 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 132 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 133 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 134 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 135 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 136 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 137 | golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= 138 | golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= 139 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 140 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 141 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 142 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 143 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 144 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 145 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 146 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 147 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 148 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 149 | -------------------------------------------------------------------------------- /img/6a48ba28621f2465028f0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwO-Network/DeepLX/56637adc3c82c83bf26bcaf0cceb286ffc32c109/img/6a48ba28621f2465028f0.png -------------------------------------------------------------------------------- /img/c5c19dd89df6fae1a256d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwO-Network/DeepLX/56637adc3c82c83bf26bcaf0cceb286ffc32c109/img/c5c19dd89df6fae1a256d.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | ### 2 | # @Author: Vincent Young 3 | # @Date: 2023-02-12 09:53:21 4 | # @LastEditors: Vincent Young 5 | # @LastEditTime: 2023-02-12 10:01:57 6 | # @FilePath: /DeepLX/install.sh 7 | # @Telegram: https://t.me/missuo 8 | # 9 | # Copyright © 2023 by Vincent, All Rights Reserved. 10 | ### 11 | 12 | install_deeplx(){ 13 | last_version=$(curl -Ls "https://api.github.com/repos/OwO-Network/DeepLX/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') 14 | if [[ ! -n "$last_version" ]]; then 15 | echo -e "${red}Failed to detect DeepLX version, probably due to exceeding Github API limitations.${plain}" 16 | exit 1 17 | fi 18 | echo -e "DeepLX latest version: ${last_version}, Start install..." 19 | wget -q -N --no-check-certificate -O /usr/bin/deeplx https://github.com/OwO-Network/DeepLX/releases/download/${last_version}/deeplx_linux_amd64 20 | 21 | chmod +x /usr/bin/deeplx 22 | wget -q -N --no-check-certificate -O /etc/systemd/system/deeplx.service https://raw.githubusercontent.com/OwO-Network/DeepLX/main/deeplx.service 23 | systemctl daemon-reload 24 | systemctl enable deeplx 25 | systemctl start deeplx 26 | echo -e "Installed successfully, listening at 0.0.0.0:1188" 27 | } 28 | install_deeplx 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Yang 3 | * @Date: 2023-07-01 21:45:34 4 | * @LastEditors: Jason Lyu 5 | * @LastEditTime: 2025-04-08 13:45:00 6 | * @FilePath: /DeepLX/main.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | 18 | "github.com/gin-gonic/gin" 19 | 20 | "github.com/OwO-Network/DeepLX/service" 21 | ) 22 | 23 | func main() { 24 | cfg := service.InitConfig() 25 | 26 | fmt.Printf("DeepL X has been successfully launched! Listening on %v:%v\n", cfg.IP, cfg.Port) 27 | fmt.Println("Developed by sjlleo and missuo .") 28 | 29 | // Setting the application to release mode 30 | gin.SetMode(gin.ReleaseMode) 31 | 32 | app := service.Router(cfg) 33 | app.Run(fmt.Sprintf("%v:%v", cfg.IP, cfg.Port)) 34 | } 35 | -------------------------------------------------------------------------------- /me.missuo.deeplx.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | me.missuo.deeplx 7 | KeepAlive 8 | 9 | ProgramArguments 10 | 11 | /usr/local/bin/deeplx 12 | 13 | RunAtLoad 14 | 15 | OnDemand 16 | 17 | LaunchOnlyOnce 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /service/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Yang 3 | * @Date: 2024-04-23 00:39:03 4 | * @LastEditors: Jason Lyu 5 | * @LastEditTime: 2025-04-08 13:45:00 6 | * @FilePath: /DeepLX/config.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package service 14 | 15 | import ( 16 | "flag" 17 | "fmt" 18 | "os" 19 | ) 20 | 21 | type Config struct { 22 | IP string 23 | Port int 24 | Token string 25 | DlSession string 26 | Proxy string 27 | } 28 | 29 | func InitConfig() *Config { 30 | cfg := &Config{ 31 | IP: "0.0.0.0", 32 | Port: 1188, 33 | } 34 | 35 | // IP flag 36 | if ip, ok := os.LookupEnv("IP"); ok && ip != "" { 37 | cfg.IP = ip 38 | } 39 | flag.StringVar(&cfg.IP, "ip", cfg.IP, "set up the IP address to bind to") 40 | flag.StringVar(&cfg.IP, "i", cfg.IP, "set up the IP address to bind to") 41 | 42 | // Port flag 43 | if port, ok := os.LookupEnv("PORT"); ok && port != "" { 44 | fmt.Sscanf(port, "%d", &cfg.Port) 45 | } 46 | flag.IntVar(&cfg.Port, "port", cfg.Port, "set up the port to listen on") 47 | flag.IntVar(&cfg.Port, "p", cfg.Port, "set up the port to listen on") 48 | 49 | // DL Session flag 50 | flag.StringVar(&cfg.DlSession, "s", "", "set the dl-session for /v1/translate endpoint") 51 | if cfg.DlSession == "" { 52 | if dlSession, ok := os.LookupEnv("DL_SESSION"); ok { 53 | cfg.DlSession = dlSession 54 | } 55 | } 56 | 57 | // Access token flag 58 | flag.StringVar(&cfg.Token, "token", "", "set the access token for /translate endpoint") 59 | if cfg.Token == "" { 60 | if token, ok := os.LookupEnv("TOKEN"); ok { 61 | cfg.Token = token 62 | } 63 | } 64 | 65 | // HTTP Proxy flag 66 | flag.StringVar(&cfg.Proxy, "proxy", "", "set the proxy URL for HTTP requests") 67 | if cfg.Proxy == "" { 68 | if proxy, ok := os.LookupEnv("PROXY"); ok { 69 | cfg.Proxy = proxy 70 | } 71 | } 72 | 73 | flag.Parse() 74 | return cfg 75 | } 76 | -------------------------------------------------------------------------------- /service/service.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Yang 3 | * @Date: 2023-07-01 21:45:34 4 | * @LastEditors: Jason Lyu 5 | * @LastEditTime: 2025-04-08 13:45:00 6 | * @FilePath: /DeepLX/main.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package service 14 | 15 | import ( 16 | "fmt" 17 | "log" 18 | "net/http" 19 | "net/url" 20 | "os" 21 | "strings" 22 | 23 | "github.com/gin-contrib/cors" 24 | "github.com/gin-gonic/gin" 25 | 26 | "github.com/OwO-Network/DeepLX/translate" 27 | ) 28 | 29 | func authMiddleware(cfg *Config) gin.HandlerFunc { 30 | return func(c *gin.Context) { 31 | if cfg.Token != "" { 32 | providedTokenInQuery := c.Query("token") 33 | providedTokenInHeader := c.GetHeader("Authorization") 34 | 35 | // Compatability with the Bearer token format 36 | if providedTokenInHeader != "" { 37 | parts := strings.Split(providedTokenInHeader, " ") 38 | if len(parts) == 2 { 39 | if parts[0] == "Bearer" || parts[0] == "DeepL-Auth-Key" { 40 | providedTokenInHeader = parts[1] 41 | } else { 42 | providedTokenInHeader = "" 43 | } 44 | } else { 45 | providedTokenInHeader = "" 46 | } 47 | } 48 | 49 | if providedTokenInHeader != cfg.Token && providedTokenInQuery != cfg.Token { 50 | c.JSON(http.StatusUnauthorized, gin.H{ 51 | "code": http.StatusUnauthorized, 52 | "message": "Invalid access token", 53 | }) 54 | c.Abort() 55 | return 56 | } 57 | } 58 | 59 | c.Next() 60 | } 61 | } 62 | 63 | type PayloadFree struct { 64 | TransText string `json:"text"` 65 | SourceLang string `json:"source_lang"` 66 | TargetLang string `json:"target_lang"` 67 | TagHandling string `json:"tag_handling"` 68 | } 69 | 70 | type PayloadAPI struct { 71 | Text []string `json:"text"` 72 | TargetLang string `json:"target_lang"` 73 | SourceLang string `json:"source_lang"` 74 | TagHandling string `json:"tag_handling"` 75 | } 76 | 77 | func Router(cfg *Config) *gin.Engine { 78 | // Set Proxy 79 | proxyURL := os.Getenv("PROXY") 80 | if proxyURL == "" { 81 | proxyURL = cfg.Proxy 82 | } 83 | if proxyURL != "" { 84 | proxy, err := url.Parse(proxyURL) 85 | if err != nil { 86 | log.Fatalf("Failed to parse proxy URL: %v", err) 87 | } 88 | http.DefaultTransport = &http.Transport{ 89 | Proxy: http.ProxyURL(proxy), 90 | } 91 | } 92 | 93 | if cfg.Token != "" { 94 | fmt.Println("Access token is set.") 95 | } 96 | 97 | r := gin.Default() 98 | r.Use(cors.Default()) 99 | 100 | // Defining the root endpoint which returns the project details 101 | r.GET("/", func(c *gin.Context) { 102 | c.JSON(http.StatusOK, gin.H{ 103 | "code": http.StatusOK, 104 | "message": "DeepL Free API, Developed by sjlleo and missuo. Go to /translate with POST. http://github.com/OwO-Network/DeepLX", 105 | }) 106 | }) 107 | 108 | // Free API endpoint, No Pro Account required 109 | r.POST("/translate", authMiddleware(cfg), func(c *gin.Context) { 110 | req := PayloadFree{} 111 | c.BindJSON(&req) 112 | 113 | sourceLang := req.SourceLang 114 | targetLang := req.TargetLang 115 | translateText := req.TransText 116 | tagHandling := req.TagHandling 117 | 118 | proxyURL := cfg.Proxy 119 | 120 | if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { 121 | c.JSON(http.StatusBadRequest, gin.H{ 122 | "code": http.StatusBadRequest, 123 | "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", 124 | }) 125 | return 126 | } 127 | 128 | result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, "") 129 | if err != nil { 130 | log.Fatalf("Translation failed: %s", err) 131 | } 132 | 133 | if result.Code == http.StatusOK { 134 | c.JSON(http.StatusOK, gin.H{ 135 | "code": http.StatusOK, 136 | "id": result.ID, 137 | "data": result.Data, 138 | "alternatives": result.Alternatives, 139 | "source_lang": result.SourceLang, 140 | "target_lang": result.TargetLang, 141 | "method": result.Method, 142 | }) 143 | } else { 144 | c.JSON(result.Code, gin.H{ 145 | "code": result.Code, 146 | "message": result.Message, 147 | }) 148 | 149 | } 150 | }) 151 | 152 | // Pro API endpoint, Pro Account required 153 | r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) { 154 | req := PayloadFree{} 155 | c.BindJSON(&req) 156 | 157 | sourceLang := req.SourceLang 158 | targetLang := req.TargetLang 159 | translateText := req.TransText 160 | tagHandling := req.TagHandling 161 | proxyURL := cfg.Proxy 162 | 163 | dlSession := cfg.DlSession 164 | 165 | if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" { 166 | c.JSON(http.StatusBadRequest, gin.H{ 167 | "code": http.StatusBadRequest, 168 | "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.", 169 | }) 170 | return 171 | } 172 | 173 | cookie := c.GetHeader("Cookie") 174 | if cookie != "" { 175 | dlSession = strings.Replace(cookie, "dl_session=", "", -1) 176 | } 177 | 178 | if dlSession == "" { 179 | c.JSON(http.StatusUnauthorized, gin.H{ 180 | "code": http.StatusUnauthorized, 181 | "message": "No dl_session Found", 182 | }) 183 | return 184 | } else if strings.Contains(dlSession, ".") { 185 | c.JSON(http.StatusUnauthorized, gin.H{ 186 | "code": http.StatusUnauthorized, 187 | "message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.", 188 | }) 189 | return 190 | } 191 | 192 | result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, dlSession) 193 | if err != nil { 194 | log.Fatalf("Translation failed: %s", err) 195 | } 196 | 197 | if result.Code == http.StatusOK { 198 | c.JSON(http.StatusOK, gin.H{ 199 | "code": http.StatusOK, 200 | "id": result.ID, 201 | "data": result.Data, 202 | "alternatives": result.Alternatives, 203 | "source_lang": result.SourceLang, 204 | "target_lang": result.TargetLang, 205 | "method": result.Method, 206 | }) 207 | } else { 208 | c.JSON(result.Code, gin.H{ 209 | "code": result.Code, 210 | "message": result.Message, 211 | }) 212 | 213 | } 214 | }) 215 | 216 | // Free API endpoint, Consistent with the official API format 217 | r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) { 218 | proxyURL := cfg.Proxy 219 | 220 | var translateText string 221 | var targetLang string 222 | 223 | translateText = c.PostForm("text") 224 | targetLang = c.PostForm("target_lang") 225 | 226 | if translateText == "" || targetLang == "" { 227 | var jsonData struct { 228 | Text []string `json:"text"` 229 | TargetLang string `json:"target_lang"` 230 | } 231 | 232 | if err := c.BindJSON(&jsonData); err != nil { 233 | c.JSON(http.StatusBadRequest, gin.H{ 234 | "code": http.StatusBadRequest, 235 | "message": "Invalid request payload", 236 | }) 237 | return 238 | } 239 | 240 | translateText = strings.Join(jsonData.Text, "\n") 241 | targetLang = jsonData.TargetLang 242 | } 243 | 244 | result, err := translate.TranslateByDeepLX("", targetLang, translateText, "", proxyURL, "") 245 | if err != nil { 246 | log.Fatalf("Translation failed: %s", err) 247 | } 248 | 249 | if result.Code == http.StatusOK { 250 | c.JSON(http.StatusOK, gin.H{ 251 | "translations": []map[string]interface{}{ 252 | { 253 | "detected_source_language": result.SourceLang, 254 | "text": result.Data, 255 | }, 256 | }, 257 | }) 258 | } else { 259 | c.JSON(result.Code, gin.H{ 260 | "code": result.Code, 261 | "message": result.Message, 262 | }) 263 | } 264 | }) 265 | 266 | // Catch-all route to handle undefined paths 267 | r.NoRoute(func(c *gin.Context) { 268 | c.JSON(http.StatusNotFound, gin.H{ 269 | "code": http.StatusNotFound, 270 | "message": "Path not found", 271 | }) 272 | }) 273 | 274 | return r 275 | } 276 | -------------------------------------------------------------------------------- /translate/translate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Young 3 | * @Date: 2024-09-16 11:59:24 4 | * @LastEditors: Vincent Yang 5 | * @LastEditTime: 2025-04-08 14:26:33 6 | * @FilePath: /DeepLX/translate/translate.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package translate 14 | 15 | import ( 16 | "bytes" 17 | "compress/flate" 18 | "compress/gzip" 19 | "fmt" 20 | "io" 21 | "net/http" 22 | "net/url" 23 | "strings" 24 | 25 | "github.com/abadojack/whatlanggo" 26 | "github.com/imroc/req/v3" 27 | 28 | "github.com/andybalholm/brotli" 29 | "github.com/tidwall/gjson" 30 | ) 31 | 32 | // makeRequest makes an HTTP request to DeepL API 33 | func makeRequest(postData *PostData, proxyURL string, dlSession string) (gjson.Result, error) { 34 | urlFull := "https://www2.deepl.com/jsonrpc" 35 | postStr := formatPostString(postData) 36 | 37 | // Create a new req client 38 | client := req.C().SetTLSFingerprintRandomized() 39 | 40 | // Set headers 41 | headers := http.Header{ 42 | "Content-Type": []string{"application/json"}, 43 | "User-Agent": []string{"DeepL/1627620 CFNetwork/3826.500.62.2.1 Darwin/24.4.0"}, 44 | "Accept": []string{"*/*"}, 45 | "X-App-Os-Name": []string{"iOS"}, 46 | "X-App-Os-Version": []string{"18.4.0"}, 47 | "Accept-Language": []string{"en-US,en;q=0.9"}, 48 | "Accept-Encoding": []string{"gzip, deflate, br"}, // Keep this! 49 | "X-App-Device": []string{"iPhone16,2"}, 50 | "Referer": []string{"https://www.deepl.com/"}, 51 | "X-Product": []string{"translator"}, 52 | "X-App-Build": []string{"1627620"}, 53 | "X-App-Version": []string{"25.1"}, 54 | } 55 | 56 | if dlSession != "" { 57 | headers.Set("Cookie", "dl_session="+dlSession) 58 | } 59 | 60 | // Set proxy if provided 61 | if proxyURL != "" { 62 | proxy, err := url.Parse(proxyURL) 63 | if err != nil { 64 | return gjson.Result{}, err 65 | } 66 | client.SetProxyURL(proxy.String()) 67 | } 68 | 69 | // Make the request 70 | r := client.R() 71 | r.Headers = headers 72 | resp, err := r. 73 | SetBody(bytes.NewReader([]byte(postStr))). 74 | Post(urlFull) 75 | 76 | if err != nil { 77 | return gjson.Result{}, err 78 | } 79 | 80 | var bodyReader io.Reader 81 | contentEncoding := resp.Header.Get("Content-Encoding") 82 | switch contentEncoding { 83 | case "br": 84 | bodyReader = brotli.NewReader(resp.Body) 85 | case "gzip": 86 | bodyReader, err = gzip.NewReader(resp.Body) // Use gzip.NewReader 87 | if err != nil { 88 | return gjson.Result{}, fmt.Errorf("failed to create gzip reader: %w", err) 89 | } 90 | case "deflate": // Less common, but good to handle 91 | bodyReader = flate.NewReader(resp.Body) 92 | default: 93 | bodyReader = resp.Body 94 | } 95 | 96 | body, err := io.ReadAll(bodyReader) 97 | if err != nil { 98 | return gjson.Result{}, fmt.Errorf("failed to read response body: %w", err) 99 | } 100 | return gjson.ParseBytes(body), nil 101 | } 102 | 103 | // TranslateByDeepLX performs translation using DeepL API 104 | func TranslateByDeepLX(sourceLang, targetLang, text string, tagHandling string, proxyURL string, dlSession string) (DeepLXTranslationResult, error) { 105 | if text == "" { 106 | return DeepLXTranslationResult{ 107 | Code: http.StatusNotFound, 108 | Message: "No text to translate", 109 | }, nil 110 | } 111 | 112 | if tagHandling == "" { 113 | tagHandling = "plaintext" 114 | } 115 | 116 | // Split text by newlines and store them for later reconstruction 117 | textParts := strings.Split(text, "\n") 118 | var translatedParts []string 119 | var allAlternatives [][]string // Store alternatives for each part 120 | 121 | for _, part := range textParts { 122 | if strings.TrimSpace(part) == "" { 123 | translatedParts = append(translatedParts, "") 124 | allAlternatives = append(allAlternatives, []string{""}) 125 | continue 126 | } 127 | 128 | // Get detected language if source language is auto 129 | if sourceLang == "auto" || sourceLang == "" { 130 | sourceLang = strings.ToUpper(whatlanggo.DetectLang(part).Iso6391()) 131 | } 132 | 133 | // Prepare jobs from split result 134 | var jobs []Job 135 | 136 | jobs = append(jobs, Job{ 137 | Kind: "default", 138 | PreferredNumBeams: 4, 139 | RawEnContextBefore: []string{}, 140 | RawEnContextAfter: []string{}, 141 | Sentences: []Sentence{{ 142 | Prefix: "", 143 | Text: text, 144 | ID: 0, 145 | }}, 146 | }) 147 | 148 | hasRegionalVariant := false 149 | targetLangCode := targetLang 150 | targetLangParts := strings.Split(targetLang, "-") 151 | if len(targetLangParts) > 1 { 152 | targetLangCode = targetLangParts[0] 153 | hasRegionalVariant = true 154 | } 155 | 156 | // Prepare translation request 157 | id := getRandomNumber() 158 | 159 | postData := &PostData{ 160 | Jsonrpc: "2.0", 161 | Method: "LMT_handle_jobs", 162 | ID: id, 163 | Params: Params{ 164 | CommonJobParams: CommonJobParams{ 165 | Mode: "translate", 166 | Formality: "undefined", 167 | TranscribeAs: "romanize", 168 | AdvancedMode: false, 169 | TextType: tagHandling, 170 | WasSpoken: false, 171 | }, 172 | Lang: Lang{ 173 | SourceLangUserSelected: "auto", 174 | TargetLang: strings.ToUpper(targetLangCode), 175 | SourceLangComputed: strings.ToUpper(sourceLang), 176 | }, 177 | Jobs: jobs, 178 | Timestamp: getTimeStamp(getICount(part)), 179 | }, 180 | } 181 | 182 | if hasRegionalVariant { 183 | postData = &PostData{ 184 | Jsonrpc: "2.0", 185 | Method: "LMT_handle_jobs", 186 | ID: id, 187 | Params: Params{ 188 | CommonJobParams: CommonJobParams{ 189 | Mode: "translate", 190 | Formality: "undefined", 191 | TranscribeAs: "romanize", 192 | AdvancedMode: false, 193 | TextType: tagHandling, 194 | WasSpoken: false, 195 | RegionalVariant: targetLang, 196 | }, 197 | Lang: Lang{ 198 | SourceLangUserSelected: "auto", 199 | TargetLang: strings.ToUpper(targetLangCode), 200 | SourceLangComputed: strings.ToUpper(sourceLang), 201 | }, 202 | Jobs: jobs, 203 | Timestamp: getTimeStamp(getICount(part)), 204 | }, 205 | } 206 | } 207 | 208 | // Make translation request 209 | result, err := makeRequest(postData, proxyURL, dlSession) 210 | if err != nil { 211 | return DeepLXTranslationResult{ 212 | Code: http.StatusServiceUnavailable, 213 | Message: err.Error(), 214 | }, nil 215 | } 216 | 217 | // Process translation results 218 | var partTranslation string 219 | var partAlternatives []string 220 | 221 | translations := result.Get("result.translations").Array() 222 | if len(translations) > 0 { 223 | // Process main translation 224 | for _, translation := range translations { 225 | partTranslation += translation.Get("beams.0.sentences.0.text").String() + " " 226 | } 227 | partTranslation = strings.TrimSpace(partTranslation) 228 | 229 | // Process alternatives 230 | numBeams := len(translations[0].Get("beams").Array()) 231 | for i := 1; i < numBeams; i++ { // Start from 1 since 0 is the main translation 232 | var altText string 233 | for _, translation := range translations { 234 | beams := translation.Get("beams").Array() 235 | if i < len(beams) { 236 | altText += beams[i].Get("sentences.0.text").String() + " " 237 | } 238 | } 239 | if altText != "" { 240 | partAlternatives = append(partAlternatives, strings.TrimSpace(altText)) 241 | } 242 | } 243 | } 244 | 245 | if partTranslation == "" { 246 | return DeepLXTranslationResult{ 247 | Code: http.StatusServiceUnavailable, 248 | Message: "Translation failed", 249 | }, nil 250 | } 251 | 252 | translatedParts = append(translatedParts, partTranslation) 253 | allAlternatives = append(allAlternatives, partAlternatives) 254 | } 255 | 256 | // Join all translated parts with newlines 257 | translatedText := strings.Join(translatedParts, "\n") 258 | 259 | // Combine alternatives with proper newline handling 260 | var combinedAlternatives []string 261 | maxAlts := 0 262 | for _, alts := range allAlternatives { 263 | if len(alts) > maxAlts { 264 | maxAlts = len(alts) 265 | } 266 | } 267 | 268 | // Create combined alternatives preserving line structure 269 | for i := 0; i < maxAlts; i++ { 270 | var altParts []string 271 | for j, alts := range allAlternatives { 272 | if i < len(alts) { 273 | altParts = append(altParts, alts[i]) 274 | } else if len(translatedParts[j]) == 0 { 275 | altParts = append(altParts, "") // Keep empty lines 276 | } else { 277 | altParts = append(altParts, translatedParts[j]) // Use main translation if no alternative 278 | } 279 | } 280 | combinedAlternatives = append(combinedAlternatives, strings.Join(altParts, "\n")) 281 | } 282 | 283 | return DeepLXTranslationResult{ 284 | Code: http.StatusOK, 285 | ID: getRandomNumber(), // Using new ID for the complete translation 286 | Data: translatedText, 287 | Alternatives: combinedAlternatives, 288 | SourceLang: sourceLang, 289 | TargetLang: targetLang, 290 | Method: map[bool]string{true: "Pro", false: "Free"}[dlSession != ""], 291 | }, nil 292 | } 293 | -------------------------------------------------------------------------------- /translate/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Young 3 | * @Date: 2024-09-16 11:59:24 4 | * @LastEditors: Vincent Yang 5 | * @LastEditTime: 2025-03-01 04:16:07 6 | * @FilePath: /DeepLX/translate/types.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package translate 14 | 15 | // Lang represents the language settings for translation 16 | type Lang struct { 17 | SourceLangUserSelected string `json:"source_lang_user_selected"` // Can be "auto" 18 | TargetLang string `json:"target_lang"` 19 | SourceLangComputed string `json:"source_lang_computed,omitempty"` 20 | } 21 | 22 | // CommonJobParams represents common parameters for translation jobs 23 | type CommonJobParams struct { 24 | Formality string `json:"formality"` // Can be "undefined" 25 | TranscribeAs string `json:"transcribe_as"` 26 | Mode string `json:"mode"` 27 | WasSpoken bool `json:"wasSpoken"` 28 | AdvancedMode bool `json:"advancedMode"` 29 | TextType string `json:"textType"` 30 | RegionalVariant string `json:"regionalVariant,omitempty"` 31 | } 32 | 33 | // Sentence represents a sentence in the translation request 34 | type Sentence struct { 35 | Prefix string `json:"prefix"` 36 | Text string `json:"text"` 37 | ID int `json:"id"` 38 | } 39 | 40 | // Job represents a translation job 41 | type Job struct { 42 | Kind string `json:"kind"` 43 | PreferredNumBeams int `json:"preferred_num_beams"` 44 | RawEnContextBefore []string `json:"raw_en_context_before"` 45 | RawEnContextAfter []string `json:"raw_en_context_after"` 46 | Sentences []Sentence `json:"sentences"` 47 | } 48 | 49 | // Params represents parameters for translation requests 50 | type Params struct { 51 | CommonJobParams CommonJobParams `json:"commonJobParams"` 52 | Lang Lang `json:"lang"` 53 | Jobs []Job `json:"jobs"` 54 | Timestamp int64 `json:"timestamp"` 55 | } 56 | 57 | // PostData represents the complete translation request 58 | type PostData struct { 59 | Jsonrpc string `json:"jsonrpc"` 60 | Method string `json:"method"` 61 | ID int64 `json:"id"` 62 | Params Params `json:"params"` 63 | } 64 | 65 | // TranslationResponse represents the response from translation 66 | type TranslationResponse struct { 67 | Jsonrpc string `json:"jsonrpc"` 68 | ID int64 `json:"id"` 69 | Result struct { 70 | Translations []struct { 71 | Beams []struct { 72 | Sentences []SentenceResponse `json:"sentences"` 73 | NumSymbols int `json:"num_symbols"` 74 | RephraseVariant struct { // Added rephrase_variant 75 | Name string `json:"name"` 76 | } `json:"rephrase_variant"` 77 | } `json:"beams"` 78 | Quality string `json:"quality"` // Added quality 79 | } `json:"translations"` 80 | TargetLang string `json:"target_lang"` 81 | SourceLang string `json:"source_lang"` 82 | SourceLangIsConfident bool `json:"source_lang_is_confident"` 83 | DetectedLanguages map[string]interface{} `json:"detectedLanguages"` // Use interface{} for now 84 | } `json:"result"` 85 | } 86 | 87 | // SentenceResponse is a helper struct for the response sentences 88 | type SentenceResponse struct { 89 | Text string `json:"text"` 90 | IDS []int `json:"ids"` // Added IDS 91 | } 92 | 93 | // DeepLXTranslationResult represents the final translation result 94 | type DeepLXTranslationResult struct { 95 | Code int `json:"code"` 96 | ID int64 `json:"id"` 97 | Message string `json:"message,omitempty"` 98 | Data string `json:"data"` // The primary translated text 99 | Alternatives []string `json:"alternatives"` // Other possible translations 100 | SourceLang string `json:"source_lang"` 101 | TargetLang string `json:"target_lang"` 102 | Method string `json:"method"` 103 | } 104 | -------------------------------------------------------------------------------- /translate/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Vincent Young 3 | * @Date: 2024-09-16 11:59:24 4 | * @LastEditors: Vincent Yang 5 | * @LastEditTime: 2025-04-08 14:27:21 6 | * @FilePath: /DeepLX/translate/utils.go 7 | * @Telegram: https://t.me/missuo 8 | * @GitHub: https://github.com/missuo 9 | * 10 | * Copyright © 2024 by Vincent, All Rights Reserved. 11 | */ 12 | 13 | package translate 14 | 15 | import ( 16 | "encoding/json" 17 | "math/rand" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | // getICount returns the number of 'i' characters in the text 23 | func getICount(translateText string) int64 { 24 | return int64(strings.Count(translateText, "i")) 25 | } 26 | 27 | // getRandomNumber generates a random number for request ID 28 | func getRandomNumber() int64 { 29 | src := rand.NewSource(time.Now().UnixNano()) 30 | rng := rand.New(src) 31 | num := rng.Int63n(99999) + 8300000 32 | return num * 1000 33 | } 34 | 35 | // getTimeStamp generates timestamp for request based on i count 36 | func getTimeStamp(iCount int64) int64 { 37 | ts := time.Now().UnixMilli() 38 | if iCount != 0 { 39 | iCount = iCount + 1 40 | return ts - ts%iCount + iCount 41 | } 42 | return ts 43 | } 44 | 45 | // formatPostString formats the request JSON string with specific spacing rules 46 | func formatPostString(postData *PostData) string { 47 | postBytes, _ := json.Marshal(postData) 48 | postStr := string(postBytes) 49 | 50 | if (postData.ID+5)%29 == 0 || (postData.ID+3)%13 == 0 { 51 | postStr = strings.Replace(postStr, `"method":"`, `"method" : "`, 1) 52 | } else { 53 | postStr = strings.Replace(postStr, `"method":"`, `"method": "`, 1) 54 | } 55 | 56 | return postStr 57 | } 58 | --------------------------------------------------------------------------------