lintonxue00 commited on
Commit
3c5f668
1 Parent(s): d64834a

Upload ext_voice.py

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