def sendEmail(self): lastSubject = '' while True: if lastSubject != self.qrcodeMail['subject']: qrcode = self.qrcode.getVal() if qrcode is None: break qrcode = '' if self.qrcodeServer else qrcode try: with self.mailAgent.SMTP() as smtp: smtp.send(png_content=qrcode, **self.qrcodeMail) except Exception as e: WARN('无法将二维码发送至邮箱%s %s', self.mailAgent.account, e, exc_info=True) else: INFO('已将二维码发送至邮箱%s', self.mailAgent.account) if self.qrcodeServer: break else: lastSubject = self.qrcodeMail['subject'] else: time.sleep(65) qrcode = self.qrcode.getVal() if qrcode is None: break try: DEBUG('开始查询邮箱 %s 中的最近的邮件', self.mailAgent.account) with self.mailAgent.IMAP() as imap: lastSubject = imap.getSubject(-1) except Exception as e: WARN('查询邮箱 %s 中的邮件失败 %s', self.mailAgent.account, e) else: DEBUG('最近的邮件: %s', lastSubject)
def get_room_msgs(bot): start_t = time.time() global pocket48_handler if pocket48_handler.is_login is False: DEBUG('登录失败,无法监控房间消息') return full_room_id_length = len(ConfigReader.get_all_member_room_id_list()) if 0 < len(global_config.ACTIVE_MEMBER_ROOM_ID_LIST) < full_room_id_length: # 获取监控房间的room列表,循环获取房间消息 for roomId in global_config.ACTIVE_MEMBER_ROOM_ID_LIST: r1 = pocket48_handler.get_member_room_msg(roomId[1]) pocket48_handler.parse_room_msg(r1) # 开启房间评论 # r2 = pocket48_handler.get_member_room_comment(global_config.ROOM_ID) # pocket48_handler.parse_room_comment(r2) # DEBUG('last_msg_time: %s', pocket48_handler.last_msg_time) end_t = time.time() DEBUG('获取房间消息 执行时间: %s', end_t-start_t) else: if len(global_config.ACTIVE_MEMBER_ROOM_ID_LIST) == full_room_id_length and len(pocket48_handler.member_room_msg_ids) >= pocket48_handler.init_room_msg_ids_length: for roomId in global_config.ACTIVE_MEMBER_ROOM_ID_LIST: r1 = pocket48_handler.get_member_room_msg(roomId[1]) pocket48_handler.parse_room_msg(r1) # 开启房间评论 # r2 = pocket48_handler.get_member_room_comment(global_config.ROOM_ID) # pocket48_handler.parse_room_comment(r2) # DEBUG('last_msg_time: %s', pocket48_handler.last_msg_time) end_t = time.time() DEBUG('获取房间消息 执行时间: %s', end_t-start_t) else: return
def get_member_weibo(bot): global weibo_handler DEBUG("Weibo check begin!") for weibo_id in weibo_handler.weibo_ids: res = weibo_handler.get_member_weibo(weibo_id) weibo_handler.parse_watchMember_weibo(res, weibo_id) DEBUG("Weibo check finish!")
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 get_member_room_msg_lite(self): """ 发送成员房间消息(简易版,只提醒在房间里出现) :return: """ time_now = time.time() msg = '' if self.unread_other_member_msg_amount > 0 and len( self.member_room_msg_lite_groups) > 0: if self.last_other_member_msg_time < 0 or time_now - self.last_other_member_msg_time >= 10 * 60: DEBUG('其他成员出现在房间中') member_name = ', '.join(self.other_members_names) QQHandler.send_to_groups(self.member_room_msg_lite_groups, '%s来你们灰的房间里串门啦~' % member_name) self.unread_other_member_msg_amount = 0 self.last_other_member_msg_time = time_now if self.unread_msg_amount > 0 and len( self.member_room_msg_lite_groups) > 0: # 距离上一次提醒时间超过10分钟且有未读消息 if self.last_msg_time < 0 or time_now - self.last_msg_time >= 10 * 60: DEBUG('向大群发送简易版提醒') msg = util.random_str(global_config.ROOM_MSG_LITE_NOTIFY) QQHandler.send_to_groups(self.member_room_msg_lite_groups, msg) INFO(msg) self.unread_msg_amount = 0 else: DEBUG('不向大群发送简易版提醒') self.last_msg_time = time_now else: INFO('最近10分钟内没有未读消息')
def onQQMessage(bot, contact, member, content): # 当收到 QQ 消息时被调用 # bot : QQBot 对象,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 # contact : QContact 对象,消息的发送者 # member : QContact 对象,仅当本消息为 群或讨论组 消息时有效,代表实际发消息的成员 # content : str 对象,消息内容 if content == '--version': DEBUG('member:%s', member) DEBUG('contact:%s', contact) DEBUG('content:%s', content) bot.SendTo(contact, 'QQbot-' + bot.conf.version)
def setTable(self, tinfo, table): ctype, owner = GetCTypeAndOwner(tinfo) if ctype in ('buddy', 'group', 'discuss'): self.ctables[ctype] = table # if ctype in ('group', 'discuss'): # for c in table.List(): # self.ctables[ctype+'-member'][c.uin] = NullTable DEBUG('已更新 %s 列表', CTYPES[ctype]) else: self.ctables[ctype][owner.uin] = table DEBUG('已更新 %s 的成员列表', owner)
def onQQMessage(bot, contact, member, content): # 当收到 QQ 消息时被调用 # bot : QQBot 对象,提供 List/SendTo/Stop/Restart 四个接口,详见文档第五节 # contact : QContact 对象,消息的发送者,具有 ctype/qq/uin/name/nick/mark/card 属性,这些属性都是 str 对象 # member : QContact 对象,仅当本消息为 群或讨论组 消息时有效,代表实际发消息的成员 # content : str 对象,消息内容 if contact.ctype != 'buddy': DEBUG("onQQMessage: ctype=" + contact.ctype + " member=(qq=" + member.qq + ", uin=" + str(member.uin) + ", name=" + member.name + ') uin=' + contact.uin + ' qq=' + contact.qq + ' name=' + contact.name) else: DEBUG("onQQMessage: ctype=" + contact.ctype + 'uin=' + contact.uin + ' qq=' + contact.qq + ' name=' + contact.name) pass
def get_roomChat_msg(self, _room_id, limit=10): url = "https://pjuju.48.cn/imsystem/api/im/v1/member/room/message/chat" params = {"lastTime": 0, "limit": limit, "roomId": _room_id} res = self.session.post(url, json=params, headers=self.roomChat_header_args()).json() if res["status"] == 200: DEBUG("Get room:%s chat success." % _room_id) else: DEBUG("fail to get room chat.") return res
def notify_group_number(bot): INFO('检查群内人数') QQHandler.update() for g_number in global_config.MEMBER_ROOM_MSG_LITE_GROUPS: number = QQHandler.get_group_number(g_number) DEBUG('群%s: %d人', g_number, number) DEBUG('global_config.GROUP_MEMBER_NUM: %d', global_config.GROUP_MEMBER_NUM[g_number]) if 0 < global_config.GROUP_MEMBER_NUM[g_number] < number: INFO('有新人入群啦~') g_obj = QQHandler.list_group([g_number]) QQHandler.send_to_groups(g_obj, '中泰机器人欢迎你~/好棒') global_config.GROUP_MEMBER_NUM[g_number] = number
def onFetchComplete(bot): # 完成一轮联系人列表刷新时被调用 # bot : QQBot 对象 # 以下为示例: DEBUG('onFetchComplete: All contacts lists fetch complete.') pass
def fetchSimpleGroupMemberTable(self, group): memberTable = QContactTable('group-member') try: r = self.smartRequest( url='http://qun.qq.com/cgi-bin/qun_mgr/search_group_members', Referer='http://qun.qq.com/member.html', data={ 'gc': group.qq, 'st': '0', 'end': '20', 'sort': '0', 'bkn': self.bkn }, repeateOnDeny=5) except RequestError: return memberTable except: DEBUG('', exc_info=True) return memberTable else: for m in r['mems']: qq, nick, card = \ str(m['uin']), str(m['nick']), str(m.get('card', '')) memberTable.Add(qq=qq, name=(card or nick), nick=nick, card=card) memberTable.lastUpdateTime = time.time() return memberTable
def onStartupComplete(bot): # 启动工作全部完成时被调用(此时已登录成功,且已开始监听消息和 qterm 客户端命令) # bot : QQBot 对象 # 以下为示例: DEBUG('onStartupComplete: QQbot started.') pass
def login(self, username, password): loginApi = 'https://passport.weibo.cn/sso/login' loginPostData = { 'username': username, 'password': password, 'savestate': 1, 'r': '', 'ec': '0', 'pagerefer': '', 'entry': 'mweibo', 'wentry': '', 'loginfrom': '', 'client_id': '', 'code': '', 'qq': '', 'mainpageflag': 1, 'hff': '', 'hfp': '' } res = self.session.post(loginApi, data=loginPostData, headers=self.reqHeaders) if res.status_code == 200 and json.loads( res.text)['retcode'] == 20000000: DEBUG('Weibo login successful! UserId:' + json.loads(res.text)['data']['uid'])
def update_weibo_conf(bot): global weibo_monitor DEBUG('读取微博配置') global_config.MEMBER_WEIBO_GROUPS = ConfigReader.get_property( 'qq_conf', 'member_weibo_groups').split(';') member_name = ConfigReader.get_property('root', 'member_name') if global_config.MEMBER_NAME == '' or global_config.MEMBER_NAME != member_name: DEBUG('微博监控变更') global_config.MEMBER_NAME = member_name uid = ConfigReader.get_property('weibo', member_name) if uid != '': weibo_monitor.getWBQueue(uid) else: INFO('没有微博UID')
def close(self): if self.handler is None: return self.handler = None self.sock.close() DEBUG('%s disconnected', self.name)
def ObjOfList(self, ctype, info1=None, info2=None): if ctype in ('buddy', 'group', 'discuss'): return self.objOfList(ctype, cinfo=info1) elif ctype in ('group-member', 'discuss-member'): assert info1 oinfo, cinfo = info1, info2 cl = self.List(ctype[:-7], oinfo) if cl is None: return None, '错误:无法向 QQ 服务器获取联系人资料' elif not cl: return None, '错误:%s(%s)不存在' % (CTYPES[ctype[:-7]], oinfo) else: result = [] for owner in cl: r = self.objOfList(owner, cinfo) result.append({ 'owner': owner.__dict__, 'membs': { 'r': r[0], 'e': r[1] } }) return result, None else: DEBUG(ctype) assert False
def get_ticket_info(bot): ''' 每周2下午1点获取票务信息 ''' global pocket48_handler global_config.TICKET_INFO = pocket48_handler.get_ticket_info() DEBUG('获取票务信息')
def onInterval(bot): # 每隔 5 分钟被调用 # bot : QQBot 对象 # 以下为示例: DEBUG('onInterval: 5min interval reached.') pass
def onExit(bot, code, reason, error): # MainLoop(主循环)终止时被调用, Mainloop 是一个无限循环,QQBot 登录成功后便开始运 # 行,当且仅当以下事件发生时 Mainloop 终止: # 1) 调用了 bot.Stop() ,此时: # code = 0, reason = 'stop', error = None # 2) 调用了 bot.Restart() ,此时: # code = 201, reason = 'restart', error = None # 3) 调用了 bot.FreshRestart() ,此时: # code = 202, reason = 'fresh-restart', error = None # 4) 调用了 sys.exit(x) ( x 不等于 0,201,202,203 ),此时: # code = x, reason = 'system-exit', error = None # 5) 登录的 cookie 已过期,此时: # code = 203, reason = 'login-expire', error = None # 6) 发生未知错误 e (暂未出现过,出现则表明 qqbot 程序内部可能存在错误),此时: # code = 1, reason = 'unknown-error', error = e # # 一般情况下: # 发生 1/2/3/4 时,可以安全的调用 bot.List/SendTo/GroupXXX 等接口 # 发生 5/6 时,调用 bot.List/SendTo/GroupXXX 等接口将出错 # # 一般情况下,用户插件内的代码和运行错误会被捕捉并忽略,不会引起 MainLoop 的退出 # # 本函数被调用后,会执行 sys.exit(code) 退出本次进程并返回到父进程,父进程会根据 # “ code 的数值” 以及 “是否配置为自动重启模式” 来决定是否重启 QQBot 。 # DEBUG('%s.onExit: %r %r %r', __name__, code, reason, error)
def QLogin(qq=None, user=None): conf = QConf(qq, user) conf.Display() if conf.qq: INFO('开始自动登录...') picklePath = conf.PicklePath() session = QSession() contactdb = QContactDB(session) try: contactdb.Restore(picklePath) except Exception as e: WARN('自动登录失败,原因:%s', e) else: INFO('成功从文件 "%s" 中恢复登录信息' % picklePath) try: contactdb.session.TestLogin() except RequestError: WARN('自动登录失败,原因:上次保存的登录信息已过期') except Exception as e: WARN('自动登录失败,原因:%s', e) DEBUG('', exc_info=True) else: return contactdb.session.Copy(), contactdb, conf INFO('开始手动登录...') session = QSession() session.Login(conf) contactdb = QContactDB(session, conf.PicklePath()) contactdb.Dump() return session.Copy(), contactdb, conf
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: DEBUG('', 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 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 onQrcode(bot, pngPath, pngContent): # 获取到二维码时被调用 # 注意 : 此时 bot 尚未启动,因此请勿在本函数中调用 bot.List/SendTo/GroupXXX/Stop/Restart 等接口 # 只可以访问配置信息 bot.conf # bot : QQBot 对象 # pngPath : 二维码图片路径 # pngContent : 二维码图片内容 DEBUG('%s.onQrcode: %s (%d bytes)', __name__, pngPath, len(pngContent))
def onNewContact(bot, contact, owner): # 当新增 好友/群/讨论组/群成员/讨论组成员 时被调用 # bot : QQBot 对象 # contact : QContact 对象,代表新增的联系人 # owner : QContact 对象,仅在新增 群成员/讨论组成员 时有效,代表新增成员所在的 群/讨论组 # 以下为示例: if contact.ctype != 'buddy': # ctype: 'buddy', 'group-member', 'discuss-member' DEBUG("onNewContact: ctype=" + contact.ctype + " owner=(qq=" + owner.qq + ", uin=" + owner.uin + ", name=" + owner.name + ') uin=' + contact.uin + ' qq=' + contact.qq + ' name=' + contact.name) else: DEBUG("onNewContact: ctype=" + contact.ctype + 'uin=' + contact.uin + ' qq=' + contact.qq + ' name=' + contact.name) pass
def get_current_and_target(self, wds): """ 获取当前进度和总额 :return: """ header = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Encoding': 'gzip,deflate,sdch,br', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'wds.modian.com', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.3408.400 QQBrowser/9.6.12028.400' } r = requests.get(wds.link, headers=header) soup = BeautifulSoup(r.text, 'lxml') # print soup.prettify() DEBUG('集资项目: %s', wds.title) DEBUG('集资链接: %s', wds.link) project_info = soup.find_all(class_="project-info")[0] support_num = project_info.find_all(class_="b")[0].find_all( name="span")[0].contents[0].strip() DEBUG('当前集资人数: %s', support_num) wds.support_num = int(support_num.replace(',', '')) current = project_info.find_all(class_="current")[0].find_all( name="span")[1].contents[1].strip() DEBUG('当前进度: %s元', current) wds.current = float(current.replace(',', '')) target = project_info.find_all(class_="target")[0].find_all( name="span")[1].contents[1].strip() DEBUG('目标金额: %s元', target) wds.target = float(current.replace(',', '')) return support_num, current, target
def onQQMessage(bot, contact, member, content): # 记录这个群的消息到日志文件 logger.history("qq", contact.name, member.name, content) #当本 QQ 发消息时, QQBot 也会收到一条同样的消息, bot 对象提供一个 isMe 方法来判断是否是自己发的消息 if bot.isMe(contact, member): DEBUG('是我自己发的消息') return #自己发的没必要记录日志,但是需要保存到聊天历史当中 DEBUG('收到消息[message]:%s', content) DEBUG('发消息者[contact]:%s', contact) DEBUG('发送成员[member ]:%s', member) #被群内其他成员 @ 的通知 if '@ME' in content: logger.debug("是发给机器人的消息,也就是别人@机器人了") #bot.SendTo(contact, member.name + ',艾特我干嘛呢?') else: #logger.debug("@ME不在消息中,此消息不是@机器人的,忽略掉") return #消息都是 "@xxx yyyyyy",要把@xxx去掉,否则,无法做意图识别 content = __remove_at(content) logger.debug('去除@后的消息:[message]:%s', content) for group in bot.groups: logger.debug("检查bot上注册的群/讨论组:group[%s],contact.name[%s]", group, contact.name) if contact.name == group: logger.debug('是这个群组[%s]的消息,我立即消息路由', group) # 得到我们的业务处理组件 | route(self, client, user, group, msg): biz_comp, context = bot.bizManager.route("qq", member.name, group, content) if biz_comp is None: logger.error("无法找到对应的业务处理器![QQ],user[%s],group[%s]", member.name, group) return "不理解您的回复,请再对我说点啥" logger.debug("成功加载业务处理器[%r]", biz_comp) # 调用业务组件的接口方法来处理消息 returnMsg = biz_comp.bot2system(bot.qbot, "qq", context, member.name, group, content)
def GetCTypeAndOwner(tinfo): ctype, owner = tinfo, tinfo if ctype in ('buddy', 'group', 'discuss'): return ctype, None elif isinstance(owner, QContact) and owner.ctype in ('group', 'discuss'): return owner.ctype + '-member', owner else: DEBUG(tinfo) assert False
def init_msg_queue(self, limit=10): weibo_top_cnt = 0 for weibo_id in self.weibo_ids: is_top = 0 res = self.get_member_weibo(weibo_id) res = filter(lambda x: x["card_type"] == 9, res.json()["cards"]) for i in res: id = i["mblog"]["id"] self.weibo_msg_queue[weibo_id].append(id) if "isTop" in i["mblog"].keys(): is_top = 1 self.weibo_top_id[weibo_id] = id weibo_top_cnt += is_top time.sleep(0.01) DEBUG("init weibo_id:%s success! has weibo:%s \nids:%s" \ %(weibo_id, len(res), ' '.join(self.weibo_msg_queue[weibo_id]))) DEBUG("Init weibo msg queue success.") DEBUG("%s/%s 有置顶微博." % (weibo_top_cnt, len(self.weibo_ids)))
def update_conf(bot): """ 每隔1分钟读取配置文件 :param bot: :return: """ global pocket48_handler # 初始化人数统计 for group_number in global_config.MEMBER_ROOM_MSG_LITE_GROUPS: if group_number not in global_config.GROUP_MEMBER_NUM.keys(): global_config.GROUP_MEMBER_NUM[group_number] = 0 DEBUG('member_room_msg_groups: %s, length: %d', ','.join(global_config.MEMBER_ROOM_MSG_GROUPS), len(pocket48_handler.member_room_msg_groups)) DEBUG('member_room_comment_groups: %s, length: %d', ','.join(global_config.MEMBER_ROOM_COMMENT_GROUPS), len(pocket48_handler.member_room_comment_msg_groups)) DEBUG('auto_reply_groups: %s, length: %d', ','.join(global_config.AUTO_REPLY_GROUPS), len(pocket48_handler.auto_reply_groups)) DEBUG('member_live_groups: %s, length: %d', ','.join(global_config.MEMBER_LIVE_GROUPS), len(pocket48_handler.member_live_groups)) DEBUG('member_room_comment_lite_groups: %s, length: %d', ','.join(global_config.MEMBER_ROOM_MSG_LITE_GROUPS), len(pocket48_handler.member_room_msg_lite_groups))