Browse Source

Merge Pull Ruquest #757 into master

master
lanvent 1 year ago
parent
commit
7e7484a27d
7 changed files with 171 additions and 233 deletions
  1. +2
    -1
      bot/chatgpt/chat_gpt_bot.py
  2. +2
    -2
      channel/channel_factory.py
  3. +12
    -1
      channel/wechatmp/README.md
  4. +31
    -36
      channel/wechatmp/receive.py
  5. +118
    -188
      channel/wechatmp/wechatmp_channel.py
  6. +2
    -1
      config.py
  7. +4
    -4
      plugins/role/roles.json

+ 2
- 1
bot/chatgpt/chat_gpt_bot.py View File

@@ -91,7 +91,8 @@ class ChatGPTBot(Bot,OpenAIImage):
"top_p":1, "top_p":1,
"frequency_penalty":conf().get('frequency_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容 "frequency_penalty":conf().get('frequency_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
"presence_penalty":conf().get('presence_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容 "presence_penalty":conf().get('presence_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
"request_timeout": conf().get('request_timeout', 30), # 请求超时时间
"request_timeout": conf().get('request_timeout', 60), # 请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
"timeout": conf().get('request_timeout', 120), #重试超时时间,在这个时间内,将会自动重试
} }


def reply_text(self, session:ChatGPTSession, session_id, api_key, retry_count=0) -> dict: def reply_text(self, session:ChatGPTSession, session_id, api_key, retry_count=0) -> dict:


+ 2
- 2
channel/channel_factory.py View File

@@ -18,6 +18,6 @@ def create_channel(channel_type):
from channel.terminal.terminal_channel import TerminalChannel from channel.terminal.terminal_channel import TerminalChannel
return TerminalChannel() return TerminalChannel()
elif channel_type == 'wechatmp': elif channel_type == 'wechatmp':
from channel.wechatmp.wechatmp_channel import WechatMPServer
return WechatMPServer()
from channel.wechatmp.wechatmp_channel import WechatMPChannel
return WechatMPChannel()
raise RuntimeError raise RuntimeError

+ 12
- 1
channel/wechatmp/README.md View File

@@ -18,7 +18,18 @@ pip3 install web.py


然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](https://mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址`URL`和令牌`Token`。这里的`URL`是`example.com/wx`的形式,不可以使用IP,`Token`是你自己编的一个特定的令牌。消息加解密方式目前选择的是明文模式。 然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](https://mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址`URL`和令牌`Token`。这里的`URL`是`example.com/wx`的形式,不可以使用IP,`Token`是你自己编的一个特定的令牌。消息加解密方式目前选择的是明文模式。


相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要在本项目根目录的`config.json`中添加`"channel_type": "wechatmp", "wechatmp_token": "your Token", ` 然后运行`python3 app.py`启动web服务器,然后在刚才的“服务器配置”中点击`提交`即可验证你的服务器。
相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要在本项目根目录的`config.json`中添加
```
"channel_type": "wechatmp",
"wechatmp_token": "your Token",
```
然后运行`python3 app.py`启动web服务器。这里会默认监听8080端口,但是微信公众号的服务器配置只支持80/443端口,有两种方法来解决这个问题。第一个是推荐的方法,使用端口转发命令将80端口转发到8080端口(443同理,注意需要支持SSL,也就是https的访问,在`wechatmp_channel.py`需要修改相应的证书路径):
```
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables-save > /etc/iptables/rules.v4
```
第二个方法是让python程序直接监听80端口,可以直接使用命令`python3 app.py 80`。这样可能会导致权限问题,在linux上需要使用`sudo`。然而这会导致后续缓存文件的权限问题,因此不是推荐的方法。
最后在刚才的“服务器配置”中点击`提交`即可验证你的服务器。


随后在[微信公众平台](https://mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。 随后在[微信公众平台](https://mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。




+ 31
- 36
channel/wechatmp/receive.py View File

@@ -1,47 +1,42 @@
# -*- coding: utf-8 -*-# # -*- coding: utf-8 -*-#
# filename: receive.py # filename: receive.py
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from bridge.context import ContextType
from channel.chat_message import ChatMessage
from common.log import logger




def parse_xml(web_data): def parse_xml(web_data):
if len(web_data) == 0: if len(web_data) == 0:
return None return None
xmlData = ET.fromstring(web_data) xmlData = ET.fromstring(web_data)
msg_type = xmlData.find('MsgType').text
if msg_type == 'text':
return TextMsg(xmlData)
elif msg_type == 'image':
return ImageMsg(xmlData)
elif msg_type == 'event':
return Event(xmlData)
return WeChatMPMessage(xmlData)



class Msg(object):
def __init__(self, xmlData):
self.ToUserName = xmlData.find('ToUserName').text
self.FromUserName = xmlData.find('FromUserName').text
self.CreateTime = xmlData.find('CreateTime').text
self.MsgType = xmlData.find('MsgType').text
self.MsgId = xmlData.find('MsgId').text


class TextMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Content = xmlData.find('Content').text.encode("utf-8")


class ImageMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.PicUrl = xmlData.find('PicUrl').text
self.MediaId = xmlData.find('MediaId').text


class Event(object):
class WeChatMPMessage(ChatMessage):
def __init__(self, xmlData): def __init__(self, xmlData):
self.ToUserName = xmlData.find('ToUserName').text
self.FromUserName = xmlData.find('FromUserName').text
self.CreateTime = xmlData.find('CreateTime').text
self.MsgType = xmlData.find('MsgType').text
self.Event = xmlData.find('Event').text
super().__init__(xmlData)
self.to_user_id = xmlData.find('ToUserName').text
self.from_user_id = xmlData.find('FromUserName').text
self.create_time = xmlData.find('CreateTime').text
self.msg_type = xmlData.find('MsgType').text
self.msg_id = xmlData.find('MsgId').text
self.is_group = False
# reply to other_user_id
self.other_user_id = self.from_user_id

if self.msg_type == 'text':
self.ctype = ContextType.TEXT
self.content = xmlData.find('Content').text.encode("utf-8")
elif self.msg_type == 'voice':
self.ctype = ContextType.TEXT
self.content = xmlData.find('Recognition').text.encode("utf-8") # 接收语音识别结果
elif self.msg_type == 'image':
# not implemented
self.pic_url = xmlData.find('PicUrl').text
self.media_id = xmlData.find('MediaId').text
elif self.msg_type == 'event':
self.event = xmlData.find('Event').text
else: # video, shortvideo, location, link
# not implemented
pass

+ 118
- 188
channel/wechatmp/wechatmp_channel.py View File

@@ -4,9 +4,10 @@ import time
import math import math
import hashlib import hashlib
import textwrap import textwrap
from channel.channel import Channel
from channel.chat_channel import ChatChannel
import channel.wechatmp.reply as reply import channel.wechatmp.reply as reply
import channel.wechatmp.receive as receive import channel.wechatmp.receive as receive
from common.singleton import singleton
from common.log import logger from common.log import logger
from config import conf from config import conf
from bridge.reply import * from bridge.reply import *
@@ -21,202 +22,136 @@ import traceback
# certificate='/ssl/cert.pem', # certificate='/ssl/cert.pem',
# private_key='/ssl/cert.key') # private_key='/ssl/cert.key')


class WechatMPServer():

# from concurrent.futures import ThreadPoolExecutor
# thread_pool = ThreadPoolExecutor(max_workers=8)

@singleton
class WechatMPChannel(ChatChannel):
def __init__(self): def __init__(self):
pass
super().__init__()
self.cache_dict = dict()
self.query1 = dict()
self.query2 = dict()
self.query3 = dict()



def startup(self):
def startup(self):
urls = ( urls = (
'/wx', 'WechatMPChannel',
'/wx', 'SubsribeAccountQuery',
) )
app = web.application(urls, globals()) app = web.application(urls, globals())
web.httpserver.runsimple(app.wsgifunc(), ('0.0.0.0', 80))

cache_dict = dict()
query1 = dict()
query2 = dict()
query3 = dict()

from concurrent.futures import ThreadPoolExecutor
thread_pool = ThreadPoolExecutor(max_workers=8)

class WechatMPChannel(Channel):

def GET(self):
try:
data = web.input()
if len(data) == 0:
return "hello, this is handle view"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = conf().get('wechatmp_token') #请按照公众平台官网\基本配置中信息填写
app.run()


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


def _do_build_reply(self, cache_key, fromUser, message):
context = dict()
context['session_id'] = fromUser
reply_text = super().build_reply_content(message, context)
# The query is done, record the cache
logger.info("[threaded] Get reply for {}: {} \nA: {}".format(fromUser, message, reply_text))
global cache_dict
reply_cnt = math.ceil(len(reply_text) / 600)
cache_dict[cache_key] = (reply_cnt, reply_text)



def send(self, reply : Reply, cache_key):
global cache_dict
def send(self, reply: Reply, context: Context):
reply_cnt = math.ceil(len(reply.content) / 600) reply_cnt = math.ceil(len(reply.content) / 600)
cache_dict[cache_key] = (reply_cnt, reply.content)


def handle(self, context):
global cache_dict
try:
reply = Reply()
logger.debug('[wechatmp] ready to handle context: {}'.format(context))

# reply的构建步骤
e_context = PluginManager().emit_event(EventContext(Event.ON_HANDLE_CONTEXT, {'channel' : self, 'context': context, 'reply': reply}))
reply = e_context['reply']
if not e_context.is_pass():
logger.debug('[wechatmp] ready to handle context: type={}, content={}'.format(context.type, context.content))
if context.type == ContextType.TEXT or context.type == ContextType.IMAGE_CREATE:
reply = super().build_reply_content(context.content, context)
# elif context.type == ContextType.VOICE:
# msg = context['msg']
# file_name = TmpDir().path() + context.content
# msg.download(file_name)
# reply = super().build_voice_to_text(file_name)
# if reply.type != ReplyType.ERROR and reply.type != ReplyType.INFO:
# context.content = reply.content # 语音转文字后,将文字内容作为新的context
# context.type = ContextType.TEXT
# reply = super().build_reply_content(context.content, context)
# if reply.type == ReplyType.TEXT:
# if conf().get('voice_reply_voice'):
# reply = super().build_text_to_voice(reply.content)
else:
logger.error('[wechatmp] unknown context type: {}'.format(context.type))
return

logger.debug('[wechatmp] ready to decorate reply: {}'.format(reply))

# reply的包装步骤
if reply and reply.type:
e_context = PluginManager().emit_event(EventContext(Event.ON_DECORATE_REPLY, {'channel' : self, 'context': context, 'reply': reply}))
reply=e_context['reply']
if not e_context.is_pass() and reply and reply.type:
if reply.type == ReplyType.TEXT:
pass
elif reply.type == ReplyType.ERROR or reply.type == ReplyType.INFO:
reply.content = str(reply.type)+":\n" + reply.content
elif reply.type == ReplyType.IMAGE_URL or reply.type == ReplyType.VOICE or reply.type == ReplyType.IMAGE:
pass
else:
logger.error('[wechatmp] unknown reply type: {}'.format(reply.type))
return

# reply的发送步骤
if reply and reply.type:
e_context = PluginManager().emit_event(EventContext(Event.ON_SEND_REPLY, {'channel' : self, 'context': context, 'reply': reply}))
reply=e_context['reply']
if not e_context.is_pass() and reply and reply.type:
logger.debug('[wechatmp] ready to send reply: {} to {}'.format(reply, context['receiver']))
self.send(reply, context['receiver'])
else:
cache_dict[context['receiver']] = (1, "No reply")

logger.info("[threaded] Get reply for {}: {} \nA: {}".format(context['receiver'], context.content, reply.content))
except Exception as exc:
print(traceback.format_exc())
cache_dict[context['receiver']] = (1, "ERROR")

receiver = context["receiver"]
self.cache_dict[receiver] = (reply_cnt, reply.content)
logger.debug("[send] reply to {} saved to cache: {}".format(receiver, reply))


def verify_server():
try:
data = web.input()
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


# This class is instantiated once per query
class SubsribeAccountQuery():


def GET(self):
return verify_server()


def POST(self): def POST(self):
channel_instance = WechatMPChannel()
try: try:
queryTime = time.time()
query_time = time.time()
webData = web.data() webData = web.data()
# logger.debug("[wechatmp] Receive request:\n" + webData.decode("utf-8")) # logger.debug("[wechatmp] Receive request:\n" + webData.decode("utf-8"))
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
fromUser = recMsg.FromUserName
toUser = recMsg.ToUserName
createTime = recMsg.CreateTime
message = recMsg.Content.decode("utf-8")
message_id = recMsg.MsgId
wechat_msg = receive.parse_xml(webData)
if wechat_msg.msg_type == 'text':
from_user = wechat_msg.from_user_id
to_user = wechat_msg.to_user_id
message = wechat_msg.content.decode("utf-8")
message_id = wechat_msg.msg_id


logger.info("[wechatmp] {}:{} Receive post query {} {}: {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), fromUser, message_id, message))
logger.info("[wechatmp] {}:{} Receive post query {} {}: {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), from_user, message_id, message))


global cache_dict
global query1
global query2
global query3
cache_key = fromUser
cache = cache_dict.get(cache_key)
cache_key = from_user
cache = channel_instance.cache_dict.get(cache_key)


reply_text = "" reply_text = ""
# New request # New request
if cache == None: if cache == None:
# The first query begin, reset the cache # The first query begin, reset the cache
cache_dict[cache_key] = (0, "")
# thread_pool.submit(self._do_build_reply, cache_key, fromUser, message)

context = Context()
context.kwargs = {'isgroup': False, 'receiver': fromUser, 'session_id': fromUser}

user_data = conf().get_user_data(fromUser)
context['openai_api_key'] = user_data.get('openai_api_key') # None or user openai_api_key

img_match_prefix = check_prefix(message, conf().get('image_create_prefix'))
if img_match_prefix:
message = message.replace(img_match_prefix, '', 1).strip()
context.type = ContextType.IMAGE_CREATE
context = channel_instance._compose_context(ContextType.TEXT, message, isgroup=False, msg=wechat_msg)
logger.debug("[wechatmp] context: {} {}".format(context, wechat_msg))
if 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_instance.cache_dict[cache_key] = (0, "")
channel_instance.produce(context)
else: else:
context.type = ContextType.TEXT
context.content = message
thread_pool.submit(self.handle, context)

query1[cache_key] = False
query2[cache_key] = False
query3[cache_key] = False
trigger_prefix = conf().get('single_chat_prefix',[''])[0]
if trigger_prefix:
content = textwrap.dedent(f"""\
请输入'{trigger_prefix}'接你想说的话跟我说话。
例如:
{trigger_prefix}你好,很高兴见到你。""")
else:
logger.error(f"[wechatmp] unknown error")
content = textwrap.dedent("""\
未知错误,请稍后再试""")
replyMsg = reply.TextMsg(wechat_msg.from_user_id, wechat_msg.to_user_id, content)
return replyMsg.send()
channel_instance.query1[cache_key] = False
channel_instance.query2[cache_key] = False
channel_instance.query3[cache_key] = False
# Request again # Request again
elif cache[0] == 0 and query1.get(cache_key) == True and query2.get(cache_key) == True and query3.get(cache_key) == True:
query1[cache_key] = False #To improve waiting experience, this can be set to True.
query2[cache_key] = False #To improve waiting experience, this can be set to True.
query3[cache_key] = False
elif cache[0] == 0 and channel_instance.query1.get(cache_key) == True and channel_instance.query2.get(cache_key) == True and channel_instance.query3.get(cache_key) == True:
channel_instance.query1[cache_key] = False #To improve waiting experience, this can be set to True.
channel_instance.query2[cache_key] = False #To improve waiting experience, this can be set to True.
channel_instance.query3[cache_key] = False
elif cache[0] >= 1: elif cache[0] >= 1:
# Skip the waiting phase # Skip the waiting phase
query1[cache_key] = True
query2[cache_key] = True
query3[cache_key] = True
channel_instance.query1[cache_key] = True
channel_instance.query2[cache_key] = True
channel_instance.query3[cache_key] = True




cache = cache_dict.get(cache_key)
if query1.get(cache_key) == False:
cache = channel_instance.cache_dict.get(cache_key)
if channel_instance.query1.get(cache_key) == False:
# The first query from wechat official server # The first query from wechat official server
logger.debug("[wechatmp] query1 {}".format(cache_key)) logger.debug("[wechatmp] query1 {}".format(cache_key))
query1[cache_key] = True
channel_instance.query1[cache_key] = True
cnt = 0 cnt = 0
while cache[0] == 0 and cnt < 45: while cache[0] == 0 and cnt < 45:
cnt = cnt + 1 cnt = cnt + 1
time.sleep(0.1) time.sleep(0.1)
cache = cache_dict.get(cache_key)
cache = channel_instance.cache_dict.get(cache_key)
if cnt == 45: if cnt == 45:
# waiting for timeout (the POST query will be closed by wechat official server) # waiting for timeout (the POST query will be closed by wechat official server)
time.sleep(5) time.sleep(5)
@@ -224,15 +159,15 @@ class WechatMPChannel(Channel):
return return
else: else:
pass pass
elif query2.get(cache_key) == False:
elif channel_instance.query2.get(cache_key) == False:
# The second query from wechat official server # The second query from wechat official server
logger.debug("[wechatmp] query2 {}".format(cache_key)) logger.debug("[wechatmp] query2 {}".format(cache_key))
query2[cache_key] = True
channel_instance.query2[cache_key] = True
cnt = 0 cnt = 0
while cache[0] == 0 and cnt < 45: while cache[0] == 0 and cnt < 45:
cnt = cnt + 1 cnt = cnt + 1
time.sleep(0.1) time.sleep(0.1)
cache = cache_dict.get(cache_key)
cache = channel_instance.cache_dict.get(cache_key)
if cnt == 45: if cnt == 45:
# waiting for timeout (the POST query will be closed by wechat official server) # waiting for timeout (the POST query will be closed by wechat official server)
time.sleep(5) time.sleep(5)
@@ -240,43 +175,44 @@ class WechatMPChannel(Channel):
return return
else: else:
pass pass
elif query3.get(cache_key) == False:
elif channel_instance.query3.get(cache_key) == False:
# The third query from wechat official server # The third query from wechat official server
logger.debug("[wechatmp] query3 {}".format(cache_key)) logger.debug("[wechatmp] query3 {}".format(cache_key))
query3[cache_key] = True
channel_instance.query3[cache_key] = True
cnt = 0 cnt = 0
while cache[0] == 0 and cnt < 45: while cache[0] == 0 and cnt < 45:
cnt = cnt + 1 cnt = cnt + 1
time.sleep(0.1) time.sleep(0.1)
cache = cache_dict.get(cache_key)
cache = channel_instance.cache_dict.get(cache_key)
if cnt == 45: if cnt == 45:
# Have waiting for 3x5 seconds # Have waiting for 3x5 seconds
# return timeout message # return timeout message
reply_text = "【正在响应中,回复任意文字尝试获取回复】" reply_text = "【正在响应中,回复任意文字尝试获取回复】"
logger.info("[wechatmp] Three queries has finished For {}: {}".format(fromUser, message_id))
replyPost = reply.TextMsg(fromUser, toUser, reply_text).send()
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 return replyPost
else: else:
pass pass


if float(time.time()) - float(queryTime) > 4.8:
logger.info("[wechatmp] Timeout for {} {}".format(fromUser, message_id))
if float(time.time()) - float(query_time) > 4.8:
logger.info("[wechatmp] Timeout for {} {}".format(from_user, message_id))
return return




if cache[0] > 1: if cache[0] > 1:
reply_text = cache[1][:600] + "\n【未完待续,回复任意文字以继续】" #wechatmp auto_reply length limit reply_text = cache[1][:600] + "\n【未完待续,回复任意文字以继续】" #wechatmp auto_reply length limit
cache_dict[cache_key] = (cache[0] - 1, cache[1][600:])
channel_instance.cache_dict[cache_key] = (cache[0] - 1, cache[1][600:])
elif cache[0] == 1: elif cache[0] == 1:
reply_text = cache[1] reply_text = cache[1]
cache_dict.pop(cache_key)
channel_instance.cache_dict.pop(cache_key)
logger.info("[wechatmp] {}:{} Do send {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), reply_text)) logger.info("[wechatmp] {}:{} Do send {}".format(web.ctx.env.get('REMOTE_ADDR'), web.ctx.env.get('REMOTE_PORT'), reply_text))
replyPost = reply.TextMsg(fromUser, toUser, reply_text).send()
replyPost = reply.TextMsg(from_user, to_user, reply_text).send()
return replyPost return replyPost


elif isinstance(recMsg, receive.Event) and recMsg.MsgType == 'event':
logger.info("[wechatmp] Event {} from {}".format(recMsg.Event, recMsg.FromUserName))
content = textwrap.dedent("""\
elif wechat_msg.msg_type == 'event':
logger.info("[wechatmp] Event {} from {}".format(wechat_msg.Event, wechat_msg.from_user_id))
trigger_prefix = conf().get('single_chat_prefix',[''])[0]
content = textwrap.dedent(f"""\
感谢您的关注! 感谢您的关注!
这里是ChatGPT,可以自由对话。 这里是ChatGPT,可以自由对话。
资源有限,回复较慢,请勿着急。 资源有限,回复较慢,请勿着急。
@@ -284,8 +220,8 @@ class WechatMPChannel(Channel):
暂时不支持图片输入。 暂时不支持图片输入。
支持图片输出,画字开头的问题将回复图片链接。 支持图片输出,画字开头的问题将回复图片链接。
支持角色扮演和文字冒险两种定制模式对话。 支持角色扮演和文字冒险两种定制模式对话。
输入'#帮助' 查看详细指令。""")
replyMsg = reply.TextMsg(recMsg.FromUserName, recMsg.ToUserName, content)
输入'{trigger_prefix}#帮助' 查看详细指令。""")
replyMsg = reply.TextMsg(wechat_msg.from_user_id, wechat_msg.to_user_id, content)
return replyMsg.send() return replyMsg.send()
else: else:
logger.info("暂且不处理") logger.info("暂且不处理")
@@ -294,9 +230,3 @@ class WechatMPChannel(Channel):
logger.exception(exc) logger.exception(exc)
return exc return exc



def check_prefix(content, prefix_list):
for prefix in prefix_list:
if content.startswith(prefix):
return prefix
return None

+ 2
- 1
config.py View File

@@ -45,7 +45,8 @@ available_setting = {
"top_p": 1, "top_p": 1,
"frequency_penalty": 0, "frequency_penalty": 0,
"presence_penalty": 0, "presence_penalty": 0,
"request_timeout": 30, # chatgpt请求超时时间
"request_timeout": 60, # chatgpt请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
"timeout": 120, # chatgpt重试超时时间,在这个时间内,将会自动重试


# 语音设置 # 语音设置
"speech_recognition": False, # 是否开启语音识别 "speech_recognition": False, # 是否开启语音识别


+ 4
- 4
plugins/role/roles.json View File

@@ -170,10 +170,10 @@
}, },
{ {
"title": "群聊取名", "title": "群聊取名",
"description": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。",
"descn": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。",
"wrapper": "信息和背景是:\n\"%s\"",
"remark": "根据给出的信息和背景为群聊取名。"
"description": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。",
"descn": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。",
"wrapper": "信息和背景是:\n\"%s\"",
"remark": "根据给出的信息和背景为群聊取名。"
}, },
{ {
"title": "表情符号翻译器", "title": "表情符号翻译器",


Loading…
Cancel
Save