lintonxue00 commited on
Commit
637d234
1 Parent(s): 52bbf17

Upload ext_voice.py

Browse files
Files changed (1) hide show
  1. ext_voice.py +184 -0
ext_voice.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ 启用tecent翻译 可以在YAML 中填入下面的参数
3
+ ng_voice_translate_on : True
4
+ tencentcloud_common_region : "ap-shanghai"
5
+ tencentcloud_common_secretid : "xxxxx"
6
+ tencentcloud_common_secretkey : "xxxxx"
7
+ ng_voice_tar : 'ja'
8
+ '''
9
+
10
+ from .Extension import Extension
11
+ import urllib, requests, uuid, os, base64
12
+ from aiohttp import request
13
+ from binascii import b2a_base64
14
+ from hashlib import sha1
15
+ from hmac import new
16
+ from random import randint
17
+ from sys import maxsize, version_info
18
+ from time import time
19
+ from nonebot import get_driver
20
+ from aiohttp import request
21
+ from loguru import logger
22
+ from nonebot.exception import ActionFailed
23
+ import asyncio
24
+ import requests
25
+ import base64
26
+ import uuid
27
+ import re
28
+
29
+
30
+ try:
31
+ from ujson import loads as loadJsonS
32
+ except:
33
+ from json import loads as loadJsonS
34
+
35
+ # 拓展的配置信息,用于ai理解拓展的功能 *必填*
36
+ ext_config:dict = {
37
+ "name": "voice", # 拓展名称,用于标识拓展
38
+ "arguments": {
39
+ 'sentence': 'str', # 需要转换的文本
40
+ },
41
+ # 拓展的描述信息,用于提示ai理解拓展的功能 *必填* 尽量简短 使用英文更节省token
42
+ "description": "Send a voice sentence. (usage in response: /#voice&你好#/)",
43
+ # 参考词,用于上下文参考使用,为空则每次都会被参考(消耗token)
44
+ "refer_word": [],
45
+ # 作者信息
46
+ "author": "KroMiose",
47
+ # 版本
48
+ "version": "0.0.2",
49
+ # 拓展简介
50
+ "intro": "发送语音消息(支持翻译)",
51
+ }
52
+
53
+
54
+ # 自定义扩展
55
+ class CustomExtension(Extension):
56
+ async def call(self, arg_dict: dict, ctx_data: dict) -> dict:
57
+ """ 当拓展被调用时执行的函数 *由拓展自行实现*
58
+
59
+ 参数:
60
+ arg_dict: dict, 由ai解析的参数字典 {参数名: 参数值}
61
+ """
62
+ custom_config:dict = self.get_custom_config() # 获取yaml中的配置信息
63
+
64
+ ng_voice_translate_on = custom_config.get('ng_voice_translate_on', False) # 是否启用翻译
65
+ tencentcloud_common_region = custom_config.get('tencentcloud_common_region', "ap-shanghai") # 腾讯翻译-地区
66
+ tencentcloud_common_secretid = custom_config.get('tencentcloud_common_secretid',"xxxxx") # 腾讯翻译-密钥id
67
+ tencentcloud_common_secretkey = custom_config.get('tencentcloud_common_secretkey', "xxxxx") # 腾讯翻译-密钥
68
+ ng_voice_tar = custom_config.get('g_voice_tar', 'ja') # 翻译目标语言
69
+ is_base64 = custom_config.get('is_base64', False) # 是否使用base64编码
70
+
71
+ voice_path = 'voice_cache/'
72
+ # 创建缓存文件夹
73
+ if not os.path.exists(voice_path):
74
+ os.mkdir(voice_path)
75
+
76
+ # 获取参数
77
+ raw_text = arg_dict.get('sentence', None)
78
+
79
+ # 从这里开始翻译
80
+
81
+ # 腾讯翻译-签名
82
+ config = get_driver().config
83
+ async def getReqSign(params: dict) -> str:
84
+ common = {
85
+ "Action": "TextTranslate",
86
+ "Region": f"{tencentcloud_common_region}",
87
+ "Timestamp": int(time()),
88
+ "Nonce": randint(1, maxsize),
89
+ "SecretId": f"{tencentcloud_common_secretid}",
90
+ "Version": "2018-03-21",
91
+ }
92
+ params.update(common)
93
+ sign_str = "POSTtmt.tencentcloudapi.com/?"
94
+ sign_str += "&".join("%s=%s" % (k, params[k]) for k in sorted(params))
95
+ secret_key = tencentcloud_common_secretkey
96
+ if version_info[0] > 2:
97
+ sign_str = bytes(sign_str, "utf-8")
98
+ secret_key = bytes(secret_key, "utf-8")
99
+ hashed = new(secret_key, sign_str, sha1)
100
+ signature = b2a_base64(hashed.digest())[:-1]
101
+ if version_info[0] > 2:
102
+ signature = signature.decode()
103
+ return signature
104
+
105
+
106
+ async def q_translate(message) -> str:
107
+ _source_text = message
108
+ _source = "auto"
109
+ _target = ng_voice_tar
110
+ try:
111
+ endpoint = "https://tmt.tencentcloudapi.com"
112
+ params = {
113
+ "Source": _source,
114
+ "SourceText": _source_text,
115
+ "Target": _target,
116
+ "ProjectId": 0,
117
+ }
118
+ params["Signature"] = await getReqSign(params)
119
+ # 加上超时参数
120
+ async with request("POST", endpoint, data=params) as resp:
121
+ data = loadJsonS(await asyncio.wait_for(resp.read(), timeout=30))["Response"]
122
+ message = data["TargetText"]
123
+ except ActionFailed as e:
124
+ logger.warning(
125
+ f"ActionFailed {e.info['retcode']} {e.info['msg'].lower()} {e.info['wording']}"
126
+ )
127
+ except TimeoutError as e:
128
+ logger.warning(
129
+ f"TimeoutError {e}"
130
+ )
131
+ return message
132
+
133
+
134
+ # 到这里翻译函数结束
135
+
136
+
137
+ if ng_voice_translate_on == True :
138
+ t_result = await q_translate(raw_text)
139
+ else:
140
+ t_result = raw_text
141
+ text = t_result + '~' # 加上一个字符,避免合成语音丢失结尾
142
+ text = urllib.parse.quote(text) # url编码
143
+
144
+ # ! moegoe.azurewebsites.net 是一个公开的语音合成api,非本人搭建,请勿滥用 (tip by KroMiose)
145
+ # 该api只支持日语,如果传入其它语言,会导致合成结果不可预知
146
+ # 如果你开启了翻译就没事了
147
+ url = f"https://lintonxue00-vits-umamusume-voice-synthesizer.hf.space/api/tts?text={text}&speaker_id=0"
148
+
149
+ # todo: 如果需要使用本地的语音合成api,请取消注释下面的代码,并自行改为您的api地址,将填入合成文本的地方改为{text}
150
+ # url = f"http://127.0.0.1:23211/to_voice?text={text}"
151
+ # 去除base64字符串中的非法字符
152
+ def call(self, args, context):
153
+ url = args['url']
154
+ text = args.get('text')
155
+ is_base64 = args.get('is_base64', False)
156
+ raw_text = text
157
+
158
+ # 下载语音文件
159
+ r = requests.get(url)
160
+
161
+ # 对base64字符串进行处理
162
+ if is_base64:
163
+ pattern = re.compile('[^a-zA-Z0-9+/=]')
164
+ content = pattern.sub('', r.content.decode())
165
+ audio_data = base64.b64decode(content + b'=' * (-len(content) % 4), validate=True)
166
+ else:
167
+ audio_data = r.content
168
+
169
+ # 写入音频文件
170
+ file_name = f"{voice_path}{uuid.uuid1()}.ogg"
171
+ with open(file_name, "wb") as f:
172
+ f.write(audio_data)
173
+
174
+ local_url = f"file:///{os.path.abspath(file_name)}"
175
+
176
+ if text is not None:
177
+ return {
178
+ 'voice': local_url, # 语音url
179
+ 'text': f"[语音消息] {raw_text}", # 文本
180
+ }
181
+ return {}
182
+
183
+ def __init__(self, custom_config: dict):
184
+ super().__init__(ext_config.copy(), custom_config)