def __init__(self, qq=None, user=None, conf=None, ai=None): MessageFactory.__init__(self) self.conf = conf if conf else QConf(qq, user) ai = ai if ai else BasicAI() termServer = QTermServer(self.conf.termServerPort) self.On('qqmessage', ai.OnQQMessage) # main thread self.On('polltimeout', ai.OnPollTimeout) # main thread self.On('termmessage', ai.OnTermMessage) # main thread self.On('pollcomplete', QQBot.onPollComplete) # main thread self.AddGenerator(self.pollForever) # child thread 1 self.AddGenerator(termServer.Run) # child thread 2
def runBot(argv=None): if sys.argv[-1] == '--subprocessCall': sys.argv.pop() bot = QQBot._bot bot.init(argv) bot.Run() else: conf = QConf() if conf.daemon: conf.Daemonize() if sys.argv[0].endswith('py') or sys.argv[0].endswith('pyc'): args = [sys.executable] + sys.argv else: args = sys.argv if '--bench' not in args: args = args + ['--bench', os.getcwd()] args = args + ['--qq', str(conf.qq)] args = args + ['--subprocessCall'] while True: p = subprocess.Popen(args) code = p.wait() if code == 0: INFO('QQBot 正常停止') sys.exit(code) elif code == RESTART: INFO('1 秒后重新启动 QQBot (自动登陆,qq=%s)', args[-2]) time.sleep(1) else: CRITICAL('QQBOT 异常停止(code=%s)', code) if conf.restartOnOffline: args[-2] = '0' INFO('5秒后重新启动 QQBot (手动登录)') time.sleep(5) else: sys.exit(code)
def QLogin(qq=None, user=None, conf=None): if conf is None: conf = QConf(qq, user) if conf.qq: INFO('开始自动登录...') picklePath = conf.PicklePath() try: with open(picklePath, 'rb') as f: session, contacts = pickle.load(f) except Exception as e: WARN('自动登录失败,原因:%s', e) else: INFO('成功从文件 "%s" 中恢复登录信息和联系人' % picklePath) try: session.TestLogin() except QSession.Error: WARN('自动登录失败,原因:上次保存的登录信息已过期') else: INFO('登录成功。登录账号:%s(%s)', session.nick, session.qq) return session, contacts INFO('开始手动登录...') session = QSession() contacts = session.Login(conf) INFO('登录成功。登录账号:%s(%s)', session.nick, session.qq) conf.qq = session.qq picklePath = conf.PicklePath() try: with open(picklePath, 'wb') as f: pickle.dump((session, contacts), f) except IOError: WARN('保存登录信息及联系人失败:IOError %s', picklePath) else: INFO('登录信息及联系人已保存至文件:file://%s' % picklePath) return session, contacts
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 Main(): try: if sys.argv[-1] == '--subprocessCall': isSubprocessCall = True sys.argv.pop() else: isSubprocessCall = False conf = QConf() if not conf.restartOnOffline or isSubprocessCall: bot = QQBot(conf=conf) bot.Login() sys.exit(bot.Run()) else: args = ['python', __file__] + sys.argv[1:] + \ ['--mailAuthCode', conf.mailAuthCode, '--subprocessCall'] while subprocess.call(args) != 0: INFO('重新启动 QQBot ') except KeyboardInterrupt: sys.exit(0)
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()
N[T % 4] ^= ord(K[T]) U, V = 'ECOK', [0] * 4 V[0] = ((x >> 24) & 255) ^ ord(U[0]) V[1] = ((x >> 16) & 255) ^ ord(U[1]) V[2] = ((x >> 8) & 255) ^ ord(U[2]) V[3] = ((x >> 0) & 255) ^ ord(U[3]) U1 = [0] * 8 for T in range(8): U1[T] = N[T >> 1] if T % 2 == 0 else V[T >> 1] N1, V1 = '0123456789ABCDEF', '' for aU1 in U1: V1 += N1[((aU1 >> 4) & 15)] V1 += N1[((aU1 >> 0) & 15)] return V1 def bknHash(skey, init_str=5381): hash_str = init_str for i in skey: hash_str += (hash_str << 5) + ord(i) hash_str = int(hash_str & 2147483647) return hash_str if __name__ == '__main__': session, contacts = QLogin(conf=QConf())
osName = platform.system() if osName == 'Windows': filename = filename.decode('utf8').encode('cp936') retcode = subprocess.call([filename], shell=True) elif osName == 'Linux': retcode = subprocess.call(['gvfs-open', filename]) elif osName == 'Darwin': retcode = subprocess.call(['open', filename]) else: retcode = 1 if retcode: raise if __name__ == '__main__': from qconf import QConf # 需要先在 ~/.qqbot-tmp/v2.x.conf 文件中设置好邮箱帐号和授权码 conf = QConf(user='******') conf.Display() qrm = QrcodeManager(conf) with open('tmp.png', 'rb') as f: qrcode = f.read() qrm.Show(qrcode) time.sleep(60) qrm.Show(qrcode) qrm.Destroy() time.sleep(60)
return None, -1 data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1] if not PY3: msg = message_from_string(data[0][1]) s, encoding = decode_header(msg['Subject'])[0] subject = s.decode(encoding or 'utf-8').encode('utf-8') else: msg = message_from_bytes(data[0][1]) s, encoding = decode_header(msg['Subject'])[0] subject = s if type(s) is str else s.decode(encoding or 'utf-8') return subject if __name__ == '__main__': import time from qconf import QConf conf = QConf(user='******') ma = MailAgent(conf.mailAccount, conf.mailAuthCode) with ma.SMTP() as s: s.send(conf.mailAccount, 'hello', 'faf房间多啦') print('send ok') time.sleep(5) with ma.IMAP() as i: subject = i.getSubject(-1) print('latest email: '+subject) print('recv ok') # with ma.IMAP() as i: # subject = i.getUnSeenSubject(-1)[0]
def FileUpload(self, path: str, filepath: str): # 上传群文件 r'target is the groupID' payload = {'sessionKey': self.session} payload['type'] = 'group' payload['path'] = path files = filepath return self._file('upload', payload, files=files) ### 多媒体内容上传 ### def Upload(self, mode: str, form: str, filepath): # 上传图片或语言 if mode not in ['Image', 'Voice']: raise RequestError if form not in ['friend', 'group', 'temp']: raise RequestError if mode == 'Voice': form = 'group' payload = {'sessionKey': self.session} payload['type'] = form files = {'file': open(filepath, 'rb')} return self.basicsession(Post, 'upload{mode}', data=json.dumps(payload), files=files) if __name__ == '__main__': from qconf import QConf qconf = QConf() q = MiraiApi(qconf.qq, qconf.verifyKey) print(q.started)
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())
conn = self.conn id_list = conn.search(None, '(UNSEEN)')[1][0].split() try: email_id = id_list[i] except IndexError: return None, -1 data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1] msg = message_from_string(data[0][1]) s, encoding = decode_header(msg['Subject'])[0] subject = s.decode(encoding or 'utf-8').encode('utf-8') return subject if __name__ == '__main__': import time from qconf import QConf conf = QConf(user='******') ma = MailAgent(conf.mailAccount, conf.mailAuthCode) with ma.SMTP() as s: s.send(conf.mailAccount, 'hello') print 'send ok' # with ma.IMAP() as i: # subject = i.getUnSeenSubject(-1)[0] # print 'latest email:', subject # print 'recv ok' # # time.sleep(5) # # with ma.IMAP() as i: # i.delMail(subject)
return None, -1 data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1] if not PY3: msg = message_from_string(data[0][1]) s, encoding = decode_header(msg['Subject'])[0] subject = s.decode(encoding or 'utf-8').encode('utf-8') else: msg = message_from_bytes(data[0][1]) s, encoding = decode_header(msg['Subject'])[0] subject = s if type(s) is str else s.decode(encoding or 'utf-8') return subject if __name__ == '__main__': import time from qconf import QConf conf = QConf(['-u', 'hcj']) ma = MailAgent(conf.mailAccount, conf.mailAuthCode) with ma.SMTP() as s: s.send(conf.mailAccount, 'hello', 'faf房间多啦') print('send ok') time.sleep(5) with ma.IMAP() as i: subject = i.getSubject(-1) print('latest email: '+subject) print('recv ok') # with ma.IMAP() as i: # subject = i.getUnSeenSubject(-1)[0]