MISASI commited on
Commit
12d5b92
·
verified ·
1 Parent(s): 0898abd

Upload 7 files

Browse files
Files changed (6) hide show
  1. LICENSE +21 -0
  2. README.md +91 -10
  3. scf_bootstrap +2 -0
  4. server.js +62 -0
  5. test.js +15 -0
  6. translate.js +97 -0
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 LegendLeo
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.
README.md CHANGED
@@ -1,10 +1,91 @@
1
- ---
2
- title: DeeplXnodeless
3
- emoji: 🐢
4
- colorFrom: purple
5
- colorTo: yellow
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeepLX Serverless
2
+
3
+ DeepLX 免费翻译API**腾讯云函数部署版**,与[原项目DeepLX](https://github.com/OwO-Network/DeepLX)的区别在于**利用了云函数的请求IP不固定的特性,极大程度上避免了`429`请求太频繁报错**
4
+
5
+ 感谢原项目[OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX)提供的灵感,这是本项目的坚实基础
6
+
7
+ ## Usage | 用法
8
+
9
+ ### Prerequisites | 你需要准备什么
10
+
11
+ - 一台电脑或平板
12
+ - 一个腾讯旗下的账号或者手机号
13
+
14
+ ### Deploy | 部署
15
+
16
+ 在 [https://cloud.tencent.com/](https://cloud.tencent.com/) 注册账号
17
+
18
+ 进入云函数控制台:[https://console.cloud.tencent.com/scf/list](https://console.cloud.tencent.com/scf/list)
19
+
20
+ 依次点击【新建】->【从头开始】,然后按照以下配置,**没写出来的就不用管,使用默认设置**
21
+
22
+ - 函数类型:Web函数
23
+ - 函数名称:deeplx(名字随便取)
24
+ - 地域:任意(国内也可直连)
25
+ - 运行环境:Nodejs 16.13(或者更高的版本)
26
+ - 高级配置:
27
+ - 内存:64M
28
+ - 执行超时时间:60 秒
29
+ - 请求多并发:5 并发(个人体验下来,2个都行)
30
+ - 日志配置 -> 日志投递:启用(可以选择不开,开的话一个月应该几分钱)
31
+ - 函数代码:本地上传zip包([点我下载 ZIP 包](https://github.com/LegendLeo/deeplx-serverless/releases/download/v1.0.0/dist.zip))
32
+ - 触发器配置(这里可能要创建一个新的触发器):
33
+ - 默认触发器
34
+ - 触发别名/版本:默认流量
35
+ - 请求方法:ANY
36
+ - 发布环境:发布
37
+ - 鉴权方法:免鉴权
38
+
39
+ 此时已部署完成,可以点击“完成”按钮,进入【函数管理】,点击【函数代码】,往下拉,找到【访问路径】并复制后续使用
40
+
41
+
42
+ ### How to use | 如何使用
43
+
44
+ 建议搭配浏览器插件沉浸式翻译一同使用,使用的时候需要把访问路径里的 `/release` 部分替换为翻译路径`translate`
45
+
46
+ 例如:`https://service-aaaaa.gz.apigw.tencentcs.com/release/` 改为:`https://service-aaaaa.gz.apigw.tencentcs.com/translate`
47
+
48
+ 请求示例:
49
+
50
+ ``` bash
51
+ curl --location 'https://service-aaaaa.gz.apigw.tencentcs.com/translate' \
52
+ --header 'Content-Type: application/json' \
53
+ --data '{
54
+ "text": "你好,世界",
55
+ "source_lang": "zh",
56
+ "target_lang": "en"
57
+ }'
58
+ ```
59
+
60
+ 响应示例:
61
+
62
+ ``` json
63
+ {
64
+ "code": 200,
65
+ "message": "success",
66
+ "data": "Hello, world.",
67
+ "source_lang": "zh",
68
+ "target_lang": "en",
69
+ "alternatives": ["Hello, World.", "Hello, world!", "Hi, world."]
70
+ }
71
+ ```
72
+
73
+ #### 沉浸式翻译设置
74
+
75
+ 1. 在浏览器上安装最新的 [沉浸式翻译](https://github.com/immersive-translate/immersive-translate/releases)。
76
+ 2. 点击左下角的 "开发者设置"。启用测试版实验功能。
77
+ 3. 翻译服务选中 `DeepLX(beta)`
78
+ 3. 设置 URL 为刚才获取的访问路径(需带translate)。
79
+
80
+ ![沉浸式翻译](https://github.com/LegendLeo/deeplx-serverless/assets/25115173/d3affe2b-9e99-4d5c-bc8c-cd67e70d0368)
81
+
82
+ ## 自托管
83
+
84
+ 尽管本项目是专为 serverless 适配的方案,但是也能使用自己提供服务器进行部署
85
+
86
+ ``` bash
87
+ git clone https://github.com/LegendLeo/deeplx-serverless
88
+ cd deeplx-serverless
89
+ npm install
90
+ npm run start
91
+ ```
scf_bootstrap ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ #!/bin/sh
2
+ /var/lang/node16/bin/node server.js
server.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const bodyParser = require('body-parser');
3
+ const { translate } = require('./translate');
4
+ const cors = require('cors');
5
+ const fs = require('fs');
6
+ const app = express();
7
+ const PORT = 7860;
8
+
9
+ app.use(cors({ origin: '*' }));
10
+ app.use(bodyParser.json());
11
+
12
+ let delay = 0;
13
+ const delayIncrement = 100;
14
+ const maxDelay = 5000;
15
+
16
+ if (fs.existsSync('delay.txt')) {
17
+ delay = parseInt(fs.readFileSync('delay.txt', 'utf8'));
18
+ }
19
+
20
+ console.log('File system loaded')
21
+
22
+ app.post('/translate', async (req, res) => {
23
+
24
+ console.log('Translate start')
25
+
26
+ const { text, source_lang, target_lang } = req.body;
27
+
28
+ try {
29
+ await new Promise(resolve => setTimeout(resolve, delay));
30
+
31
+ const result = await translate(text, source_lang, target_lang);
32
+ const responseData = {
33
+ alternatives: result.alternatives,
34
+ code: 200,
35
+ data: result.text,
36
+ id: Math.floor(Math.random() * 10000000000),
37
+ method: 'Free',
38
+ source_lang,
39
+ target_lang,
40
+ };
41
+ res.json(responseData);
42
+ } catch (error) {
43
+ if (error.response && error.response.status === 429) {
44
+
45
+ delay += delayIncrement;
46
+ if (delay > maxDelay) {
47
+ delay = maxDelay;
48
+ }
49
+ fs.writeFileSync('delay.txt', delay.toString());
50
+ console.log(`429 에러 발생. 딜레이 증가: ${delay}ms`);
51
+ }
52
+ res.status(500).json({ error: 'Translation failed' });
53
+ }
54
+ });
55
+
56
+ app.get('/', (req, res) => {
57
+ res.send('서버가 구동되었습니다.');
58
+ });
59
+
60
+ app.listen(PORT, () => {
61
+ console.log(`Server is running on http://localhost:${PORT}`);
62
+ });
test.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const translate = require('./translate');
2
+
3
+ ;(async () => {
4
+ // Example Call
5
+ console.log(await translate('明天你好', 'ZH', 'EN', true, true));
6
+ console.log(
7
+ await translate(
8
+ 'Generate a cryptographically strong random string',
9
+ 'EN',
10
+ 'ZH',
11
+ true,
12
+ true
13
+ )
14
+ );
15
+ })()
translate.js ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios').default;
2
+ const { random } = require('lodash');
3
+
4
+ const DEEPL_BASE_URL = 'https://www2.deepl.com/jsonrpc';
5
+ const headers = {
6
+ 'Content-Type': 'application/json',
7
+ Accept: '*/*',
8
+ 'x-app-os-name': 'iOS',
9
+ 'x-app-os-version': '16.3.0',
10
+ 'Accept-Language': 'en-US,en;q=0.9',
11
+ 'Accept-Encoding': 'gzip, deflate, br',
12
+ 'x-app-device': 'iPhone13,2',
13
+ 'User-Agent': 'DeepL-iOS/2.9.1 iOS 16.3.0 (iPhone13,2)',
14
+ 'x-app-build': '510265',
15
+ 'x-app-version': '2.9.1',
16
+ Connection: 'keep-alive',
17
+ };
18
+
19
+ function getICount(translateText) {
20
+ return (translateText || '').split('i').length - 1;
21
+ }
22
+
23
+ function getRandomNumber() {
24
+ return random(8300000, 8399998) * 1000;
25
+ }
26
+
27
+ function getTimestamp(iCount) {
28
+ const ts = Date.now();
29
+ if (iCount === 0) {
30
+ return ts;
31
+ }
32
+ iCount++;
33
+ return ts - (ts % iCount) + iCount;
34
+ }
35
+
36
+ async function translate(
37
+ text,
38
+ sourceLang = 'AUTO',
39
+ targetLang = 'KO',
40
+ numberAlternative = 0,
41
+ printResult = false,
42
+ ) {
43
+ const iCount = getICount(text);
44
+ const id = getRandomNumber();
45
+
46
+ numberAlternative = Math.max(Math.min(3, numberAlternative), 0);
47
+
48
+ const postData = {
49
+ jsonrpc: '2.0',
50
+ method: 'LMT_handle_texts',
51
+ id: id,
52
+ params: {
53
+ texts: [{ text: text, requestAlternatives: numberAlternative }],
54
+ splitting: 'newlines',
55
+ lang: {
56
+ source_lang_user_selected: sourceLang.toUpperCase(),
57
+ target_lang: targetLang.toUpperCase(),
58
+ },
59
+ timestamp: getTimestamp(iCount),
60
+ },
61
+ };
62
+
63
+ let postDataStr = JSON.stringify(postData);
64
+
65
+ if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) {
66
+ postDataStr = postDataStr.replace('"method":"', '"method" : "');
67
+ } else {
68
+ postDataStr = postDataStr.replace('"method":"', '"method": "');
69
+ }
70
+
71
+ try {
72
+ const response = await axios.post(DEEPL_BASE_URL, postDataStr, {
73
+ headers: headers,
74
+ });
75
+
76
+ if (response.status === 429) {
77
+ throw new Error(
78
+ `Too many requests, your IP has been blocked by DeepL temporarily, please don't request it frequently in a short time.`
79
+ );
80
+ }
81
+
82
+ if (response.status !== 200) {
83
+ console.error('Error', response.status);
84
+ return;
85
+ }
86
+
87
+ const result = response.data.result.texts[0]
88
+ if (printResult) {
89
+ console.log(result);
90
+ }
91
+ return result;
92
+ } catch (err) {
93
+ console.error(err.message);
94
+ }
95
+ }
96
+
97
+ exports.translate = translate;