From cd1390d4499e5ec2fe7ab1159cb0059fc984c66a Mon Sep 17 00:00:00 2001 From: Soulter <37870767+Soulter@users.noreply.github.com> Date: Sun, 5 Mar 2023 10:59:31 +0800 Subject: [PATCH 1/3] =?UTF-8?q?perf:=20=E9=80=86=E5=90=91=E5=BA=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81access/session=E7=99=BB=E5=BD=95=20fix:=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=B9=E9=80=86=E5=90=91=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/revChatGPT/revchatgpt.py | 4 ++-- configs/config.yaml | 14 ++++++++++++-- cores/qqbot/core.py | 17 ++++++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/addons/revChatGPT/revchatgpt.py b/addons/revChatGPT/revchatgpt.py index 7257642e9..31fbf54a9 100644 --- a/addons/revChatGPT/revchatgpt.py +++ b/addons/revChatGPT/revchatgpt.py @@ -3,8 +3,8 @@ from revChatGPT.V1 import Chatbot class revChatGPT: def __init__(self, config): - print("[RevChatGPT] 逆向库初始化:"+str(config)) - config['password'] = str(config['password']) + if 'password' in config: + config['password'] = str(config['password']) self.chatbot = Chatbot(config=config) def chat(self, prompt): diff --git a/configs/config.yaml b/configs/config.yaml index 7fcd33984..a6ae93cf8 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -56,20 +56,30 @@ direct_message_mode: true # enable设置为true后,将会停止使用上面正常的官方API调用而使用本逆向项目 # # 多账户可以保证每个请求都能得到及时的回复。 -# 关于account的格式 + + +# 关于account的格式,请你务必认真阅读以下格式。 + + +# account支持email+password、session_token、access_token多种方式登录。 +# 如果要使用session_token、access_token登录,直接添加新的一行(注意缩进!) - access_token: xxxxxxx即可 # account: +# - session_token: xxxxxxxx +# - access_token: xxxxxxxx # - email: 第1个账户 # password: 第1个账户密码 # - email: 第2个账户 # password: 第2个账户密码 # - .... # 请严格按照上面这个格式填写。 -# 这里我免费提供2个账号给大家,不过用的人一定会很多的,所以会造成一些bug,因此还是用自己的账号好一点。 +# 这里免费提供2个账号给大家,不过用的人一定会很多的,所以会造成一些bug,因此还是用自己的账号好一点。 # 需要账号可以联系我。QQ905617992 rev_ChatGPT: enable: false account: + - access_token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UaEVOVUpHTkVNMVFURTRNMEZCTWpkQ05UZzVNRFUxUlRVd1FVSkRNRU13UmtGRVFrRXpSZyJ9.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL3Byb2ZpbGUiOnsiZW1haWwiOiJkdWVsdG1wK3JzdG56QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJnZW9pcF9jb3VudHJ5IjoiU0cifSwiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS9hdXRoIjp7InVzZXJfaWQiOiJ1c2VyLURNUmhqWXc1N2JGWURiTXdPUWwyRWtTQiJ9LCJpc3MiOiJodHRwczovL2F1dGgwLm9wZW5haS5jb20vIiwic3ViIjoiYXV0aDB8NjNkODdiYjk4MWY4NDViYmU5NWFhMmU0IiwiYXVkIjpbImh0dHBzOi8vYXBpLm9wZW5haS5jb20vdjEiLCJodHRwczovL29wZW5haS5vcGVuYWkuYXV0aDBhcHAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTY3NzgyNzk1NCwiZXhwIjoxNjc5MDM3NTU0LCJhenAiOiJUZEpJY2JlMTZXb1RIdE45NW55eXdoNUU0eU9vNkl0RyIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgbW9kZWwucmVhZCBtb2RlbC5yZXF1ZXN0IG9yZ2FuaXphdGlvbi5yZWFkIG9mZmxpbmVfYWNjZXNzIn0.gpj7nLFYGmkKj3y0qcR2Ggrgi1fSbPdMA06uUeHNjG8Gw9cTxKxdyvl9LB80Xbua0wlsMoitXfthYck4f4Ok9s970ilPLvi77ZQKuqwoW1DvbQO_4NLpZhoerJYJDycjOD1O-FRkAA2Qi0tD7MJ-jwQ5hDwxdywm6o8BTqu6NU8fsGjmrYjURI8IeNYlOG61xuY9z0g_BELRkNcucD5tMeSqKbkdjEc2M_XUQwSPLqoWgbAlbvw3B7swzbDbIMo-Etyx7pCnDTRzf0iRkn_KwcTCOy3F_KYakDvr2N4wOwQxY0fOc8f8nXDpSaL_0eUbUQgxfZFqUZJ5QVuwPcbrsA + # - session_token: xxxxxxxx - email: d.o.m.her.ry61.7@gmail.com password: 11111111 - email: ca.it.li.nal.o.i.si.o91@gmail.com diff --git a/cores/qqbot/core.py b/cores/qqbot/core.py index 239cc1aa3..6f7bc5e25 100644 --- a/cores/qqbot/core.py +++ b/cores/qqbot/core.py @@ -187,17 +187,18 @@ def initBot(cfg, prov): if 'account' in cfg['rev_ChatGPT']: from addons.revChatGPT.revchatgpt import revChatGPT for i in range(0, len(cfg['rev_ChatGPT']['account'])): - print(f"[System] 正在创建rev_ChatGPT负载{str(i)}: " + cfg['rev_ChatGPT']['account'][i]['email']) try: + print(f"[System] 创建rev_ChatGPT负载{str(i)}: " + str(cfg['rev_ChatGPT']['account'][i])) revstat = { 'obj': revChatGPT(cfg['rev_ChatGPT']['account'][i]), 'busy': False } rev_chatgpt.append(revstat) + except: print("[System] 创建rev_ChatGPT负载失败") else: - input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT的email和password") + input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置") elif prov == OPENAI_OFFICIAL: from cores.openai.core import ChatGPT chatgpt = ChatGPT(cfg['openai']) @@ -345,9 +346,11 @@ def get_rev_ChatGPT_response(prompts_str): def send_qq_msg(message, res, image_mode=False): if not image_mode: try: - asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop) - except: - raise + res = asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop) + res.result() + except BaseException as e: + print("[System-Error] 回复QQ消息失败") + raise e else: asyncio.run_coroutine_threadsafe(message.reply(image=res, content=""), client.loop) @@ -574,7 +577,7 @@ def oper_msg(message, at=False, loop=None): gap_chatgpt_res = gap_chatgpt_res.replace(i, "***") # 发送信息 send_qq_msg(message, ''+gap_chatgpt_res) - except: + except BaseException as e: print("QQ频道API错误: \n"+str(e)) f_res = "" for t in chatgpt_res: @@ -582,7 +585,7 @@ def oper_msg(message, at=False, loop=None): try: send_qq_msg(message, ''+f_res) # send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}") - except: + except BaseException as e: # 如果还是不行则过滤url f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', f_res, flags=re.MULTILINE) f_res = f_res.replace(".", "·") From 651ba7b3d627a72820038b7815897c1a5ee1d4d0 Mon Sep 17 00:00:00 2001 From: Soulter <37870767+Soulter@users.noreply.github.com> Date: Sun, 5 Mar 2023 11:02:32 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/config.yaml b/configs/config.yaml index a6ae93cf8..f577db6c9 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -78,7 +78,7 @@ direct_message_mode: true rev_ChatGPT: enable: false account: - - access_token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UaEVOVUpHTkVNMVFURTRNMEZCTWpkQ05UZzVNRFUxUlRVd1FVSkRNRU13UmtGRVFrRXpSZyJ9.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL3Byb2ZpbGUiOnsiZW1haWwiOiJkdWVsdG1wK3JzdG56QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJnZW9pcF9jb3VudHJ5IjoiU0cifSwiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS9hdXRoIjp7InVzZXJfaWQiOiJ1c2VyLURNUmhqWXc1N2JGWURiTXdPUWwyRWtTQiJ9LCJpc3MiOiJodHRwczovL2F1dGgwLm9wZW5haS5jb20vIiwic3ViIjoiYXV0aDB8NjNkODdiYjk4MWY4NDViYmU5NWFhMmU0IiwiYXVkIjpbImh0dHBzOi8vYXBpLm9wZW5haS5jb20vdjEiLCJodHRwczovL29wZW5haS5vcGVuYWkuYXV0aDBhcHAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTY3NzgyNzk1NCwiZXhwIjoxNjc5MDM3NTU0LCJhenAiOiJUZEpJY2JlMTZXb1RIdE45NW55eXdoNUU0eU9vNkl0RyIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgbW9kZWwucmVhZCBtb2RlbC5yZXF1ZXN0IG9yZ2FuaXphdGlvbi5yZWFkIG9mZmxpbmVfYWNjZXNzIn0.gpj7nLFYGmkKj3y0qcR2Ggrgi1fSbPdMA06uUeHNjG8Gw9cTxKxdyvl9LB80Xbua0wlsMoitXfthYck4f4Ok9s970ilPLvi77ZQKuqwoW1DvbQO_4NLpZhoerJYJDycjOD1O-FRkAA2Qi0tD7MJ-jwQ5hDwxdywm6o8BTqu6NU8fsGjmrYjURI8IeNYlOG61xuY9z0g_BELRkNcucD5tMeSqKbkdjEc2M_XUQwSPLqoWgbAlbvw3B7swzbDbIMo-Etyx7pCnDTRzf0iRkn_KwcTCOy3F_KYakDvr2N4wOwQxY0fOc8f8nXDpSaL_0eUbUQgxfZFqUZJ5QVuwPcbrsA + - access_token: # - session_token: xxxxxxxx - email: d.o.m.her.ry61.7@gmail.com password: 11111111 From 54cdca01d3100f2a142cb551c98509f67ac870cb Mon Sep 17 00:00:00 2001 From: Soulter <37870767+Soulter@users.noreply.github.com> Date: Sun, 5 Mar 2023 12:26:46 +0800 Subject: [PATCH 3/3] =?UTF-8?q?perf:=20=E9=87=8D=E6=9E=84ChatGPT=20API?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E7=A8=B3=E5=AE=9A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cores/openai/core.py | 62 +++++++++--------------- cores/qqbot/core.py | 109 ++++++++++++++++++++++++++++++------------- 2 files changed, 99 insertions(+), 72 deletions(-) diff --git a/cores/openai/core.py b/cores/openai/core.py index 1dd0f997e..b3466e9af 100644 --- a/cores/openai/core.py +++ b/cores/openai/core.py @@ -29,55 +29,37 @@ class ChatGPT: self.chatGPT_configs = chatGPT_configs self.openai_configs = cfg - def chat(self, prompt, image_mode = False): + def chat(self, req, image_mode = False): # ChatGPT API 2023/3/2 - messages = [{"role": "user", "content": prompt}] + # messages = [{"role": "user", "content": prompt}] try: - if not image_mode: - - response = openai.ChatCompletion.create( - messages=messages, - **self.chatGPT_configs - ) - else: - response = openai.Image.create( - prompt=prompt, - n=1, - size="512x512", - ) + response = openai.ChatCompletion.create( + messages=req, + **self.chatGPT_configs + ) except Exception as e: print(e) - if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(e) or 'No API key provided.' in str(e): - print("[System] 当前Key已超额,正在切换") + if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e): + print("[System] 当前Key已超额或者不正常,正在切换") self.key_stat[openai.api_key]['exceed'] = True self.save_key_record() - response, is_switched = self.handle_switch_key(prompt) + response, is_switched = self.handle_switch_key(req) if not is_switched: - # 所有Key都超额 + # 所有Key都超额或不正常 raise e else: - if not image_mode: - response = openai.ChatCompletion.create( - messages=messages, - **self.chatGPT_configs - ) - else: - response = openai.Image.create( - prompt=prompt, - n=1, - size="512x512", - ) - if not image_mode: - self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens'] - self.save_key_record() - print("[ChatGPT] "+str(response["choices"][0]["message"]["content"])) - return str(response["choices"][0]["message"]["content"]).strip(), response['usage']['total_tokens'] - else: - return response['data'][0]['url'] + response = openai.ChatCompletion.create( + messages=req, + **self.chatGPT_configs + ) + self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens'] + self.save_key_record() + print("[ChatGPT] "+str(response["choices"][0]["message"]["content"])) + return str(response["choices"][0]["message"]["content"]).strip(), response['usage']['total_tokens'] - def handle_switch_key(self, prompt): - messages = [{"role": "user", "content": prompt}] + def handle_switch_key(self, req): + # messages = [{"role": "user", "content": prompt}] while True: is_all_exceed = True for key in self.key_stat: @@ -85,10 +67,10 @@ class ChatGPT: is_all_exceed = False openai.api_key = key print(f"[System] 切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}") - if prompt != '': + if len(req) > 0: try: response = openai.ChatCompletion.create( - messages=messages, + messages=req, **self.chatGPT_configs ) return response, True diff --git a/cores/qqbot/core.py b/cores/qqbot/core.py index 239cc1aa3..0200a9ea4 100644 --- a/cores/qqbot/core.py +++ b/cores/qqbot/core.py @@ -307,16 +307,23 @@ def run_bot(appid, token): ''' 得到OpenAI官方API的回复 ''' -def get_chatGPT_response(prompts_str, image_mode=False): +def get_chatGPT_response(context, request, image_mode=False): res = '' usage = '' + + req_list = [] + for i in context: + req_list.append(i['user']) + req_list.append(i['AI']) + req_list.append(request['user']) + if not image_mode: - res, usage = chatgpt.chat(prompts_str) + res, usage = chatgpt.chat(req_list) # 处理结果文本 chatgpt_res = res.strip() return res, usage else: - res = chatgpt.chat(prompts_str, image_mode = True) + res = chatgpt.chat(req_list, image_mode = True) return res ''' @@ -345,8 +352,9 @@ def get_rev_ChatGPT_response(prompts_str): def send_qq_msg(message, res, image_mode=False): if not image_mode: try: - asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop) - except: + future = asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop) + future.result() + except BaseException as e: raise else: asyncio.run_coroutine_threadsafe(message.reply(image=res, content=""), client.loop) @@ -366,10 +374,13 @@ def get_prompts_by_cache_list(cache_data_list, divide=False, paging=False, size= page_end = len(cache_data_list) cache_data_list = cache_data_list[page_begin:page_end] for item in cache_data_list: - prompts += str(item['prompt']) + prompts += str(item['user']['role']) + ":\n" + str(item['user']['content']) + "\n" + prompts += str(item['AI']['role']) + ":\n" + str(item['AI']['content']) + "\n" + if divide: prompts += "----------\n" return prompts + def get_user_usage_tokens(cache_list): usage_tokens = 0 @@ -494,26 +505,44 @@ def oper_msg(message, at=False, loop=None): if provider == OPENAI_OFFICIAL: # 获取缓存 - cache_prompt = '' + # cache_prompt = '' cache_data_list = session_dict[session_id] - cache_prompt = get_prompts_by_cache_list(cache_data_list) - cache_prompt += "\nHuman: "+ qq_msg + "\nAI: " + # cache_prompt = get_prompts_by_cache_list(cache_data_list) + # cache_prompt += "\nHuman: "+ qq_msg + "\nAI: " + + # 创建一个新的Record + + record_obj = { + "user": { + "role": "user", + "content": qq_msg, + }, + "AI": {}, + 'usage_tokens': 0, + 'level': 'normal', + } + + if command_type == 1: + record_obj['user'] = 'system' + + print("[Debug] "+ str(cache_data_list)) # 请求chatGPT获得结果 try: - chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt) + chatgpt_res, current_usage_tokens = get_chatGPT_response(context=cache_data_list, request=record_obj) except (BaseException) as e: print("[System-Err] OpenAI API错误。原因如下:\n"+str(e)) if 'maximum context length' in str(e): print("token超限, 清空对应缓存") session_dict[session_id] = [] cache_data_list = [] - cache_prompt = "Human: "+ qq_msg + "\nAI: " - chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt) + chatgpt_res, current_usage_tokens = get_chatGPT_response(context=cache_data_list, request=record_obj) elif 'exceeded' in str(e): send_qq_msg(message, f"OpenAI API错误。原因:\n{str(e)} \n超额了。可自己搭建一个机器人(Github仓库:QQChannelChatGPT)") else: - send_qq_msg(message, f"OpenAI API错误。原因如下:\n{str(e)} \n前往官方频道反馈~") + f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE) + f_res = f_res.replace(".", "·") + send_qq_msg(message, f"OpenAI API错误。原因如下:\n{f_res} \n前往官方频道反馈~") return # 超过指定tokens, 尽可能的保留最多的条目,直到小于max_tokens @@ -530,28 +559,44 @@ def oper_msg(message, at=False, loop=None): index += 1 # 删除完后更新相关字段 session_dict[session_id] = cache_data_list - cache_prompt = get_prompts_by_cache_list(cache_data_list) + # cache_prompt = get_prompts_by_cache_list(cache_data_list) - # 添加新条目进入缓存的prompt + # 人格置顶 if command_type == 1: level = 'max' else: level = 'normal' - if len(cache_data_list) > 0: - single_record = { - "prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n', - "usage_tokens": current_usage_tokens, - "single_tokens": current_usage_tokens - int(cache_data_list[-1]['usage_tokens']), - "level": level - } + + # 添加新条目进入缓存的prompt + record_obj['AI'] = { + 'role': 'assistant', + 'content': chatgpt_res, + } + record_obj['usage_tokens'] = current_usage_tokens + if len(cache_data_list) > 0: + record_obj['single_tokens'] = current_usage_tokens - int(cache_data_list[-1]['usage_tokens']) else: - single_record = { - "prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n', - "usage_tokens": current_usage_tokens, - "single_tokens": current_usage_tokens, - "level": level - } - cache_data_list.append(single_record) + record_obj['single_tokens'] = current_usage_tokens + record_obj['level'] = level + + cache_data_list.append(record_obj) + # if len(cache_data_list) > 0: + # single_record = { + # 'role': 'assistant', + # "content": chatgpt_res, + # "usage_tokens": current_usage_tokens, + # "single_tokens": current_usage_tokens - int(cache_data_list[-1]['usage_tokens']), + # "level": level + # } + # else: + # single_record = { + # 'role': 'assistant', + # "prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n', + # "usage_tokens": current_usage_tokens, + # "single_tokens": current_usage_tokens, + # "level": level + # } + # cache_data_list.append(single_record) session_dict[session_id] = cache_data_list elif provider == REV_CHATGPT: @@ -574,7 +619,7 @@ def oper_msg(message, at=False, loop=None): gap_chatgpt_res = gap_chatgpt_res.replace(i, "***") # 发送信息 send_qq_msg(message, ''+gap_chatgpt_res) - except: + except BaseException as e: print("QQ频道API错误: \n"+str(e)) f_res = "" for t in chatgpt_res: @@ -582,9 +627,9 @@ def oper_msg(message, at=False, loop=None): try: send_qq_msg(message, ''+f_res) # send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}") - except: + except BaseException as e: # 如果还是不行则过滤url - f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', f_res, flags=re.MULTILINE) + f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE) f_res = f_res.replace(".", "·") send_qq_msg(message, ''+f_res) # send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")