From 650e0b4ad4ecf1f625744eb9db4b00a4700b64a8 Mon Sep 17 00:00:00 2001 From: JS00000 Date: Fri, 21 Apr 2023 02:16:13 +0800 Subject: [PATCH 1/2] wechatmp: adjust log --- channel/wechatmp/passive_reply.py | 24 +++++++++++++++++++++--- channel/wechatmp/wechatmp_channel.py | 21 +++++++++------------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/channel/wechatmp/passive_reply.py b/channel/wechatmp/passive_reply.py index eca94ba..55bb5c0 100644 --- a/channel/wechatmp/passive_reply.py +++ b/channel/wechatmp/passive_reply.py @@ -62,7 +62,7 @@ class Query: channel.running.add(from_user) channel.produce(context) else: - trigger_prefix = conf().get("single_chat_prefix", [""]) + trigger_prefix = conf().get("single_chat_prefix", [""])[0] if trigger_prefix or not supported: if trigger_prefix: content = textwrap.dedent( @@ -92,13 +92,13 @@ class Query: request_cnt = channel.request_cnt.get(message_id, 0) + 1 channel.request_cnt[message_id] = request_cnt logger.info( - "[wechatmp] Request {} from {} {}\n{}\n{}:{}".format( + "[wechatmp] Request {} from {} {} {}:{}\n{}".format( request_cnt, from_user, message_id, - message, web.ctx.env.get("REMOTE_ADDR"), web.ctx.env.get("REMOTE_PORT"), + message ) ) @@ -168,12 +168,30 @@ class Query: elif (reply_type == "voice"): media_id = content asyncio.run_coroutine_threadsafe(channel.delete_media(media_id), channel.delete_media_loop) + logger.info( + "[wechatmp] Request {} do send to {} {}: {} voice media_id {}".format( + request_cnt, + from_user, + message_id, + message, + media_id, + ) + ) replyPost = VoiceMsg(from_user, to_user, media_id).send() return replyPost elif (reply_type == "image"): media_id = content asyncio.run_coroutine_threadsafe(channel.delete_media(media_id), channel.delete_media_loop) + logger.info( + "[wechatmp] Request {} do send to {} {}: {} image media_id {}".format( + request_cnt, + from_user, + message_id, + message, + media_id, + ) + ) replyPost = ImageMsg(from_user, to_user, media_id).send() return replyPost diff --git a/channel/wechatmp/wechatmp_channel.py b/channel/wechatmp/wechatmp_channel.py index 9780048..b8330ac 100644 --- a/channel/wechatmp/wechatmp_channel.py +++ b/channel/wechatmp/wechatmp_channel.py @@ -70,24 +70,22 @@ class WechatMPChannel(ChatChannel): if self.passive_reply: if reply.type == ReplyType.TEXT or reply.type == ReplyType.INFO or reply.type == ReplyType.ERROR: reply_text = reply.content - logger.info("[wechatmp] reply to {} cached:\n{}".format(receiver, reply_text)) + logger.info("[wechatmp] text cached, receiver {}\n{}".format(receiver, reply_text)) self.cache_dict[receiver] = ("text", reply_text) elif reply.type == ReplyType.VOICE: voice_file_path = reply.content - logger.info("[wechatmp] voice file path {}".format(voice_file_path)) + logger.debug("[wechatmp] voice file path {}".format(voice_file_path)) with open(voice_file_path, 'rb') as f: filename = receiver + "-" + context["msg"].msg_id + ".mp3" media_id = self.client.upload_permanent_media("voice", (filename, f, "audio/mpeg")) # 根据文件大小估计一个微信自动审核的时间,审核结束前返回将会导致语音无法播放,这个估计有待验证 f_size = os.fstat(f.fileno()).st_size - print(f_size) time.sleep(1.0 + 2 * f_size / 1024 / 1024) - logger.info("[wechatmp] voice reply to {} uploaded: {}".format(receiver, media_id)) + logger.info("[wechatmp] voice uploaded, receiver {}, media_id {}".format(receiver, media_id)) self.cache_dict[receiver] = ("voice", media_id) elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片 img_url = reply.content pic_res = requests.get(img_url, stream=True) - print(pic_res.headers) image_storage = io.BytesIO() for block in pic_res.iter_content(1024): image_storage.write(block) @@ -96,7 +94,7 @@ class WechatMPChannel(ChatChannel): filename = receiver + "-" + context["msg"].msg_id + "." + image_type content_type = "image/" + image_type media_id = self.client.upload_permanent_media("image", (filename, image_storage, content_type)) - logger.info("[wechatmp] image reply to {} uploaded: {}".format(receiver, media_id)) + logger.info("[wechatmp] image uploaded, receiver {}, media_id {}".format(receiver, media_id)) self.cache_dict[receiver] = ("image", media_id) elif reply.type == ReplyType.IMAGE: # 从文件读取图片 image_storage = reply.content @@ -105,16 +103,16 @@ class WechatMPChannel(ChatChannel): filename = receiver + "-" + context["msg"].msg_id + "." + image_type content_type = "image/" + image_type media_id = self.client.upload_permanent_media("image", (filename, image_storage, content_type)) - logger.info("[wechatmp] image reply to {} uploaded: {}".format(receiver, media_id)) + logger.info("[wechatmp] image uploaded, receiver {}, media_id {}".format(receiver, media_id)) self.cache_dict[receiver] = ("image", media_id) else: if reply.type == ReplyType.TEXT or reply.type == ReplyType.INFO or reply.type == ReplyType.ERROR: reply_text = reply.content self.client.send_text(receiver, reply_text) - logger.info("[wechatmp] Do send to {}: {}".format(receiver, reply_text)) + logger.info("[wechatmp] Do send text to {}: {}".format(receiver, reply_text)) elif reply.type == ReplyType.VOICE: voice_file_path = reply.content - logger.info("[wechatmp] voice file path {}".format(voice_file_path)) + logger.debug("[wechatmp] voice file path {}".format(voice_file_path)) with open(voice_file_path, 'rb') as f: filename = receiver + "-" + context["msg"].msg_id + ".mp3" media_id = self.client.upload_media("voice", (filename, f, "audio/mpeg")) @@ -123,7 +121,6 @@ class WechatMPChannel(ChatChannel): elif reply.type == ReplyType.IMAGE_URL: # 从网络下载图片 img_url = reply.content pic_res = requests.get(img_url, stream=True) - print(pic_res.headers) image_storage = io.BytesIO() for block in pic_res.iter_content(1024): image_storage.write(block) @@ -134,7 +131,7 @@ class WechatMPChannel(ChatChannel): # content_type = pic_res.headers.get('content-type') media_id = self.client.upload_media("image", (filename, image_storage, content_type)) self.client.send_image(receiver, media_id) - logger.info("[wechatmp] sendImage url={}, receiver={}".format(img_url, receiver)) + logger.info("[wechatmp] Do send image to {}".format(receiver)) elif reply.type == ReplyType.IMAGE: # 从文件读取图片 image_storage = reply.content image_storage.seek(0) @@ -143,7 +140,7 @@ class WechatMPChannel(ChatChannel): content_type = "image/" + image_type media_id = self.client.upload_media("image", (filename, image_storage, content_type)) self.client.send_image(receiver, media_id) - logger.info("[wechatmp] sendImage, receiver={}".format(receiver)) + logger.info("[wechatmp] Do send image to {}".format(receiver)) return def _success_callback(self, session_id, context, **kwargs): # 线程异常结束时的回调函数 From 1cd6a71ce07febc76ca8a495682506e2d88907f2 Mon Sep 17 00:00:00 2001 From: JS00000 Date: Fri, 21 Apr 2023 18:31:20 +0800 Subject: [PATCH 2/2] fix the bug of pytts in linux --- voice/pytts/pytts_voice.py | 43 +++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/voice/pytts/pytts_voice.py b/voice/pytts/pytts_voice.py index 072e28b..17cd6ff 100644 --- a/voice/pytts/pytts_voice.py +++ b/voice/pytts/pytts_voice.py @@ -2,8 +2,9 @@ pytts voice service (offline) """ +import os +import sys import time - import pyttsx3 from bridge.reply import Reply, ReplyType @@ -11,7 +12,6 @@ from common.log import logger from common.tmp_dir import TmpDir from voice.voice import Voice - class PyttsVoice(Voice): engine = pyttsx3.init() @@ -20,19 +20,42 @@ class PyttsVoice(Voice): self.engine.setProperty("rate", 125) # 音量 self.engine.setProperty("volume", 1.0) - for voice in self.engine.getProperty("voices"): - if "Chinese" in voice.name: - self.engine.setProperty("voice", voice.id) + if sys.platform == 'win32': + for voice in self.engine.getProperty("voices"): + if "Chinese" in voice.name: + self.engine.setProperty("voice", voice.id) + else: + self.engine.setProperty("voice", "zh") + # If the problem of espeak is fixed, using runAndWait() and remove this startLoop() + # TODO: check if this is work on win32 + self.engine.startLoop(useDriverLoop=False) def textToVoice(self, text): try: - wavFile = TmpDir().path() + "reply-" + str(int(time.time())) + ".wav" + # avoid the same filename + wavFileName = "reply-" + str(int(time.time())) + "-" + str(hash(text) & 0x7fffffff) + ".wav" + wavFile = TmpDir().path() + wavFileName + logger.info("[Pytts] textToVoice text={} voice file name={}".format(text, wavFile)) + self.engine.save_to_file(text, wavFile) - self.engine.runAndWait() - logger.info( - "[Pytts] textToVoice text={} voice file name={}".format(text, wavFile) - ) + + if sys.platform == 'win32': + self.engine.runAndWait() + else: + # In ubuntu, runAndWait do not really wait until the file created. + # It will return once the task queue is empty, but the task is still running in coroutine. + # And if you call runAndWait() and time.sleep() twice, it will stuck, so do not use this. + # If you want to fix this, add self._proxy.setBusy(True) in line 127 in espeak.py, at the beginning of the function save_to_file. + # self.engine.runAndWait() + + # Before espeak fix this problem, we iterate the generator and control the waiting by ourself. + # But this is not the canonical way to use it, for example if the file already exists it also cannot wait. + self.engine.iterate() + while self.engine.isBusy() or wavFileName not in os.listdir(TmpDir().path()): + time.sleep(0.1) + reply = Reply(ReplyType.VOICE, wavFile) + except Exception as e: reply = Reply(ReplyType.ERROR, str(e)) finally: