diff --git a/bot/chatgpt/chat_gpt_bot.py b/bot/chatgpt/chat_gpt_bot.py index a1b41ec..a0b4bbc 100644 --- a/bot/chatgpt/chat_gpt_bot.py +++ b/bot/chatgpt/chat_gpt_bot.py @@ -127,7 +127,7 @@ class ChatGPTBot(Bot): def create_img(self, query, retry_count=0): try: if conf().get('rate_limit_dalle') and not self.tb4dalle.get_token(): - return "请求太快了,请休息一下再问我吧" + return False, "请求太快了,请休息一下再问我吧" logger.info("[OPEN_AI] image_query={}".format(query)) response = openai.Image.create( prompt=query, #图片描述 diff --git a/bridge/bridge.py b/bridge/bridge.py index df9393e..a439e1b 100644 --- a/bridge/bridge.py +++ b/bridge/bridge.py @@ -12,13 +12,15 @@ from common import const class Bridge(object): def __init__(self): self.btype={ - "chat": "chatGPT", + "chat": const.CHATGPT, "voice_to_text": "openai", "text_to_voice": "baidu" } + model_type = conf().get("model") + if model_type in ["text-davinci-003"]: + self.btype['chat'] = const.OPEN_AI self.bots={} - def get_bot(self,typename): if self.bots.get(typename) is None: logger.info("create bot {} for {}".format(self.btype[typename],typename)) @@ -35,13 +37,7 @@ class Bridge(object): def fetch_reply_content(self, query, context : Context) -> Reply: - bot_type = const.CHATGPT - model_type = conf().get("model") - if model_type in ["gpt-3.5-turbo", "gpt-4", "gpt-4-32k"]: - bot_type = const.CHATGPT - elif model_type in ["text-davinci-003"]: - bot_type = const.OPEN_AI - return bot_factory.create_bot(bot_type).reply(query, context) + return self.get_bot("chat").reply(query, context) def fetch_voice_to_text(self, voiceFile) -> Reply: diff --git a/channel/terminal/terminal_channel.py b/channel/terminal/terminal_channel.py index 1c9a61d..7e8ad35 100644 --- a/channel/terminal/terminal_channel.py +++ b/channel/terminal/terminal_channel.py @@ -1,9 +1,10 @@ +from bridge.context import * from channel.channel import Channel import sys class TerminalChannel(Channel): def startup(self): - context = {"from_user_id": "User"} + context = Context() print("\nPlease input your question") while True: try: @@ -12,12 +13,13 @@ class TerminalChannel(Channel): print("\nExiting...") sys.exit() + context.type = ContextType.TEXT + context['session_id'] = "User" + context.content = prompt print("Bot:") sys.stdout.flush() - for res in super().build_reply_content(prompt, context): - print(res, end="") - sys.stdout.flush() - print("\n") + res = super().build_reply_content(prompt, context).content + print(res) def get_input(self, prompt): diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py index 5c5826c..c3c2ed1 100644 --- a/channel/wechat/wechat_channel.py +++ b/channel/wechat/wechat_channel.py @@ -56,14 +56,15 @@ class WechatChannel(Channel): # start message listener itchat.run() - # handle_* 系列函数处理收到的消息后构造context,然后调用handle函数处理context - # context是一个字典,包含了消息的所有信息,包括以下key + # handle_* 系列函数处理收到的消息后构造Context,然后传入handle函数中处理Context和发送回复 + # Context包含了消息的所有信息,包括以下属性 # type 消息类型, 包括TEXT、VOICE、IMAGE_CREATE # content 消息内容,如果是TEXT类型,content就是文本内容,如果是VOICE类型,content就是语音文件名,如果是IMAGE_CREATE类型,content就是图片生成命令 - # session_id: 会话id - # isgroup: 是否是群聊 - # msg: 原始消息对象 - # receiver: 需要回复的对象 + # kwargs 附加参数字典,包含以下的key: + # session_id: 会话id + # isgroup: 是否是群聊 + # receiver: 需要回复的对象 + # msg: itchat的原始消息对象 def handle_voice(self, msg): if conf().get('speech_recognition') != True: diff --git a/channel/wechat/wechaty_channel.py b/channel/wechat/wechaty_channel.py index 0556864..d9f6c2d 100644 --- a/channel/wechat/wechaty_channel.py +++ b/channel/wechat/wechaty_channel.py @@ -194,12 +194,12 @@ class WechatyChannel(Channel): try: if not query: return - context = dict() + context = Context(ContextType.TEXT, query) context['session_id'] = reply_user_id - reply_text = super().build_reply_content(query, context) + reply_text = super().build_reply_content(query, context).content if reply_text: # 转换 mp3 文件为 silk 格式 - mp3_file = super().build_text_to_voice(reply_text) + mp3_file = super().build_text_to_voice(reply_text).content silk_file = mp3_file.replace(".mp3", ".silk") # Load the MP3 file audio = AudioSegment.from_file(mp3_file, format="mp3") diff --git a/common/expired_dict.py b/common/expired_dict.py index ccf16c7..f16af52 100644 --- a/common/expired_dict.py +++ b/common/expired_dict.py @@ -16,8 +16,26 @@ class ExpiredDict(dict): def __setitem__(self, key, value): expiry_time = datetime.now() + timedelta(seconds=self.expires_in_seconds) super().__setitem__(key, (value, expiry_time)) + def get(self, key, default=None): try: return self[key] except KeyError: - return default \ No newline at end of file + return default + + def __contains__(self, key): + try: + self[key] + return True + except KeyError: + return False + + def keys(self): + keys=list(super().keys()) + return [key for key in keys if key in self] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __iter__(self): + return self.keys().__iter__() \ No newline at end of file diff --git a/plugins/README.md b/plugins/README.md index 61e83c7..62b10b6 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -31,7 +31,7 @@ context.kwargs = {'isgroup': False, 'msg': msg, 'receiver': other_user_id, 'session_id': other_user_id} ``` 2. 产生回复 - 本过程用于处理消息。目前默认处理逻辑是根据`Context`的类型交付给对应的bot: + 本过程用于处理消息。目前默认处理逻辑如下,它根据`Context`的类型交付给对应的bot。如果本过程未产生任何回复,则会跳过之后的处理阶段。 ```python if context.type == ContextType.TEXT or context.type == ContextType.IMAGE_CREATE: reply = super().build_reply_content(context.content, context) #文字跟画图交付给chatgpt @@ -133,9 +133,9 @@ class Hello(Plugin): - `EventAction.BREAK`: 事件结束,不再给下个插件处理,交付给默认的处理逻辑。 - `EventAction.BREAK_PASS`: 事件结束,不再给下个插件处理,跳过默认的处理逻辑。 -以`Hello`插件为例,它处理`Context`类型为`TEXT`的消息。 +以`Hello`插件为例,它处理`Context`类型为`TEXT`的消息: - 如果内容是`Hello`,直接将回复设置为`Hello+用户昵称`,并跳过之后的插件和默认逻辑。 -- 如果内容是`End`,它会将`Context`的类型更改为`IMAGE_CREATE`,并让事件继续,如果最终交付到默认逻辑,会调用默认的画图Bot。 +- 如果内容是`End`,它会将`Context`的类型更改为`IMAGE_CREATE`,并让事件继续,如果最终交付到默认逻辑,会调用默认的画图Bot来画画。 ```python def on_handle_context(self, e_context: EventContext): if e_context['context'].type != ContextType.TEXT: diff --git a/plugins/dungeon/dungeon.py b/plugins/dungeon/dungeon.py index 9df7230..36f9386 100644 --- a/plugins/dungeon/dungeon.py +++ b/plugins/dungeon/dungeon.py @@ -8,6 +8,7 @@ from config import conf import plugins from plugins import * from common.log import logger +from common import const # https://github.com/bupticybee/ChineseAiDungeonChatGPT class StoryTeller(): @@ -51,7 +52,7 @@ class Dungeon(Plugin): if e_context['context'].type != ContextType.TEXT: return bottype = Bridge().get_bot_type("chat") - if bottype != "chatGPT": + if bottype != const.CHATGPT: return bot = Bridge().get_bot("chat") content = e_context['context'].content[:] diff --git a/plugins/godcmd/godcmd.py b/plugins/godcmd/godcmd.py index c33c1e2..3a8f770 100644 --- a/plugins/godcmd/godcmd.py +++ b/plugins/godcmd/godcmd.py @@ -10,6 +10,7 @@ from bridge.reply import Reply, ReplyType from config import load_config import plugins from plugins import * +from common import const from common.log import logger # 定义指令集 @@ -163,7 +164,7 @@ class Godcmd(Plugin): elif cmd == "id": ok, result = True, f"用户id=\n{user}" elif cmd == "reset": - if bottype == "chatGPT": + if bottype == const.CHATGPT: bot.sessions.clear_session(session_id) ok, result = True, "会话已重置" else: @@ -185,7 +186,7 @@ class Godcmd(Plugin): load_config() ok, result = True, "配置已重载" elif cmd == "resetall": - if bottype == "chatGPT": + if bottype == const.CHATGPT: bot.sessions.clear_all_session() ok, result = True, "重置所有会话成功" else: diff --git a/plugins/role/role.py b/plugins/role/role.py index ad4b48e..e5ba5f3 100644 --- a/plugins/role/role.py +++ b/plugins/role/role.py @@ -5,6 +5,7 @@ import os from bridge.bridge import Bridge from bridge.context import ContextType from bridge.reply import Reply, ReplyType +from common import const import plugins from plugins import * from common.log import logger @@ -73,7 +74,7 @@ class Role(Plugin): if e_context['context'].type != ContextType.TEXT: return bottype = Bridge().get_bot_type("chat") - if bottype != "chatGPT": + if bottype != const.CHATGPT: return bot = Bridge().get_bot("chat") content = e_context['context'].content[:] @@ -119,7 +120,7 @@ class Role(Plugin): e_context.action = EventAction.CONTINUE def get_help_text(self): - help_text = "输入\"$角色 (角色名)\"或\"$role (角色名)\"为我设定角色吧,#reset 可以清除设定的角色。\n\n目前可用角色列表:\n" + help_text = "输入\"$角色 (角色名)\"或\"$role (角色名)\"为我设定角色吧,\"$停止扮演 \" 可以清除设定的角色。\n\n目前可用角色列表:\n" for role in self.roles: help_text += f"[{role}]: {self.roles[role]['remark']}\n" return help_text diff --git a/plugins/role/roles.json b/plugins/role/roles.json index ab9a12e..506974c 100644 --- a/plugins/role/roles.json +++ b/plugins/role/roles.json @@ -4,7 +4,7 @@ "title": "英语翻译或修改", "description": "I want you to act as an English translator, spelling corrector and improver. I will speak to you in any language and you will detect the language, translate it and answer in the corrected and improved version of my text, in English. I want you to replace my simplified A0-level words and sentences with more beautiful and elegant, upper level English words and sentences. Keep the meaning same, but make them more literary. I want you to only reply the correction, the improvements and nothing else, do not write explanations. Please treat every message I send later as text content", "descn": "我希望你能充当英语翻译、拼写纠正者和改进者。我将用任何语言与你交谈,你将检测语言,翻译它,并在我的文本的更正和改进版本中用英语回答。我希望你用更漂亮、更优雅、更高级的英语单词和句子来取代我的简化 A0 级单词和句子。保持意思不变,但让它们更有文学性。我希望你只回答更正,改进,而不是其他,不要写解释。请把我之后的每一条消息都当作文本内容。", - "wrapper": "内容是:\n\"%s\"", + "wrapper": "你要翻译或纠正的内容是:\n\"%s\"", "remark": "将其他语言翻译成英文,或改进你提供的英文句子。" }, { @@ -142,10 +142,10 @@ }, { "title": "阴阳怪气语录生成器", - "description": "我希望你充当一个讽刺性阴阳怪气语录生成器。当我给你一个主题时,你需要为我提供一个讽刺性的、带有阴阳怪气的短语或句子来反驳该主题。这些短语或句子应该是幽默、机智且具有讽刺意味的。不要提供相关主题的普通或无趣的表述。", - "descn": "我希望你充当一个讽刺性阴阳怪气语录生成器。当我给你一个主题时,你需要为我提供一个讽刺性的、带有阴阳怪气的短语或句子来反驳该主题。这些短语或句子应该是幽默、机智且具有讽刺意味的。不要提供相关主题的普通或无趣的表述。", + "description": "我希望你充当一个阴阳怪气讽刺语录生成器。当我给你一个主题时,你需要使用阴阳怪气的语气来评价该主题,评价的思路是挖苦和讽刺。如果有该主题的反例更好(比如失败经历,糟糕体验。注意不要直接说那些糟糕体验,而是通过反讽、幽默的类比等方式来说明)。", + "descn": "我希望你充当一个阴阳怪气讽刺语录生成器。当我给你一个主题时,你需要使用阴阳怪气的语气来评价该主题,评价的思路是挖苦和讽刺。如果有该主题的反例更好(比如失败经历,糟糕体验。注意不要直接说那些糟糕体验,而是通过反讽、幽默的类比等方式来说明)。", "wrapper": "主题是:\n\"%s\"", - "remark": "根据主题生成阴阳怪气语录。" + "remark": "根据主题生成阴阳怪气讽刺语录。" }, { "title": "舔狗语录生成器", @@ -153,6 +153,34 @@ "descn": "我希望你充当一个舔狗语录生成器,为我提供不同场景下的甜言蜜语。请根据提供的状态生成一句适当的舔狗语录,让女神感受到我的关心和温柔,给女神做牛做马。不需要提供背景解释,只需提供根据场景生成的舔狗语录。", "wrapper": "场景是:\n\"%s\"", "remark": "根据场景生成舔狗语录。" + }, + { + "title": "群聊取名", + "description": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。", + "descn": "我希望你充当微信群聊的命名专家。根据我提供的信息和背景,为这个群聊起几个有趣顺口且贴切的名字,每个不要超过8个字。请在回答中仅给出群聊名称,不要写任何额外的解释。", + "wrapper": "信息和背景是:\n\"%s\"", + "remark": "根据给出的信息和背景为群聊取名。" + }, + { + "title": "表情符号翻译器", + "description": "I want you to translate the sentences I wrote into emojis. I will write the sentence, and you will express it with emojis. I just want you to express it with emojis. I don't want you to reply with anything but emoji. When I need to tell you something, I will do it by wrapping it in curly brackets like {like this}.", + "descn": "我想让你把我写的句子翻译成表情符号。我写句子,你就用表情符号来表达。你只能用 emojis 来表达,除了表情符号不能使用任何文字。当我需要告诉你一些事情的时候,我会用大括号把它包起来,比如{像这样}。", + "wrapper": "需要翻译成表情符号的内容是:\n\"%s\"", + "remark": "将输入文字翻译为表情符号。" + }, + { + "title": "AI 医生", + "description": "I want you to act as an AI assisted doctor. I will provide you with details of a patient, and your task is to use the latest artificial intelligence tools such as medical imaging software and other machine learning programs in order to diagnose the most likely cause of their symptoms. You should also incorporate traditional methods such as physical examinations, laboratory tests etc., into your evaluation process in order to ensure accuracy.", + "descn": "我想让你充当一名人工智能辅助的医生。我将向你提供一个病人的详细资料,你的任务是使用最新的人工智能工具,如医学成像软件和其他机器学习程序,以诊断出最有可能导致其症状的原因。你还应将传统方法,如体检、实验室测试等,纳入你的评估过程,以确保准确性。", + "wrapper": "需要诊断的资料是:\n\"%s\"", + "remark": "辅助诊断" + }, + { + "title": "知识点阐述", + "description": "我会给予你词语,请你按照我给的词构建一个知识文字世界,你是此世界的导游,在世界里一切知识都是以象征的形式表达的,你在描述经历时应当适当加入五感的描述", + "descn": "我会给予你词语,请你按照我给的词构建一个知识文字世界,你是此世界的导游,在世界里一切知识都是以象征的形式表达的,你在描述经历时应当适当加入五感的描述", + "wrapper": "词语是:\n\"%s\"", + "remark": "用比喻的方式解释词语。" } ] } \ No newline at end of file