Skip to content

Commit

Permalink
refactor(wechatmp): use wechatpy to handle wechatmp messages
Browse files Browse the repository at this point in the history
feat(wechatmp): add support for image and voice replies
  • Loading branch information
lanvent committed Apr 20, 2023
1 parent c60f051 commit 89dd8a1
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 340 deletions.
57 changes: 26 additions & 31 deletions channel/wechatmp/active_reply.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

import web

from channel.wechatmp.wechatmp_message import parse_xml
from channel.wechatmp.passive_reply_message import TextMsg
from channel.wechatmp.wechatmp_message import WeChatMPMessage
from bridge.context import *
from bridge.reply import ReplyType
from channel.wechatmp.common import *
from channel.wechatmp.wechatmp_channel import WechatMPChannel
from wechatpy import parse_message
from common.log import logger
from config import conf

from wechatpy.replies import create_reply

# This class is instantiated once per query
class Query:
Expand All @@ -21,16 +20,22 @@ def POST(self):
# Make sure to return the instance that first created, @singleton will do that.
channel = WechatMPChannel()
try:
webData = web.data()
message = web.data() # todo crypto
# logger.debug("[wechatmp] Receive request:\n" + webData.decode("utf-8"))
wechatmp_msg = parse_xml(webData)
if (
wechatmp_msg.msg_type == "text"
or wechatmp_msg.msg_type == "voice"
# or wechatmp_msg.msg_type == "image"
):
msg = parse_message(message)
if msg.type == "event":
logger.info(
"[wechatmp] Event {} from {}".format(
msg.event, msg.source
)
)
reply_text = subscribe_msg()
replyPost = create_reply(reply_text, msg)
return replyPost.render()
wechatmp_msg = WeChatMPMessage(msg, client=channel.client)
if wechatmp_msg.ctype in [ContextType.TEXT, ContextType.IMAGE, ContextType.VOICE]:
from_user = wechatmp_msg.from_user_id
message = wechatmp_msg.content
content = wechatmp_msg.content
message_id = wechatmp_msg.msg_id

logger.info(
Expand All @@ -39,16 +44,18 @@ def POST(self):
web.ctx.env.get("REMOTE_PORT"),
from_user,
message_id,
message,
content,
)
)
if (wechatmp_msg.msg_type == "voice" and conf().get("voice_reply_voice") == True):
rtype = ReplyType.VOICE
if (msg.type == "voice" and wechatmp_msg.ctype == ContextType.TEXT):
origin_ctype = ContextType.VOICE
context = channel._compose_context(
wechatmp_msg.ctype, content, isgroup=False, origin_ctype=origin_ctype, msg=wechatmp_msg
)
else:
rtype = None
context = channel._compose_context(
ContextType.TEXT, message, isgroup=False, desire_rtype=rtype, msg=wechatmp_msg
)
context = channel._compose_context(
wechatmp_msg.ctype, content, isgroup=False, msg=wechatmp_msg
)
if context:
# set private openai_api_key
# if from_user is not changed in itchat, this can be placed at chat_channel
Expand All @@ -59,18 +66,6 @@ def POST(self):
channel.produce(context)
# The reply will be sent by channel.send() in another thread
return "success"

elif wechatmp_msg.msg_type == "event":
logger.info(
"[wechatmp] Event {} from {}".format(
wechatmp_msg.Event, wechatmp_msg.from_user_id
)
)
content = subscribe_msg()
replyMsg = TextMsg(
wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content
)
return replyMsg.send()
else:
logger.info("暂且不处理")
return "success"
Expand Down
28 changes: 10 additions & 18 deletions channel/wechatmp/common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import hashlib
import textwrap
import web

from config import conf

from wechatpy.utils import check_signature
from wechatpy.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException
MAX_UTF8_LEN = 2048


Expand All @@ -12,27 +14,17 @@ class WeChatAPIException(Exception):

def verify_server(data):
try:
if len(data) == 0:
return "None"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = conf().get("wechatmp_token") # 请按照公众平台官网\基本配置中信息填写

data_list = [token, timestamp, nonce]
data_list.sort()
sha1 = hashlib.sha1()
# map(sha1.update, data_list) #python2
sha1.update("".join(data_list).encode("utf-8"))
hashcode = sha1.hexdigest()
print("handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return ""
except Exception as Argument:
return Argument
check_signature(token, signature, timestamp, nonce)
return echostr
except InvalidSignatureException:
raise web.Forbidden("Invalid signature")
except Exception as e:
raise web.Forbidden(str(e))


def subscribe_msg():
Expand Down
96 changes: 50 additions & 46 deletions channel/wechatmp/passive_reply.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@

import web

from channel.wechatmp.wechatmp_message import parse_xml
from channel.wechatmp.passive_reply_message import TextMsg, VoiceMsg, ImageMsg
from channel.wechatmp.wechatmp_message import WeChatMPMessage
from bridge.context import *
from bridge.reply import ReplyType
from channel.wechatmp.common import *
from channel.wechatmp.wechatmp_channel import WechatMPChannel
from common.log import logger
from config import conf

from wechatpy import parse_message
from wechatpy.replies import create_reply, ImageReply, VoiceReply

# This class is instantiated once per query
class Query:
Expand All @@ -22,36 +21,49 @@ def POST(self):
try:
request_time = time.time()
channel = WechatMPChannel()
webData = web.data()
logger.debug("[wechatmp] Receive post data:\n" + webData.decode("utf-8"))
wechatmp_msg = parse_xml(webData)
if wechatmp_msg.msg_type == "text" or wechatmp_msg.msg_type == "voice":
message = web.data() # todo crypto
msg = parse_message(message)
logger.debug("[wechatmp] Receive post data:\n" + message.decode("utf-8"))

if msg.type == "event":
logger.info(
"[wechatmp] Event {} from {}".format(
msg.event, msg.source
)
)
reply_text = subscribe_msg()
replyPost = create_reply(reply_text, msg)
return replyPost.render()

wechatmp_msg = WeChatMPMessage(msg, client=channel.client)
if wechatmp_msg.ctype in [ContextType.TEXT, ContextType.IMAGE, ContextType.VOICE]:
from_user = wechatmp_msg.from_user_id
to_user = wechatmp_msg.to_user_id
message = wechatmp_msg.content
content = wechatmp_msg.content
message_id = wechatmp_msg.msg_id

supported = True
if "【收到不支持的消息类型,暂无法显示】" in message:
if "【收到不支持的消息类型,暂无法显示】" in content:
supported = False # not supported, used to refresh

# New request
if (
from_user not in channel.cache_dict
and from_user not in channel.running
or message.startswith("#")
or content.startswith("#")
and message_id not in channel.request_cnt # insert the godcmd
):
# The first query begin
if (wechatmp_msg.msg_type == "voice" and conf().get("voice_reply_voice") == True):
rtype = ReplyType.VOICE
if (msg.type == "voice" and wechatmp_msg.ctype == ContextType.TEXT):
origin_ctype = ContextType.VOICE
context = channel._compose_context(
wechatmp_msg.ctype, content, isgroup=False, origin_ctype=origin_ctype, msg=wechatmp_msg
)
else:
rtype = None
context = channel._compose_context(
ContextType.TEXT, message, isgroup=False, desire_rtype=rtype, msg=wechatmp_msg
)
context = channel._compose_context(
wechatmp_msg.ctype, content, isgroup=False, msg=wechatmp_msg
)
logger.debug(
"[wechatmp] context: {} {}".format(context, wechatmp_msg)
"[wechatmp] context: {} {} {}".format(context, wechatmp_msg, supported)
)

if supported and context:
Expand All @@ -65,26 +77,27 @@ def POST(self):
trigger_prefix = conf().get("single_chat_prefix", [""])
if trigger_prefix or not supported:
if trigger_prefix:
content = textwrap.dedent(
reply_text = textwrap.dedent(
f"""\
请输入'{trigger_prefix}'接你想说的话跟我说话。
例如:
{trigger_prefix}你好,很高兴见到你。"""
)
else:
content = textwrap.dedent(
reply_text = textwrap.dedent(
"""\
你好,很高兴见到你。
请跟我说话吧。"""
)
else:
logger.error(f"[wechatmp] unknown error")
content = textwrap.dedent(
reply_text = textwrap.dedent(
"""\
未知错误,请稍后再试"""
)
replyPost = TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content).send()
return replyPost

replyPost = create_reply(reply_text, msg)
return replyPost.render()


# Wechat official server will request 3 times (5 seconds each), with the same message_id.
Expand All @@ -96,7 +109,7 @@ def POST(self):
request_cnt,
from_user,
message_id,
message,
content,
web.ctx.env.get("REMOTE_ADDR"),
web.ctx.env.get("REMOTE_PORT"),
)
Expand All @@ -121,8 +134,8 @@ def POST(self):
else: # request_cnt == 3:
# return timeout message
reply_text = "【正在思考中,回复任意文字尝试获取回复】"
replyPost = TextMsg(from_user, to_user, reply_text).send()
return replyPost
replyPost = create_reply(reply_text, msg)
return replyPost.render()

# reply is ready
channel.request_cnt.pop(message_id)
Expand Down Expand Up @@ -158,36 +171,27 @@ def POST(self):
request_cnt,
from_user,
message_id,
message,
content,
reply_text,
)
)
replyPost = TextMsg(from_user, to_user, reply_text).send()
return replyPost
replyPost = create_reply(reply_text, msg)
return replyPost.render()


elif (reply_type == "voice"):
media_id = content
asyncio.run_coroutine_threadsafe(channel.delete_media(media_id), channel.delete_media_loop)
replyPost = VoiceMsg(from_user, to_user, media_id).send()
return replyPost
replyPost = VoiceReply(message=msg)
replyPost.media_id = media_id
return replyPost.render()

elif (reply_type == "image"):
media_id = content
asyncio.run_coroutine_threadsafe(channel.delete_media(media_id), channel.delete_media_loop)
replyPost = ImageMsg(from_user, to_user, media_id).send()
return replyPost

elif wechatmp_msg.msg_type == "event":
logger.info(
"[wechatmp] Event {} from {}".format(
wechatmp_msg.content, wechatmp_msg.from_user_id
)
)
content = subscribe_msg()
replyMsg = TextMsg(
wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content
)
return replyMsg.send()
replyPost = ImageReply(message=msg)
replyPost.media_id = media_id
return replyPost.render()
else:
logger.info("暂且不处理")
return "success"
Expand Down
Loading

0 comments on commit 89dd8a1

Please sign in to comment.