You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 satır
7.6KB

  1. import time
  2. import web
  3. from channel.wechatmp.wechatmp_message import parse_xml
  4. from channel.wechatmp.passive_reply_message import TextMsg
  5. from bridge.context import *
  6. from channel.wechatmp.common import *
  7. from channel.wechatmp.wechatmp_channel import WechatMPChannel
  8. from common.log import logger
  9. from config import conf
  10. # This class is instantiated once per query
  11. class Query:
  12. def GET(self):
  13. return verify_server(web.input())
  14. def POST(self):
  15. try:
  16. request_time = time.time()
  17. channel = WechatMPChannel()
  18. webData = web.data()
  19. logger.debug("[wechatmp] Receive post data:\n" + webData.decode("utf-8"))
  20. wechatmp_msg = parse_xml(webData)
  21. if wechatmp_msg.msg_type == "text" or wechatmp_msg.msg_type == "voice":
  22. from_user = wechatmp_msg.from_user_id
  23. to_user = wechatmp_msg.to_user_id
  24. message = wechatmp_msg.content.decode("utf-8")
  25. message_id = wechatmp_msg.msg_id
  26. supported = True
  27. if "【收到不支持的消息类型,暂无法显示】" in message:
  28. supported = False # not supported, used to refresh
  29. # New request
  30. if (
  31. from_user not in channel.cache_dict
  32. and from_user not in channel.running
  33. or message.startswith("#")
  34. and message_id not in channel.request_cnt # insert the godcmd
  35. ):
  36. # The first query begin
  37. context = channel._compose_context(
  38. ContextType.TEXT, message, isgroup=False, msg=wechatmp_msg
  39. )
  40. logger.debug(
  41. "[wechatmp] context: {} {}".format(context, wechatmp_msg)
  42. )
  43. if supported and context:
  44. # set private openai_api_key
  45. # if from_user is not changed in itchat, this can be placed at chat_channel
  46. user_data = conf().get_user_data(from_user)
  47. context["openai_api_key"] = user_data.get("openai_api_key")
  48. channel.running.add(from_user)
  49. channel.produce(context)
  50. else:
  51. trigger_prefix = conf().get("single_chat_prefix", [""])
  52. if trigger_prefix or not supported:
  53. if trigger_prefix:
  54. content = textwrap.dedent(
  55. f"""\
  56. 请输入'{trigger_prefix}'接你想说的话跟我说话。
  57. 例如:
  58. {trigger_prefix}你好,很高兴见到你。"""
  59. )
  60. else:
  61. content = textwrap.dedent(
  62. """\
  63. 你好,很高兴见到你。
  64. 请跟我说话吧。"""
  65. )
  66. else:
  67. logger.error(f"[wechatmp] unknown error")
  68. content = textwrap.dedent(
  69. """\
  70. 未知错误,请稍后再试"""
  71. )
  72. replyPost = TextMsg(wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content).send()
  73. return replyPost
  74. # Wechat official server will request 3 times (5 seconds each), with the same message_id.
  75. # Because the interval is 5 seconds, here assumed that do not have multithreading problems.
  76. request_cnt = channel.request_cnt.get(message_id, 0) + 1
  77. channel.request_cnt[message_id] = request_cnt
  78. logger.info(
  79. "[wechatmp] Request {} from {} {}\n{}\n{}:{}".format(
  80. request_cnt,
  81. from_user,
  82. message_id,
  83. message,
  84. web.ctx.env.get("REMOTE_ADDR"),
  85. web.ctx.env.get("REMOTE_PORT"),
  86. )
  87. )
  88. task_running = True
  89. waiting_until = request_time + 4
  90. while time.time() < waiting_until:
  91. if from_user in channel.running:
  92. time.sleep(0.1)
  93. else:
  94. task_running = False
  95. break
  96. reply_text = ""
  97. if task_running:
  98. if request_cnt < 3:
  99. # waiting for timeout (the POST request will be closed by Wechat official server)
  100. time.sleep(2)
  101. # and do nothing, waiting for the next request
  102. return "success"
  103. else: # request_cnt == 3:
  104. # return timeout message
  105. reply_text = "【正在思考中,回复任意文字尝试获取回复】"
  106. # replyPost = reply.TextMsg(from_user, to_user, reply_text).send()
  107. # return replyPost
  108. # reply or reply_text is ready
  109. channel.request_cnt.pop(message_id)
  110. # no return because of bandwords or other reasons
  111. if (
  112. from_user not in channel.cache_dict
  113. and from_user not in channel.running
  114. ):
  115. return "success"
  116. # reply is ready
  117. if from_user in channel.cache_dict:
  118. # Only one message thread can access to the cached data
  119. try:
  120. content = channel.cache_dict.pop(from_user)
  121. except KeyError:
  122. return "success"
  123. if len(content.encode("utf8")) <= MAX_UTF8_LEN:
  124. reply_text = content
  125. else:
  126. continue_text = "\n【未完待续,回复任意文字以继续】"
  127. splits = split_string_by_utf8_length(
  128. content,
  129. MAX_UTF8_LEN - len(continue_text.encode("utf-8")),
  130. max_split=1,
  131. )
  132. reply_text = splits[0] + continue_text
  133. channel.cache_dict[from_user] = splits[1]
  134. logger.info(
  135. "[wechatmp] Request {} do send to {} {}: {}\n{}".format(
  136. request_cnt,
  137. from_user,
  138. message_id,
  139. message,
  140. reply_text,
  141. )
  142. )
  143. replyPost = TextMsg(from_user, to_user, reply_text).send()
  144. return replyPost
  145. elif wechatmp_msg.msg_type == "event":
  146. logger.info(
  147. "[wechatmp] Event {} from {}".format(
  148. wechatmp_msg.content, wechatmp_msg.from_user_id
  149. )
  150. )
  151. content = subscribe_msg()
  152. replyMsg = TextMsg(
  153. wechatmp_msg.from_user_id, wechatmp_msg.to_user_id, content
  154. )
  155. return replyMsg.send()
  156. else:
  157. logger.info("暂且不处理")
  158. return "success"
  159. except Exception as exc:
  160. logger.exception(exc)
  161. return exc