From d5adb323f8c1867ce8589e02cb084266f1a08d55 Mon Sep 17 00:00:00 2001 From: 1121917292 <1121917292@qq.com> Date: Sat, 23 Apr 2022 21:01:09 +0800 Subject: [PATCH] update 2.9.0 --- ChatCheck.py | 3 +- ChatClass.py | 184 ++++++++++++++++++++++++++++++--------------------- ChatReply.py | 115 +++++++++++++++++++++++++++++++- Chatmain.py | 90 ++++++++++++++++++++++--- 4 files changed, 307 insertions(+), 85 deletions(-) diff --git a/ChatCheck.py b/ChatCheck.py index 317d824..3896a0d 100644 --- a/ChatCheck.py +++ b/ChatCheck.py @@ -126,6 +126,7 @@ def main(data, fromchat): mergetimetip = '总词库合成间隔:{}秒'.format(config['mergetime']) intervaltip = '词库链间隔:{}秒'.format(config['interval']) blackfreqtip = '黑名单容错次数:{}次'.format(config['blackfreq']) + tempmessagenumtip = '单个群最大消息缓存数:{}条'.format(config['tempmessagenum']) typefreqtip = '回复阈值设定:\n' singlereplytip = '指定群回复触发概率:\n' singlevoicereplytip = '指定群回复概率(语音):\n' @@ -154,7 +155,7 @@ def main(data, fromchat): check_version[1]) else: versiontip = "未连接至ChatLearning服务器" - situation = learningtip + '\n' + replytip + '\n' + voicereplytip + '\n' + golbetip + '\n' + replychancetip + '\n' + voicereplychancetip + '\n' + synthesizertip + '\n' + mergetimetip + '\n' + intervaltip + '\n' + blackfreqtip + situation = learningtip + '\n' + replytip + '\n' + voicereplytip + '\n' + golbetip + '\n' + replychancetip + '\n' + voicereplychancetip + '\n' + synthesizertip + '\n' + mergetimetip + '\n' + intervaltip + '\n' + blackfreqtip+'\n'+tempmessagenumtip situationchain = [{'type': 'Plain', 'text': situation}] typefreq_message = [{'type': 'Plain', 'text': typefreqtip}] siglereply_message = [{'type': 'Plain', 'text': singlereplytip}] diff --git a/ChatClass.py b/ChatClass.py index e531178..1b15e4e 100644 --- a/ChatClass.py +++ b/ChatClass.py @@ -1,15 +1,17 @@ import os import pickle +import platform import re import shutil import threading import time import traceback -import platform + +import requests import simuse -version = '2.8.1' +version = '2.9.0' # 控制台指令类 @@ -39,11 +41,13 @@ class commandclass(): commandtips['remove subadmin <群号>'] = '#移除可自行管理的群' commandtips['remove unmerge <群号>'] = '#移除不录入总词库的群' commandtips['remove tag <群号>'] = '#移除群标签' + commandtips['fastdelete'] = '#更改快速清除的权限' commandtips['check'] = '#查看设置情况' commandtips['grouplist'] = '#查看开启记录/回复的群列表' commandtips['globe'] = '#开启/关闭全局模式' commandtips['setadmin '] = '#设置管理员QQ号' commandtips['setvoicept <训练集>'] = '#设置文字转语音回复的训练集' + commandtips['settemp <条数>'] = '#设置单个群中消息缓存最大数目' commandtips['blackfreq <次数>'] = '#设置黑名单容错次数' commandtips['uploadwav'] = '#上传源音频文件' commandtips['admin'] = '#进入管理模式' @@ -108,24 +112,45 @@ def commandhelp(self, fromchat): # 多线程类的复写 class My_Thread(threading.Thread): + Trynum = 0 daemon = True + def TrynumSet(self, Trynum): + self.Trynum = Trynum + + def Raise_log(self, traceback_str): + log = open('log.log', 'a', encoding='utf-8-sig') + log.write( + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n' + + traceback_str + '\n') + log.close() + print('抛出异常,已记录到日志(log.log文件)') + print(traceback_str) + def run(self): # 常驻为守护线程 - try: - if self._target: + if self._target: + try: self._target(*self._args, **self._kwargs) - except: - log = open('log.log', 'a', encoding='utf-8-sig') - traceback_str = traceback.format_exc() - log.write( - time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n' + - traceback_str + '\n') - log.close() - print('抛出异常,已记录到日志(log.log文件)') - print(traceback_str) - finally: - del self._target, self._args, self._kwargs + except ConnectionError: + if self.Trynum == 0: + print('{}:未与api-http取得连接,或mirai未登录'.format(self._target)) + print('{}:正在进行第{}次重试'.format(self._target.__name__, + self.Trynum + 1)) + time.sleep(2) + New_Thread = My_Thread(target=self._target, args=self._args) + New_Thread.TrynumSet(self.Trynum + 1) + New_Thread.start() + except requests.exceptions.ConnectionError: + #print('{}:与api-http连接被强制中断'.format(self._target)) + self.Raise_log('{}:与api-http连接被强制中断'.format(self._target)) + New_Thread = My_Thread(target=self._target, args=self._args) + New_Thread.start() + except: + self.Raise_log(traceback.format_exc()) + + #finally: + # del self._target, self._args, self._kwargs # 返回版本号 @@ -134,65 +159,74 @@ def Version(): return version -# 2.7.0前版本更新需要更换词库的缓存形式 -def ClChange(): - try: - filelist = os.listdir('WordStock') # 获取词库列表 - except: - filelist = os.listdir() # 获取词库列表 - cllist = [] - for i in filelist: - if i[-3:] == '.cl': - #print(i) - cllist.append(i) - #print(cllist) - print('正在为更新做一些准备,请稍等') - print('期间请勿关闭程序,否则将导致数据丢失!') - for i in cllist: - file = open(i, 'r', encoding='utf-8-sig') - dicts = file.read() - dicts = eval(dicts) - file.close() - pickle.dump(dicts, open(i, 'wb')) - print('准备完毕!') - - -def Cl_version(version): - version = int(version.replace('.', '')) - try: - filelist = os.listdir('WordStock') # 获取词库列表 - except: - filelist = os.listdir() - cllist = [] - if not (os.path.exists('WordStock')): - os.mkdir('WordStock') - for i in filelist: - if i[-3:] == '.cl': - #print(i) - cllist.append(i) - #print(cllist) - # 2.8.0前版本更新需要为词库Key添加"freq"次数键 - if version < 280: - print('正在更新词库版本,{} -> 280 请勿中途退出'.format(version)) +# 更新类 +class Update(): + + def __init__(self, config_version) -> None: + self.version = config_version + self.version = int(self.version.replace('.', '')) + + # 2.7.0前版本更新需要更换词库的缓存形式 + def ClChange(): + try: + filelist = os.listdir('WordStock') # 获取词库列表 + except: + filelist = os.listdir() # 获取词库列表 + cllist = [] + for i in filelist: + if i[-3:] == '.cl': + #print(i) + cllist.append(i) + #print(cllist) + print('正在为更新做一些准备,请稍等') + print('期间请勿关闭程序,否则将导致数据丢失!') for i in cllist: - dicts = pickle.load(open(i, 'rb')) - for k in dicts.keys(): - questiondict = dicts[k] - if not ('freq' in questiondict.keys()): - questiondict['freq'] = 1 + file = open(i, 'r', encoding='utf-8-sig') + dicts = file.read() + dicts = eval(dicts) + file.close() pickle.dump(dicts, open(i, 'wb')) - shutil.move(i, 'WordStock/' + i) - - -def Config_version(version): - version = int(version.replace('.', '')) - file = open('config.clc', 'r', encoding='utf-8-sig') - config = eval(file.read()) - file.close() - if version < 280: - print('正在更新config, -> 280 请勿中途退出') - config['tag'] = {} - config['singlereplychance'] = {} - config['singlevoicereplychance'] = {} - config['typefreq'] = {} - return config + print('准备完毕!') + + def Cl_version(self): + try: + filelist = os.listdir('WordStock') # 获取词库列表 + except: + filelist = os.listdir() + cllist = [] + if not (os.path.exists('WordStock')): + os.mkdir('WordStock') + for i in filelist: + if i[-3:] == '.cl': + #print(i) + cllist.append(i) + #print(cllist) + # 2.8.0前版本更新需要为词库Key添加"freq"次数键 + if self.version < 280: + print('正在更新词库版本,{} -> 280 请勿中途退出'.format(self.version)) + for i in cllist: + dicts = pickle.load(open(i, 'rb')) + for k in dicts.keys(): + questiondict = dicts[k] + if not ('freq' in questiondict.keys()): + questiondict['freq'] = 1 + pickle.dump(dicts, open(i, 'wb')) + shutil.move(i, 'WordStock/' + i) + + def Config_version(self): + file = open('config.clc', 'r', encoding='utf-8-sig') + config = eval(file.read()) + file.close() + # 280版本功能加入 + if self.version < 280: + print('正在更新config, -> 280 请勿中途退出') + config['tag'] = {} + config['singlereplychance'] = {} + config['singlevoicereplychance'] = {} + config['typefreq'] = {} + # 290版本功能加入 + if self.version < 290: + print('正在更新config, -> 290 请勿中途退出') + config['tempmessagenum'] = 32 + config['fastdelete'] = 0 + return config diff --git a/ChatReply.py b/ChatReply.py index 99ff6f7..24acb52 100644 --- a/ChatReply.py +++ b/ChatReply.py @@ -8,6 +8,7 @@ import requests +import ChatAllfind import ChatFilter import simuse @@ -48,6 +49,74 @@ def DelType(tempdict, answerlist): return new_answerlist +def Judge_Fast_Delete(data, TempMessage, group, messagechain, sender): + if getconfig(13) == 0: + if not (sender in getconfig(14)): + return 1 + First_index = messagechain[0] + IS_ME = 0 + if First_index['type'] == 'Quote': + SourceId = First_index['id'] + for i in messagechain: + if i['type'] == 'At': + if str(i['target']) == data['qq']: + IS_ME = 1 + if i['type'] == 'Plain' and IS_ME == 1: + if i['text'].lower() == ' !delete' or i['text'].lower( + ) == ' !delete': + Delete_Sign = Fast_Delete(TempMessage, group, SourceId) + break + else: + return 0 + if Delete_Sign == 1: + global RecallList + simuse.Recall_Message(data, SourceId) + time.sleep(1) + RecallList.append(simuse.Send_Message(data, group, 1, '删除成功!', 1)) + elif Delete_Sign == 0: + RecallList.append( + simuse.Send_Message(data, group, 1, '删除失败,该消息已不在缓存内', 1)) + elif Delete_Sign == -1: + RecallList.append( + simuse.Send_Message(data, group, 1, '删除失败,词库中已无法找到该答案', 1)) + return 1 + else: + return 0 + + +def Fast_Delete(TempMessage, group, SourceId): + try: + IDdict = TempMessage[group] + IDlist = IDdict[SourceId] + except: + return 0 + filelist = ChatAllfind.getcllist() + IS_FIND = 0 + for filename in filelist: + THIS_IS_FIND = 0 + cldict = pickle.load(open('WordStock/' + filename, 'rb')) + try: + questiondict = cldict[IDlist[0]] + except: + continue + answerlist = questiondict['answer'] + for answerdict in answerlist: + if answerdict['answertext'] == IDlist[1]: + answerlist.remove(answerdict) + IS_FIND = 1 + THIS_IS_FIND = 1 + if answerlist == []: + cldict.pop(IDlist[0]) + break + if THIS_IS_FIND == 1: + pickle.dump(cldict, open('WordStock/' + filename, 'wb')) + if IS_FIND == 1: + IDdict.pop(SourceId) + return 1 + else: + return -1 + + def talkvoice(data, group, messagechain): global nowtime try: @@ -184,6 +253,15 @@ def getconfig(choice): elif choice == 11: voicereplydict = config['singlevoicereplychance'] return voicereplydict + elif choice == 12: + TempMessageNum = config['tempmessagenum'] + return TempMessageNum + elif choice == 13: + FastDelete = config['fastdelete'] + return FastDelete + elif choice == 14: + Adminlist = config['Administrator'] + return Adminlist def getanswer(group, question): # 从词库中获取答案 @@ -256,6 +334,7 @@ def replyanswer(data, group, answer): # 发送答案 answer = eval(answer) except: pass + origin_answer = copy.deepcopy(answer) for i in answer: # 去除答案中的imageId,不去除mirai api http会无法回复 #print(i) try: @@ -302,15 +381,28 @@ def replyanswer(data, group, answer): # 发送答案 if number != None: if voicereplysign == 0: print('答案已发送', number) + return origin_answer, number else: print('答案已发送(语音)', number) + return origin_answer, number #print('->',end='') else: print('答案发送失败') def listening(data): + global RecallList + RecallList = [] + AfterSecond = 0 + TempMessage = {} while 1: + if RecallList != []: + AfterSecond += 1 + if RecallList != [] and AfterSecond >= 10: + for messageid in RecallList: + simuse.Recall_Message(data, messageid) + AfterSecond = 0 + RecallList = [] if getconfig(3) == 0: return None message = simuse.Fetch_Message(data) # 监听消息链 @@ -328,10 +420,31 @@ def listening(data): messagechain.pop(0) if talkvoice(data, group, messagechain) == 1: continue + if Judge_Fast_Delete(data, TempMessage, group, messagechain, + i['sender']) == 1: + continue question = messagechain answer = getanswer(group, question) # 获取答案 if answer != -1 and answer != None: - replyanswer(data, group, answer) # 让bot回复 + reply_answer_info = replyanswer(data, group, + answer) # 让bot回复 + if reply_answer_info != None: + reply_answer = reply_answer_info[0] + SourceId = reply_answer_info[1] + if reply_answer != None: + try: + IDdict = TempMessage[group] + # TempMessage:{group:{id:[question,answer],id:[question,answer]}} + except: + IDlist = [str(question), str(reply_answer)] + IDdict = {} + IDdict[SourceId] = IDlist + TempMessage[group] = IDdict + else: + IDlist = [str(question), str(reply_answer)] + IDdict[SourceId] = IDlist + if len(IDdict) > getconfig(12): + IDdict.pop(list(IDdict.keys())[0]) time.sleep(0.5) diff --git a/Chatmain.py b/Chatmain.py index 42f50cc..0f4216e 100644 --- a/Chatmain.py +++ b/Chatmain.py @@ -21,8 +21,7 @@ import ChatReply import ChatSubadmin import simuse -from ChatClass import (Cl_version, ClChange, Config_version, My_Thread, - Version, commandclass) +from ChatClass import My_Thread, Update, Version, commandclass nest_asyncio.apply() @@ -38,10 +37,13 @@ def hello(): config['learning'] = 0 config['admin'] = 0 if ('version' in config.keys()) == False: - ClChange() + update = Update('0.0.0') + update.ClChange() config['version'] = '2.7.0' - Cl_version(config['version']) - config = Config_version(config['version']) + else: + update = Update(config['version']) + update.Cl_version() + config = update.Config_version() config['version'] = Version() file = open('config.clc', 'w', encoding='utf-8-sig') file.write(str(config)) @@ -140,7 +142,62 @@ def testvoice(data, text, fromchat=0): simuse.Send_Message(data, fromchat, 2, '转换失败,存在不支持的字符', 1) -def blackfreq(num, fromchat=0): +def SetTempMessage(data, num, fromchat=0): + file = open('config.clc', 'r', encoding='utf-8-sig') + config = file.read() + file.close() + config = eval(config) + try: + num = int(num) + except: + print('参数错误') + return None + if num < 1: + print('参数错误') + return None + config['tempmessagenum'] = num + file = open('config.clc', 'w', encoding='utf-8-sig') + file.write(str(config)) + file.close() + print('每个群的消息缓存已设置为{}条'.format(num)) + if fromchat != 0: + simuse.Send_Message(data, fromchat, 2, '每个群的消息缓存已设置为{}条'.format(num), + 1) + + +def SetFastDeleteAdmin(FastDeletesign,fromchat=0): + global data + if FastDeletesign == 0: + file = open('config.clc', 'r', encoding='utf-8-sig') + config = file.read() + file.close() + config = eval(config) + config['fastdelete'] = 1 + file = open('config.clc', 'w', encoding='utf-8-sig') + file.write(str(config)) + file.close() + print('<-快速删除已设置为所有人可用') + if fromchat != 0: + simuse.Send_Message(data, fromchat, 2, '快速删除已设置为所有人可用', 1) + FastDeletesign = 1 + return FastDeletesign + else: + FastDeletesign = 0 + file = open('config.clc', 'r', encoding='utf-8-sig') + config = file.read() + file.close() + config = eval(config) + config['fastdelete'] = 0 + file = open('config.clc', 'w', encoding='utf-8-sig') + file.write(str(config)) + file.close() + print('<-快速删除已设置为仅管理员可用') + if fromchat != 0: + simuse.Send_Message(data, fromchat, 2, '快速删除已设置为仅管理员可用', 1) + return FastDeletesign + + +def blackfreq(data, num, fromchat=0): file = open('config.clc', 'r', encoding='utf-8-sig') config = file.read() file.close() @@ -847,7 +904,13 @@ def addgroup(args, fromchat=0): except: Subadmindict = {} for i in grouplist: - Subadmindict[i] = get_group_perm(data, i) + try: + Subadmindict[i] = get_group_perm(data, i) + except: + print('群不存在') + if fromchat != 0: + simuse.Send_Message(data, fromchat, 2, '群不存在', 1) + return None config['subadmin'] = Subadmindict file = open('config.clc', 'w', encoding='utf-8-sig') file.write(str(config)) @@ -1345,6 +1408,8 @@ def typefreq(data, args, fromchat=0): def getcommand_chat(): global data global adminsendmode + data = simuse.Get_data() # Test + data = simuse.Get_Session(data) # Test while 1: if adminsendmode == 1: print('none') @@ -1416,6 +1481,7 @@ def commandchoice(command, fromchat=0): global replysign global adminsign global voicereplysign + global FastDeletesign command = command.lower() command = ' '.join(command.split()) commandlist = commandclass(data, command) @@ -1477,11 +1543,15 @@ def commandchoice(command, fromchat=0): adminsign = admin(adminsign, fromchat) return 'breaksign' elif command[:10] == 'blackfreq ': - blackfreq(command[10:]) + blackfreq(data, command[10:]) elif command[:11] == 'setvoicept ': setvoicept(data, command[11:], fromchat) elif command[:9] == 'typefreq ': typefreq(data, command[9:], fromchat) + elif command[:8] == 'settemp ': + SetTempMessage(data, command[8:], fromchat) + elif command == 'fastdelete': + FastDeletesign = SetFastDeleteAdmin(FastDeletesign,fromchat) elif command == 'exit': exit() elif command == 'help' or command == '?' or command == '?': @@ -1500,6 +1570,10 @@ def commandchoice(command, fromchat=0): voicereplysign = ChatReply.getconfig(5) except: voicereplysign = 0 + try: + FastDeletesign = ChatReply.getconfig(13) + except: + FastDeletesign = 0 adminsign = 0 data = simuse.Get_data() data = simuse.Get_Session(data)