class LockedValue(object): def __init__(self, initialVal=None): self.val = initialVal self.lock = threading.Lock() def setVal(self, val): with self.lock: self.val = val def getVal(self): with self.lock: val = self.val return val if __name__ == '__main__': from qconf import QConf # 需要先在 ~/.qqbot-tmp/v2.x.conf 文件中设置好邮箱帐号和授权码 conf = QConf() conf.Display() qrm = QrcodeManager(conf) with open('tmp.png', 'rb') as f: qrcode = f.read() qrm.Show(qrcode) time.sleep(5) qrm.Show(qrcode) qrm.Destroy()
class QQBot(): def __init__(self) -> None: self.scheduler = BackgroundScheduler(daemon=True) self.schedTable = defaultdict(list) self.slotsTable = { 'onStartupComplete': [], 'onInterval': [], 'onQQMessage': [], 'onQQEvent': [], 'onPlug': [], 'onUnplug': [], 'onExit': [] } self.plugins = {} def init(self, argv=None): for name, slots in self.slotsTable.items(): setattr(self, name, self.wrap(slots)) self.slotsTable['onQQRequestEvent'] = [] self.conf = QConf(argv) self.conf.Display() self.Mirai = MiraiApi(self.conf.qq, self.conf.verifyKey, self.conf.host, self.conf.port) while not self.Mirai.started: pass self.MessageFromId = self.Mirai.MessageFromId self.SendMessage = self.Mirai.SendMessage self.Nudge = self.Mirai.Nudge self.Recall = self.Mirai.Recall self.List = self.Mirai.List self.Profile = self.Mirai.Profile self.DeleteFriend = self.Mirai.DeleteFriend self.Mute = self.Mirai.Mute self.kick = self.Mirai.kick self.quit = self.Mirai.quit self.MuteAll = self.Mirai.MuteAll self.SetEssence = self.Mirai.SetEssence self.GroupConfig = self.Mirai.GroupConfig self.MemberInfo = self.Mirai.MemberInfo self.FileList = self.Mirai.FileList self.FileInfo = self.Mirai.FileInfo self.FileMkdir = self.Mirai.FileMkdir self.FileDelete = self.Mirai.FileDelete self.FileMove = self.Mirai.FileMove self.FilereName = self.Mirai.FilereName self.FileUpload = self.Mirai.FileUpload self.Upload = self.Mirai.Upload for pluginName in self.conf.plugins: self.Plug(pluginName) def Run(self): self.onStartupComplete() self.onPlug() StartDaemonThread(self.pollForever) StartDaemonThread(self.intervalForever) self.scheduler.start() Put(self.Update) try: MainLoop() except SystemExit as e: raise except Exception as e: ERROR('', exc_info=True) ERROR('Mainloop 发生未知错误:%r', e) self.onExit(1, 'unknown-error', e) raise SystemExit(1) def Stop(self): sys.exit(0) def Restart(self): sys.exit(RESTART) # child thread 1 def pollForever(self): while self.Mirai.started: try: result = self.Mirai.GetMessage() except RequestError: Put(sys.exit) break except: ERROR('qsession.Poll 方法出错', exc_info=True) else: if not result: continue for r in result: Put(self.MessageAnalyst, r) def MessageAnalyst(self, Message): if 'Message' in Message.type: Type = Message.type.replace('Message', '') Sender = Message.sender Message = Message.messageChain Source = Message.pop(0) if hasattr(Sender, 'group'): INFO( f'来自 {Type} {Sender.group.name} {Sender.memberName} 的消息({Source.id}):' ) else: INFO(f'来自 {Type} {Sender.nickname} 的消息({Source.id}):') INFO(str(Message)) self.onQQMessage(Type, Sender, Source, Message) elif 'RequestEvent' in Message.type: if hasattr(self, 'onQQRequestEvent'): operate, msg = self.onQQRequestEvent(Message) self.Mirai.Event_response(Message, operate, msg) elif 'Event' in Message.type: self.onQQEvent(Message) def onQQRequestEvent(self, Message): for f in self.slotsTable['onQQRequestEvent']: operate, msg = f(self, Message) if not operate: return operate, msg else: return operate, msg # child thread 2 def intervalForever(self): while True: time.sleep(300) Put(self.onInterval) def Command(self): while True: pass def wrap(self, slots): def func(*args, **kwargs): for f in slots: _call(f, self, *args, **kwargs) return func def AddSlot(self, func): self.slotsTable[func.__name__].append(func) return func def AddSched(self, **triggerArgs): def wrapper(func): job = lambda: Put(_call, func, self) job.__name__ = func.__name__ j = self.scheduler.add_job(job, CronTrigger(**triggerArgs)) self.schedTable[func.__module__].append(j) return func return wrapper def Update(self): self.Friend = self.List('Friend') self.Group = self.List('Group') self.Member = {} for g in self.Group: self.Member[g.id] = self.List('Member', g.id) for m in self.Member[g.id]: delattr(m, 'group') def unplug(self, moduleName, removeJob=True): for slots in self.slotsTable.values(): i = 0 while i < len(slots): if slots[i].__module__ == moduleName: slots[i] = slots[-1] slots.pop() else: i += 1 if removeJob: for job in self.schedTable.pop(moduleName, []): job.remove() self.plugins.pop(moduleName, None) def Plug(self, moduleName): self.unplug(moduleName) try: module = Import(moduleName) except Exception as e: result = '错误:无法加载插件 %s ,%s: %s' % (moduleName, type(e), e) ERROR('', exc_info=True) ERROR(result) self.unplug(moduleName) else: self.unplug(moduleName, removeJob=False) names = [] for slotName in self.slotsTable.keys(): if hasattr(module, slotName): self.slotsTable[slotName].append(getattr(module, slotName)) names.append(slotName) if not names and moduleName not in self.schedTable: result = '警告:插件 %s 中没有定义回调函数或定时任务' % moduleName WARNING(result) else: self.plugins[moduleName] = module jobs = self.schedTable.get(moduleName, []) jobNames = [f.func.__name__ for f in jobs] result = '成功:加载插件 %s(回调函数%s、定时任务%s)' % \ (moduleName, names, jobNames) INFO(result) if self.Mirai.started and hasattr(module, 'onPlug'): _call(module.onPlug, self) return result def Unplug(self, moduleName): if moduleName not in self.plugins: result = '警告:试图卸载未安装的插件 %s' % moduleName WARNING(result) return result else: module = self.plugins[moduleName] self.unplug(moduleName) if hasattr(module, 'onUnplug'): _call(module.onUnplug, self) result = '成功:卸载插件 %s' % moduleName INFO(result) return result def Plugins(self): return list(self.plugins.keys())