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 WARN(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.started and hasattr(module, 'onPlug'): _call(module.onPlug, self) return result
def get_wds_rank(self, wds, type0=1, page=1, page_size=20): """ 获取微打赏聚聚榜 :param wds: :param pro_id: :param type0: :param page: :param page_size: :return: """ jizi_url = 'https://wds.modian.com/ajax/backer_ranking_list' params = { 'pro_id': wds.pro_id, 'type': type0, 'page': page, 'page_size': page_size } try: # 注意,这里的param不用转换成json了,因为参数格式为x-www-form-urlencoded r = self.session.post(jizi_url, params, headers=self.wds_header()) except Exception as e: ERROR('获取微打赏排名失败') ERROR(e) r_json = r.json() # DEBUG('response: %s', r.text) # 微打赏有bug,首页上和排名页上的人数不一致 if 'data' not in r_json or int(r_json['status']) != 0: ERROR('微打赏排名获取失败!') return None return r_json['data']['html']
def getManaulGroupQQDict(): mgQQDict = collections.defaultdict(list) from qqbot import QQBot fn = QQBot._bot.conf.absPath('groupqq') if not os.path.exists(fn): return mgQQDict try: with open(fn, 'rb') as f: s = f.read() except Exception as e: ERROR('无法读取群 QQ 文件 %s , %s', fn, e) return mgQQDict try: s = BYTES2STR(s) except Exception as e: ERROR('群 QQ 文件 %s 编码错误, %s', fn, e) return mgQQDict try: for line in s.split('\n'): if not line.startswith('#') and (',' in line): qq, nick = line.rstrip('\r').split(',', 1) mgQQDict[nick].append(qq) except Exception as e: ERROR('群 QQ 文件 %s 格式错误, %s', fn, e) return mgQQDict INFO('成功从文件 %s 中读取群的实际 QQ 号码', fn) return mgQQDict
def init_comment_queues(self): """ 初始化回复队列 :return: """ try: self.comment_id_queues = [] for i in range(len(self.wds_array)): self.comment_id_queues.append([]) for i in range(len(self.wds_array)): queue = self.comment_id_queues[i] r = self.monitor_wds_comment(self.wds_array[i]) soup = BeautifulSoup(r, 'lxml') comment_list = soup.findAll(name='li') for comment in comment_list: comment_id = comment.find( class_='add-jubao').get('to_comid') queue.append(comment_id) self.wds_queue_map[self.wds_array[i]] = queue except Exception as e: ERROR('初始化微打赏评论队列失败') ERROR(e)
def get_member_room_comment(self, room_id, limit=20): """ 获取成员房间的粉丝评论 :param limit: :param room_id: 房间id :return: """ if not self.is_login: ERROR('尚未登录') return False # url = 'https://pjuju.48.cn/imsystem/api/im/v1/member/room/message/comment' url = 'https://pjuju.48.cn/imsystem/api/im/v1/member/room/message/boardpage' params = { "roomId": room_id, "lastTime": 0, "limit": limit, "isFirst": "true" } # 收到响应 try: r = self.session.post(url, data=json.dumps(params), headers=self.juju_header_args(), verify=False) except Exception as e: ERROR('获取房间评论失败') ERROR(e) return r.text
def init_msg_queues(self, room_id): """ 初始化房间消息队列 :param room_id: :return: """ try: self.member_room_msg_ids = [] self.member_room_comment_ids = [] self.member_live_ids = [] self.unread_msg_amount = 0 r1 = self.get_member_room_msg(room_id) r2 = self.get_member_room_comment(room_id) r1_json = json.loads(r1) r2_json = json.loads(r2) for r in r1_json['content']['data']: msg_id = r['msgidClient'] self.member_room_msg_ids.append(msg_id) for r in r2_json['content']['data']: msg_id = r['msgidClient'] self.member_room_comment_ids.append(msg_id) DEBUG('成员消息队列: %s', len(self.member_room_msg_ids)) DEBUG('房间评论队列: %s', len(self.member_room_comment_ids)) DEBUG('房间未读消息数量: %d', self.unread_msg_amount) except Exception as e: ERROR('初始化消息队列失败') ERROR(e)
def login(self, username, password): """ 登录 :param username: :param password: :return: """ if self.is_login is True: ERROR('已经登录!') return if username is None or password is None: ERROR('用户名或密码为空') return login_url = 'https://puser.48.cn/usersystem/api/user/v1/login/phone' params = { 'latitude': '0', 'longitude': '0', 'password': str(password), 'account': str(username), } res = self.session.post(login_url, json=params, headers=self.login_header_args()).json() # 登录成功 if res['status'] == 200: self.token = res['content']['token'] self.is_login = True INFO('登录成功, 用户名: %s', username) INFO('TOKEN: %s', self.token) return True else: ERROR('登录失败') return False
def get_member_live_msg(self, limit=30): """ 获取所有直播间信息 :return: """ # 登录失败 不访问需要鉴权的接口 if not self.is_login: ERROR('尚未登录') url = 'https://plive.48.cn/livesystem/api/live/v1/memberLivePage' params = { "giftUpdTime": 1503766100000, "groupId": 0, # SNH48 Group所有人 "lastTime": 0, "limit": limit, "memberId": 0, "type": 0 } try: r = self.session.post(url, data=json.dumps(params), headers=self.live_header_args(), verify=False) except Exception as e: ERROR('获取成员直播失败') ERROR(e) return r.text
def onQQMessage_real(self, dialog, member, content): if dialog.qq == '#NULL': ERROR("missing dialog.qq for message %s" % content) return if member is not None: if member.qq == '#NULL': ERROR("missing member.qq for message %s" % content) return sender = member try: target = self.channelNames.toIRC[dialog.qq] except KeyError: target = '#' + dialog.qq isChannel = True else: sender = dialog target = self.me isChannel = False hostmask = self.server.buildHostmask(self.nickNames.toIRC[sender.qq], sender.qq) for line in self.newLineRegex.split(content): if isChannel and target not in self.joinedChannels: self.ircmsg(hostmask, 'PRIVMSG', self.rawChannel, '%s: %s' % (target, line)) else: self.ircmsg(hostmask, 'PRIVMSG', target, line)
def Run(self): if self.conf.startAfterFetch: self.firstFetch() self.onPlug() self.onStartupComplete() # child thread 1~4 StartDaemonThread(self.pollForever) StartDaemonThread(self.intervalForever) StartDaemonThread( QTermServer(self.conf.termServerPort, self.onTermCommand).Run) self.scheduler.start() self.started = True try: MainLoop() except SystemExit as e: self.onExit(e.code, getReason(e.code), None) raise except Exception as e: ERROR('', exc_info=True) ERROR('Mainloop 发生未知错误:%r', e) self.onExit(1, 'unknown-error', e) raise SystemExit(1)
def FetchTable(self, tinfo): ctype, owner = GetCTypeAndOwner(tinfo) try: if ctype == 'buddy': table = self.fetchBuddyTable() elif ctype == 'group': table = self.fetchGroupTable() elif ctype == 'discuss': table = self.fetchDiscussTable() elif ctype == 'group-member': table = self.fetchGroupMemberTable(owner) else: table = self.fetchDiscussMemberTable(owner) except RequestError: table = None except: ERROR('', exc_info=True) table = None if table is None: if ctype in ('buddy', 'group', 'discuss'): ERROR('获取 %s 列表失败', CTYPES[ctype]) else: ERROR('获取 %s 的成员列表失败', owner) return table
def get_member_room_msg(self, room_id, limit=5): """ 获取成员房间消息 :param limit: :param room_id: 房间id :return: """ if not self.is_login: ERROR('尚未登录') return False # url = 'https://pjuju.48.cn/imsystem/api/im/v1/member/room/message/chat' url = 'https://pjuju.48.cn/imsystem/api/im/v1/member/room/message/mainpage' params = { "roomId": room_id, "lastTime": 0, "limit": limit, "chatType": 0 } try: r = self.session.post(url, data=json.dumps(params), headers=self.juju_header_args(), verify=False) except Exception as e: ERROR('获取成员消息失败') ERROR(e) return r.text
def send(self, prefix, command, params, tail): words = [':' + prefix, command.upper()] + params + [':' + tail] msg = ' '.join(words) try: self.sock.sendall(STR2BYTES(msg) + b'\r\n') except socket.timeout: ERROR('在向 %s 发送数据时出现 %s', self.name, 'SOCKET-TIMEOUT') except socket.error: ERROR('在向 %s 发送数据时出现 %s', self.name, 'SOCKET-ERROR') self.close() else: DEBUG('%s ==> %s: %r', self.servername, self.name, msg)
def onData(self, sock, addr, data): try: resp = self.response(data) except Exception as e: resp = '%s 在处理 %s:%s 的请求时发生错误,%s' % (self.name, addr[0], addr[1], e) ERROR(resp, exc_info = True) resp = STR2BYTES(resp) try: sock.sendall(resp) except socket.error as e: ERROR('%s 在向 %s:%s 发送数据时发送错误,%s', self.name, addr[0], addr[1], e) self.onSendError(sock, addr, data) finally: sock.close()
def onTermCommand(bot, command): command = BYTES2STR(command) if command.startswith('GET /'): http = True end = command.find('\r\n') if end == -1 or not command[:end - 3].endswith(' HTTP/'): argv = [] else: url = command[5:end - 9].rstrip('/') if url == 'favicon.ico': return b'' argv = [Unquote(x) for x in url.split('/')] else: http = False argv = command.strip().split(None, 3) if argv and argv[0] in cmdFuncs: try: result, err = cmdFuncs[argv[0]](bot, argv[1:], http) except Exception as e: result, err = None, '运行命令过程中出错:' + str(type(e)) + str(e) ERROR(err, exc_info=True) else: result, err = None, 'QQBot 命令格式错误' if http: rep = {'result': result, 'err': err} rep = STR2BYTES(JsonDumps(rep, ensure_ascii=False, indent=4)) rep = (b'HTTP/1.1 200 OK\r\n' + b'Connection: close\r\n' + b'Content-Length: ' + STR2BYTES(str(len(rep))) + b'\r\n' + b'Content-Type: text/plain;charset=utf-8\r\n\r\n' + rep) else: rep = STR2BYTES(str(err or result)) + b'\r\n' return rep
def Plug(cls, moduleName): cls.unplug(moduleName) try: module = Import(moduleName) except (Exception, SystemExit) as e: result = '错误:无法加载插件 %s ,%s: %s' % (moduleName, type(e), e) ERROR(result) else: cls.unplug(moduleName, removeJob=False) names = [] for slotName in cls.slotsTable.keys(): if hasattr(module, slotName): cls.slotsTable[slotName].append(getattr(module, slotName)) names.append(slotName) if (not names) and (moduleName not in cls.schedTable): result = '警告:插件 %s 中没有定义回调函数或定时任务' % moduleName WARN(result) else: cls.plugins.add(moduleName) jobs = cls.schedTable.get(moduleName, []) jobNames = [f.func.__name__ for f in jobs] result = '成功:加载插件 %s(回调函数%s、定时任务%s)' % \ (moduleName, names, jobNames) INFO(result) return result
def main_loop(): read = set() filters = [ 'edx', 'github', 'noreply', 'docker team', 'intel', 'vuforia', 'welcome' ] filters = [a.lower() for a in filters] while True: try: # 遍历未读邮件 for num in reversed(server.get_unread()): if num != '': mail_info = server.get_mail_info(num) mail_id = mail_info['id'] if mail_id in read: break content = mail_info['subject'].replace('\n', '') app = "邮箱(" + mail_info['to'][1] + ')' who = mail_info['from'][0] INFO('来自%s%s的邮件:%s' % (app, who, content)) for f in filters: if f in who.lower(): server.add_flag(num, '\\Deleted') break else: __msg_queue.put((app, who, content)) read.add(mail_id) except Exception as e: ERROR(str(type(e)) + str(e)) server.expunge() time.sleep(3)
def main_loop(): while True: try: itchat.run() except Exception as e: ERROR(e) time.sleep(3)
def Plug(cls, moduleName): try: module = Import(moduleName) except (Exception, SystemExit) as e: cls.unplug(moduleName) cls.plugins.discard(moduleName) result = '错误:无法加载插件 %s ,%s: %s' % (moduleName, type(e), e) ERROR(result) else: cls.unplug(moduleName) names = [] for slotName in cls.slotsTable.keys(): if hasattr(module, slotName): cls.slotsTable[slotName].append(getattr(module, slotName)) names.append(slotName) if not names: INFO(module.__dict__.keys()) result = '警告:插件 %s 中不包含任何可注册的回调函数' % moduleName WARN(result) else: cls.plugins.add(moduleName) result = '成功:加载插件 %s%s' % (moduleName, names) INFO(result) return result
def insertChatContent(bot, contact, member, content): try: # 连接数据库 connect = pymysql.Connect(host='localhost', port=3306, user='******', passwd='19950105', db='qq_data', charset='utf8') # 获取游标 cursor = connect.cursor() now = datetime.datetime.now() createtime = now.strftime('%Y-%m-%d %H:%M:%S') # 插入数据 sql = "INSERT INTO chat_logs (group_number,group_name,qq,nickname,mark,content,create_time) VALUES ( '%s', '%s', '%s','%s','%s', '%s', '%s')" name = pymysql.escape_string(contact.name) nickname = pymysql.escape_string(member.name) mark = pymysql.escape_string(member.name) data = (contact.qq, name, member.qq, nickname, mark, content, createtime) print('聊天数据 {}'.format(data)) cursor.execute(sql % data) connect.commit() connect.close() print('insert success', cursor.rowcount, ' record') except Exception as e: ERROR('存储mysql 失败 原因是: {}'.format(e))
def StoreQQ(self): try: fn = self.absPath('qq(pid%s)' % os.getpid()) with open(fn, 'w') as f: f.write(getattr(self, 'qq', '')) except Exception as e: ERROR('无法保存当前 QQ 号码, %s', e)
def membsOperation(self, group, membs, tag, func, exArg): if not membs: return [] try: ok = func(group.qq, [m.qq for m in membs], exArg) except RequestError: errInfo = '错误:' + tag + '失败(远程请求被拒绝)' result = [errInfo.format(m=str(m)) for m in membs] except Exception as e: WARN('', exc_info=True) errInfo = '错误:' + tag + '失败(' + str(e) + ')' result = [errInfo.format(m=str(m)) for m in membs] else: if ok: okInfo = '成功:' + tag result = [okInfo.format(m=str(m)) for m in membs] else: errInfo = '错误:' + tag + '失败(权限不够)' result = [errInfo.format(m=str(m)) for m in membs] for r in result: INFO(r) if r.startswith('成功') else ERROR(r) return result
def main_loop(): while True: try: bot.Run() except Exception as e: ERROR(e) time.sleep(3)
def urlGet(self, url, data=None, Referer=None, Origin=None): Referer and self.session.headers.update( {'Referer': Referer} ) Origin and self.session.headers.update( {'Origin': Origin} ) timeout = 5 if url != 'https://d1.web2.qq.com/channel/poll2' else 120 try: if data is None: return self.session.get(url, timeout=timeout) else: return self.session.post(url, data=data, timeout=timeout) except (requests.exceptions.SSLError, AttributeError): # by @staugur, @pandolia if self.session.verify: time.sleep(5) ERROR('无法和腾讯服务器建立私密连接,' ' 15 秒后将尝试使用非私密连接和腾讯服务器通讯。' '若您不希望使用非私密连接,请按 Ctrl+C 退出本程序。') time.sleep(15) WARN('开始尝试使用非私密连接和腾讯服务器通讯。') self.session.verify = False requests.packages.urllib3.disable_warnings( requests.packages.urllib3.exceptions. InsecureRequestWarning ) return self.urlGet(url, data, Referer, Origin) else: raise
def SendTo(self, contact, content): if not isinstance(contact, QContact): return '错误:消息接受者必须为一个 QContact 对象' if contact.ctype.endswith('-member'): return '错误:不能给群成员或讨论组成员发消息' if not isinstance(content, str): return '错误:只能发送字符串消息' if not content: return '错误:不允许发送空消息' ctype = 'buddy' if contact.ctype.endswith('member') else contact.ctype result = '向 %s 发消息成功' % contact while content: front, content = Partition(content, 600) try: self.send(ctype, contact.uin, front) except Exception as e: result = '错误:向 %s 发消息失败 %s' % (str(contact), e) ERROR(result, exc_info=(not isinstance(e, RequestError))) break INFO('%s:%s' % (result, front)) return result
def Poll(self): try: result = self.smartRequest( url='https://d1.web2.qq.com/channel/poll2', data={ 'r': JsonDumps({ 'ptwebqq': self.ptwebqq, 'clientid': self.clientid, 'psessionid': self.psessionid, 'key': '' }) }, Referer=('https://d1.web2.qq.com/proxy.html?v=20151105001&' 'callback=1&id=2'), expectedCodes=(0, 100003, 100100, 100012)) # "{'retcode': 0, 'retmsg': 'ok', 'errmsg': 'error'}" if type(result) is dict and \ result.get('retcode', 1) == 0 and \ result.get('errmsg', '') == 'error': DEBUG(result) raise RequestError except RequestError: ERROR('接收消息出错,开始测试登录 cookie 是否过期...') try: self.TestLogin() except RequestError: ERROR('登录 cookie 很可能已过期') raise else: INFO('登录 cookie 尚未过期') return 'timeout', '', '', '' else: if (not result) or (not isinstance(result, list)): DEBUG(result) return 'timeout', '', '', '' else: result = result[0] ctype = { 'message': 'buddy', 'group_message': 'group', 'discu_message': 'discuss' }[result['poll_type']] fromUin = str(result['value']['from_uin']) memberUin = str(result['value'].get('send_uin', '')) content = FaceReverseParse(result['value']['content']) return ctype, fromUin, memberUin, content
def Login(self, conf): try: INFO('正在使用“用户名-密码”登录,请耐心等待 1 ~ 3 分钟......') self.newLogin(conf) except Exception as e: ERROR('用户名-密码登录失败,原因:%s', e, exc_info=True) INFO('开始使用“手工扫码”登录......') BasicQSession.Login(self, conf)
def init_bilibili_video_queues(self, bilibili_video_list): """ 初始化b站投稿队列 避免每次一开启 就发送n多消息 :param room_ids: :return: """ try: self.bilibili_video_ids = [] for bilibili_video in bilibili_video_list: video_id = bilibili_video['aid'] self.bilibili_video_ids.append(video_id) DEBUG('b站视频消息队列: %s', len(self.bilibili_video_ids)) except Exception as e: ERROR('初始化b站视频消息队列失败') ERROR(e)
def SendTo(self, contact, content, resendOn1202=True): result = None if not hasattr(contact, 'ctype'): result = '错误:消息接受者必须为一个 QContact 对象' if contact.ctype.endswith('-member'): result = '错误:不能给群成员或讨论组成员发消息' if PY3: if isinstance(content, str): content = content elif isinstance(content, bytes): content = content.decode('utf8') else: result = '错误:消息内容必须为 str 或 bytes 对象' else: if isinstance(content, str): content = content elif isinstance(content, unicode): content = content.encode('utf8') else: result = '错误:消息内容必须为 str 或 unicode 对象' if not content: result = '错误:不允许发送空消息' if result: ERROR(result) return result epCodes = resendOn1202 and [0] or [0, 1202] result = '向 %s 发消息成功' % contact while content: front, content = Partition(content) try: self.send(contact.ctype, contact.uin, front, epCodes) except Exception as e: result = '错误:向 %s 发消息失败 %s' % (str(contact), e) ERROR(result, exc_info=(not isinstance(e, RequestError))) break else: INFO('%s:%s' % (result, front)) return result
def onData(self, sock, addr, data): resp = self.response(data) try: sock.sendall(resp) except socket.error as e: ERROR('%s 在向 %s:%s 发送数据时发送错误 %s', self.name, addr[0], addr[1], e) self.onSendError(sock, addr, data) finally: sock.close()