import web import time import channel.wechatmp.reply as reply import channel.wechatmp.receive as receive from config import conf from common.log import logger from bridge.context import * from channel.wechatmp.common import * from channel.wechatmp.wechatmp_channel import WechatMPChannel # This class is instantiated once per query class Query(): def GET(self): return verify_server(web.input()) def POST(self): # Make sure to return the instance that first created, @singleton will do that. channel = WechatMPChannel() try: query_time = time.time() webData = web.data() logger.debug("[wechatmp] Receive request:\n" + webData.decode("utf-8")) wechatmp_msg = receive.parse_xml(webData) if wechatmp_msg.msg_type == 'text' or wechatmp_msg.msg_type == 'voice': from_user = wechatmp_msg.from_user_id to_user = wechatmp_msg.to_user_id message = wechatmp_msg.content.decode("utf-8") message_id = wechatmp_msg.msg_id logger.info("[wechatmp] {}:{} Receive post query {} {}: {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), from_user, message_id, message)) supported = True if "【收到不支持的消息类型,暂无法显示】" in message: supported = False # not supported, used to refresh cache_key = from_user reply_text = "" # New request if cache_key not in channel.cache_dict and cache_key not in channel.running: # The first query begin, reset the cache context = channel._compose_context(ContextType.TEXT, message, isgroup=False, msg=wechatmp_msg) logger.debug("[wechatmp] context: {} {}".format(context, wechatmp_msg)) if message_id in channel.received_msgs: # received and finished # no return because of bandwords or other reasons return "success" if supported and context: # set private openai_api_key # if from_user is not changed in itchat, this can be placed at chat_channel user_data = conf().get_user_data(from_user) context['openai_api_key'] = user_data.get('openai_api_key') # None or user openai_api_key channel.received_msgs[message_id] = wechatmp_msg channel.running.add(cache_key) channel.produce(context) else: trigger_prefix = conf().get('single_chat_prefix',[''])[0] if trigger_prefix or not supported: if trigger_prefix: content = textwrap.dedent(f"""\ 请输入'{trigger_prefix}'接你想说的话跟我说话。 例如: {trigger_prefix}你好,很高兴见到你。""") else: content = textwrap.dedent("""\ 你好,很高兴见到你。 请跟我说话吧。""") else: logger.error(f"[wechatmp] unknown error") content = textwrap.dedent("""\ 未知错误,请稍后再试""") replyMsg = reply.TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content) return replyMsg.send() channel.query1[cache_key] = False channel.query2[cache_key] = False channel.query3[cache_key] = False # User request again, and the answer is not ready elif cache_key in channel.running and channel.query1.get(cache_key) == True and channel.query2.get(cache_key) == True and channel.query3.get(cache_key) == True: channel.query1[cache_key] = False #To improve waiting experience, this can be set to True. channel.query2[cache_key] = False #To improve waiting experience, this can be set to True. channel.query3[cache_key] = False # User request again, and the answer is ready elif cache_key in channel.cache_dict: # Skip the waiting phase channel.query1[cache_key] = True channel.query2[cache_key] = True channel.query3[cache_key] = True assert not (cache_key in channel.cache_dict and cache_key in channel.running) if channel.query1.get(cache_key) == False: # The first query from wechat official server logger.debug("[wechatmp] query1 {}".format(cache_key)) channel.query1[cache_key] = True cnt = 0 while cache_key in channel.running and cnt < 45: cnt = cnt + 1 time.sleep(0.1) if cnt == 45: # waiting for timeout (the POST query will be closed by wechat official server) time.sleep(1) # and do nothing return else: pass elif channel.query2.get(cache_key) == False: # The second query from wechat official server logger.debug("[wechatmp] query2 {}".format(cache_key)) channel.query2[cache_key] = True cnt = 0 while cache_key in channel.running and cnt < 45: cnt = cnt + 1 time.sleep(0.1) if cnt == 45: # waiting for timeout (the POST query will be closed by wechat official server) time.sleep(1) # and do nothing return else: pass elif channel.query3.get(cache_key) == False: # The third query from wechat official server logger.debug("[wechatmp] query3 {}".format(cache_key)) channel.query3[cache_key] = True cnt = 0 while cache_key in channel.running and cnt < 40: cnt = cnt + 1 time.sleep(0.1) if cnt == 40: # Have waiting for 3x5 seconds # return timeout message reply_text = "【正在思考中,回复任意文字尝试获取回复】" logger.info("[wechatmp] Three queries has finished For {}: {}".format(from_user, message_id)) replyPost = reply.TextMsg(from_user, to_user, reply_text).send() return replyPost else: pass if cache_key not in channel.cache_dict and cache_key not in channel.running: # no return because of bandwords or other reasons return "success" # if float(time.time()) - float(query_time) > 4.8: # reply_text = "【正在思考中,回复任意文字尝试获取回复】" # logger.info("[wechatmp] Timeout for {} {}, return".format(from_user, message_id)) # replyPost = reply.TextMsg(from_user, to_user, reply_text).send() # return replyPost if cache_key in channel.cache_dict: content = channel.cache_dict[cache_key] if len(content.encode('utf8'))<=MAX_UTF8_LEN: reply_text = channel.cache_dict[cache_key] channel.cache_dict.pop(cache_key) else: continue_text = "\n【未完待续,回复任意文字以继续】" splits = split_string_by_utf8_length(content, MAX_UTF8_LEN - len(continue_text.encode('utf-8')), max_split= 1) reply_text = splits[0] + continue_text channel.cache_dict[cache_key] = splits[1] logger.info("[wechatmp] {}:{} Do send {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), reply_text)) replyPost = reply.TextMsg(from_user, to_user, reply_text).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 = reply.TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content) return replyMsg.send() else: logger.info("暂且不处理") return "success" except Exception as exc: logger.exception(exc) return exc