Exemple #1
0
def main():
    try:
        conf = QQBotConf(userName=None, version=QQBotVersion)
        if not conf.restartOnOffline or '--start-a-circle' in sys.argv:
            bot = QQBot(conf=conf)
            bot.Login()
            bot.Run()
        else:
            args = ['python', __file__] + \
                   sys.argv[1:] + \
                   ['--start-a-circle'] + \
                   ['--mail-auth-code', conf.mailAuthCode]
            while subprocess.call(args) != 0:
                INFO('重新启动 QQBot ')
    except KeyboardInterrupt:
        sys.exit(0)
Exemple #2
0
def main():
    try:
        if sys.argv[-1] == '--subprocessCall':
            isSubprocessCall = True
            sys.argv.pop()
        else:
            isSubprocessCall = False

        conf = QQBotConf(version=QQBotVersion)
        if not conf.restartOnOffline or isSubprocessCall:
            bot = QQBot(conf=conf)
            bot.Login()
            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)
Exemple #3
0
 def __init__(self, qq=None, user=None, conf=None):
     INFO('QQBot-%s', QQBotVersion)
     self.conf = conf or QQBotConf(qq, user, QQBotVersion)
     self.conf.Display()
     self.qrcodeManager = QrcodeManager(self.conf)
     self.nonDumpAttrs = self.__dict__.keys()
Exemple #4
0
        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, email_id
        

if __name__ == '__main__':
    import time
    from qqbotconf import QQBotConf
    conf = QQBotConf(user='******', version='v1.9.6')
    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.getSubject(-1)[0]
        print 'latest unseen email:', subject
    print 'recv ok'
    
    time.sleep(5)
    
    with ma.IMAP() as i:
        i.delMail(subject)
Exemple #5
0

# FILENAME must be an utf8 encoding string
def showImage(filename):
    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 qqbotconf import QQBotConf
    import time
    # 需要先在 ~/.qqbot-tmp/qqbot.conf 文件中设置好邮箱帐号和授权码
    conf = QQBotConf()
    qrm = QrcodeManager(conf)
    with open('tmp.png', 'rb') as f:
        qrcode = f.read()
    qrm.Show(qrcode)
    time.sleep(5)
    qrm.Show(qrcode)
    qrm.Destroy()
Exemple #6
0
 def __init__(self, userName=None):
     INFO('QQBot-%s', QQBotVersion)
     self.conf = QQBotConf(userName, QQBotVersion[:4])
     self.qrcodeManager = QrcodeManager(self.conf)
     self.nonDumpAttrs = self.__dict__.keys()
Exemple #7
0
class QQBot:
    def __init__(self, userName=None):
        INFO('QQBot-%s', QQBotVersion)
        self.conf = QQBotConf(userName, QQBotVersion[:4])
        self.qrcodeManager = QrcodeManager(self.conf)
        self.nonDumpAttrs = self.__dict__.keys()

    def InfiniteLoop(self):
        if self.conf.restartOnOffline:
            try:
                while True:
                    p = multiprocessing.Process(target=loop1)
                    p.start()
                    p.join()
                    if p.exitcode == 0:
                        break
            except KeyboardInterrupt:
                sys.exit(0)
            finally:
                self.qrcodeManager.Destroy()
        else:
            self.LoginAndRun()

    def LoginAndRun(self):
        try:
            self.Login()
            self.Run()
        except KeyboardInterrupt:
            sys.exit(0)
        finally:
            self.qrcodeManager.Destroy()

    def Login(self):
        INFO('开始登录...')
        if not self.conf.QQ or not self.autoLogin():
            self.manualLogin()
        INFO('登录成功。登录账号:%s (%d)', self.nick, self.qqNum)

    def manualLogin(self):
        try:
            self.prepareSession()
            self.waitForAuth()
            self.getPtwebqq()
            self.getVfwebqq()
            self.getUinAndPsessionid()
            self.testLogin()
            self.fetchBuddies()
            self.fetchGroups()
            self.fetchDiscusses()
            self.dumpSessionInfo()
        except RequestError:
            CRITICAL('手动登录失败!')
            sys.exit(1)

    def autoLogin(self):
        try:
            self.loadSessionInfo()
            self.testLogin()
            return True
        except RequestError:
            e = '上次保存的 Session info 已过期'
        except Exception as e:
            e = str(e)

        WARN('自动登录失败,原因:%s', e)
        return False

    def dumpSessionInfo(self):
        self.pollSession = pickle.loads(pickle.dumps(self.session))
        picklePath = self.conf.PicklePath()
        nonDump = CutDict(self.__dict__, self.nonDumpAttrs)
        try:
            with open(picklePath, 'wb') as f:
                f.write(pickle.dumps(self.__dict__))
        except IOError:
            WARN('保存 Session info 失败:IOError %s', picklePath)
        else:
            INFO('Session info 已保存至文件:file://%s' % picklePath)
        self.__dict__.update(nonDump)

    def loadSessionInfo(self):
        picklePath = self.conf.PicklePath()
        with open(picklePath, 'rb') as f:
            self.__dict__.update(pickle.load(f))
        INFO('成功从文件 file://%s 中恢复 Session info' % picklePath)

    def prepareSession(self):
        self.clientid = 53999199
        self.msgId = 6000000
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9;'
                           ' rv:27.0) Gecko/20100101 Firefox/27.0'),
            'Content-Type':
            'application/x-www-form-urlencoded; charset=UTF-8'
        })
        self.urlGet(
            'https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&'
            'style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&'
            'no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&'
            'f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001'
        )
        self.session.cookies.update({
            'RK':
            'OfeLBai4FB',
            'pgv_pvi':
            '911366144',
            'pgv_info':
            'ssid pgv_pvid=1051433466',
            'ptcz': ('ad3bf14f9da2738e09e498bfeb93dd9da7'
                     '540dea2b7a71acfb97ed4d3da4e277'),
            'qrsig': ('hJ9GvNx*oIvLjP5I5dQ19KPa3zwxNI'
                      '62eALLO*g2JLbKPYsZIRsnbJIxNe74NzQQ')
        })
        self.getAuthStatus()
        self.session.cookies.pop('qrsig')

    def waitForAuth(self):
        try:
            self.qrcodeManager.Show(self.getQrcode())
            while True:
                time.sleep(3)
                authStatus = self.getAuthStatus()
                if '二维码未失效' in authStatus:
                    INFO('登录 Step2 - 等待二维码扫描及授权')
                elif '二维码认证中' in authStatus:
                    INFO('二维码已扫描,等待授权')
                elif '二维码已失效' in authStatus:
                    WARN('二维码已失效, 重新获取二维码')
                    self.qrcodeManager.Show(self.getQrcode())
                elif '登录成功' in authStatus:
                    INFO('已获授权')
                    items = authStatus.split(',')
                    self.nick = items[-1].split("'")[1]
                    self.qqNum = int(self.session.cookies['superuin'][1:])
                    self.conf.QQ = str(self.qqNum)
                    self.urlPtwebqq = items[2].strip().strip("'")
                    break
                else:
                    CRITICAL('获取二维码扫描状态时出错, html="%s"', authStatus)
                    sys.exit(1)
        finally:
            self.qrcodeManager.DelPng()

    def getQrcode(self, firstTime=True):
        INFO('登录 Step1 - 获取二维码')
        return self.urlGet(
            'https://ssl.ptlogin2.qq.com/ptqrshow?appid=501004106&e=0&l=M&' +
            's=5&d=72&v=4&t=' + repr(random.random()))

    def getAuthStatus(self):
        return self.urlGet(
            url='https://ssl.ptlogin2.qq.com/ptqrlogin?webqq_type=10&' +
            'remember_uin=1&login2qq=1&aid=501004106&u1=http%3A%2F%2F' +
            'w.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&' +
            'ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&' +
            'dumy=&fp=loginerroralert&action=0-0-' +
            repr(random.random() * 900000 + 1000000) +
            '&mibao_css=m_webqq&t=undefined&g=1&js_type=0' +
            '&js_ver=10141&login_sig=&pt_randsalt=0',
            Referer=('https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&'
                     'target=self&style=16&mibao_css=m_webqq&appid=501004106&'
                     'enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2F'
                     'w.qq.com%2Fproxy.html&f_url=loginerroralert&'
                     'strong_login=1&login_state=10&t=20131024001'))

    def getPtwebqq(self):
        INFO('登录 Step3 - 获取ptwebqq')
        self.urlGet(self.urlPtwebqq)
        self.ptwebqq = self.session.cookies['ptwebqq']

    def getVfwebqq(self):
        INFO('登录 Step4 - 获取vfwebqq')
        self.vfwebqq = self.smartRequest(
            url=('http://s.web2.qq.com/api/getvfwebqq?ptwebqq=%s&'
                 'clientid=%s&psessionid=&t=%s') %
            (self.ptwebqq, self.clientid, repr(random.random())),
            Referer=('http://s.web2.qq.com/proxy.html?v=20130916001'
                     '&callback=1&id=1'),
            Origin='http://s.web2.qq.com')['vfwebqq']

    def getUinAndPsessionid(self):
        INFO('登录 Step5 - 获取uin和psessionid')
        result = self.smartRequest(
            url='http://d1.web2.qq.com/channel/login2',
            data={
                'r':
                JsonDumps({
                    'ptwebqq': self.ptwebqq,
                    'clientid': self.clientid,
                    'psessionid': '',
                    'status': 'online'
                })
            },
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001'
                     '&callback=1&id=2'),
            Origin='http://d1.web2.qq.com')
        self.uin = result['uin']
        self.psessionid = result['psessionid']
        self.hash = qHash(self.uin, self.ptwebqq)

    def testLogin(self):
        try:
            DisableLog()
            # 请求一下 get_online_buddies 页面,避免103错误。
            # 若请求无错误发生,则表明登录成功
            self.smartRequest(
                url=('http://d1.web2.qq.com/channel/get_online_buddies2?'
                     'vfwebqq=%s&clientid=%d&psessionid=%s&t=%s') %
                (self.vfwebqq, self.clientid, self.psessionid,
                 repr(random.random())),
                Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                         'callback=1&id=2'),
                Origin='http://d1.web2.qq.com',
                repeatOnDeny=0)
        finally:
            EnableLog()

    def fetchBuddies(self):
        INFO('登录 Step6 - 获取好友列表')
        result = self.smartRequest(
            url='http://s.web2.qq.com/api/get_user_friends2',
            data={
                'r': JsonDumps({
                    'vfwebqq': self.vfwebqq,
                    'hash': self.hash
                })
            },
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))
        ss, self.buddies, self.buddiesDictU, self.buddiesDictQ = [], [], {}, {}
        for info in result.get('info', []):
            uin = info['uin']
            name = info['nick']
            qq = self.fetchBuddyQQ(uin)
            buddy = {'uin': uin, 'qq': qq, 'name': name}
            self.buddies.append(buddy)
            self.buddiesDictU[uin] = buddy
            self.buddiesDictQ[qq] = buddy
            s = '%d, %s, uin%d' % (qq, name, uin)
            INFO('好友: ' + s)
            ss.append(s)
        self.buddyStr = '好友列表:\n' + '\n'.join(ss)
        INFO('获取朋友列表成功,共 %d 个朋友' % len(self.buddies))

    def fetchBuddyQQ(self, uin):
        return self.smartRequest(
            url=('http://s.web2.qq.com/api/get_friend_uin2?tuin=%d&'
                 'type=1&vfwebqq=%s&t=0.1') % (uin, self.vfwebqq),
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))['account']

    def fetchBuddyDetailInfo(self, uin):
        return self.smartRequest(
            url = ('http://s.web2.qq.com/api/get_friend_info2?tuin=%s&'
                   'vfwebqq=%s&clientid=%s&psessionid=%s&t=%s') % \
                  (uin, self.vfwebqq, self.clientid,
                   self.psessionid, repr(random.random())),
            Referer = ('http://s.web2.qq.com/proxy.html?v=20130916001&'
                       'callback=1&id=1')
        )

    unexist = {
        'uin': -1,
        'qq': -1,
        'name': 'UNEXIST',
        'member': {},
        'memberStr': ''
    }

    def getBuddyByUin(self, uin):
        return self.buddiesDictU.get(uin, QQBot.unexist)


#        try:
#            return self.buddiesDictU[uin]
#        except KeyError:
#            try:
#                qq = self.fetchBuddyQQ(uin)
#            except KeyError:
#                return QQBot.unexist
#            else:
#                name = self.fetchBuddyDetailInfo(uin)['nick']
#                buddy = {'uin': uin, 'qq': qq, 'name': name}
#                self.buddiesDictU[uin] = buddy
#                self.buddiesDictQ[qq] = buddy

    def getBuddyByQQ(self, qq):
        return self.buddiesDictQ.get(qq, QQBot.unexist)

    def fetchGroups(self):
        INFO('登录 Step7 - 获取群列表')
        INFO('=' * 60)
        result = self.smartRequest(
            url='http://s.web2.qq.com/api/get_group_name_list_mask2',
            data={
                'r': JsonDumps({
                    'vfwebqq': self.vfwebqq,
                    'hash': self.hash
                })
            },
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))

        ss, self.groups, self.groupsDictU, self.groupsDictQ = [], [], {}, {}
        for info in result.get('gnamelist', []):
            uin = info['gid']
            name = info['name']
            qq = self.fetchGroupQQ(uin)
            member = self.fetchGroupMember(info['code'])
            group = {'uin': uin, 'qq': qq, 'name': name, 'member': member}

            self.groups.append(group)
            self.groupsDictU[uin] = group
            self.groupsDictQ[qq % 1000000] = group

            s = '%d, %s, uin%d' % (qq, name, uin)
            ss.append(s)
            INFO('群: ' + s)

            mss = []
            for uin, name in member.items():
                ms = '%s, uin%d' % (name, uin)
                INFO('    成员: %s', ms)
                mss.append(ms)
            INFO('=' * 60)

            group['memberStr'] = '群 %s 的成员列表:\n%s' % (name, '\n'.join(mss))

        self.groupStr = '群列表:\n' + '\n'.join(ss)

        INFO('获取群列表成功,共 %d 个朋友' % len(self.groups))

    def fetchGroupQQ(self, uin):
        return self.smartRequest(
            url=('http://s.web2.qq.com/api/get_friend_uin2?tuin=%d&'
                 'type=4&vfwebqq=%s&t=0.1') % (uin, self.vfwebqq),
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))['account']

    def fetchGroupMember(self, gcode):
        ret = self.smartRequest(
            url=('http://s.web2.qq.com/api/get_group_info_ext2?gcode=%d'
                 '&vfwebqq=%s&t=0.1') % (gcode, self.vfwebqq),
            Referer=('http://s.web2.qq.com/proxy.html?v=20130916001'
                     '&callback=1&id=1'))
        minfos = ret['minfo']
        members = ret['ginfo']['members']
        return dict(
            (m['muin'], inf['nick']) for m, inf in zip(members, minfos))
        # groupMembers = {}
        # for member, minfo in zip(members, minfos):
        #     uin = member['muin']
        #     name = minfo['nick']
        #     qq = self.fetchBuddyQQ(uin)
        #     buddy = {'uin': uin, 'qq': qq, 'name': name}
        #     INFO('群好友 -- %d, %s, uin%d' % (qq, name, uin))
        #     groupMembers['uin'] = buddy
        # return groupMembers

    def getGroupByUin(self, uin):
        return self.groupsDictU.get(uin, QQBot.unexist)

    def getGroupByQQ(self, qq):
        return self.groupsDictQ.get(qq % 1000000, QQBot.unexist)

    def fetchDiscusses(self):
        INFO('登录 Step8 - 获取讨论组列表')
        INFO('=' * 60)
        result = self.smartRequest(
            url=('http://s.web2.qq.com/api/get_discus_list?clientid=%s&'
                 'psessionid=%s&vfwebqq=%s&t=%s') %
            (self.clientid, self.psessionid, self.vfwebqq, repr(
                random.random())),
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001'
                     '&callback=1&id=2'))
        ss, self.discusses, self.discussesDict = [], [], {}
        for info in result.get('dnamelist', []):
            uin = info['did']
            name = info['name']
            member = self.fetchDiscussMember(uin)
            discuss = {'uin': uin, 'name': name, 'member': member}
            self.discusses.append(discuss)
            self.discussesDict[uin] = discuss
            s = '%s, uin%d' % (name, uin)
            INFO('讨论组: ' + s)
            ss.append(s)

            mss = []
            for uin, name in member.items():
                ms = '%s, uin%d' % (name, uin)
                INFO('    成员: %s', ms)
                mss.append(ms)
            INFO('=' * 60)

            discuss['memberStr'] = '讨论组的 %s 的成员列表:\n%s' % \
                                   (name, '\n'.join(mss))

        self.discussStr = '讨论组列表:\n' + '\n'.join(ss)
        INFO('获取讨论组列表成功,共 %d 个讨论组' % len(self.discusses))

    def fetchDiscussMember(self, uin):
        ret = self.smartRequest(
            url=('http://d1.web2.qq.com/channel/get_discu_info?'
                 'did=%s&psessionid=%s&vfwebqq=%s&clientid=%s&t=0.1') %
            (uin, self.psessionid, self.vfwebqq, self.clientid),
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001'
                     '&callback=1&id=2'))
        return dict((m['uin'], m['nick']) for m in ret['mem_info'])

    def getDiscussByUin(self, uin):
        return self.discussesDict.get(uin, QQBot.unexist)

    def refetch(self):
        self.fetchBuddies()
        self.fetchGroups()
        self.fetchDiscusses()
        self.nick = self.fetchBuddyDetailInfo(self.uin)['nick']

    def poll(self):
        result = self.smartRequest(
            url='https://d1.web2.qq.com/channel/poll2',
            data={
                'r':
                JsonDumps({
                    'ptwebqq': self.ptwebqq,
                    'clientid': self.clientid,
                    'psessionid': self.psessionid,
                    'key': ''
                })
            },
            sessionObj=self.pollSession,
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))
        if 'errmsg' in result:
            return ('', 0, 0, '')  # 无消息
        else:
            result = result[0]

            msgType = {
                'message': 'buddy',
                'group_message': 'group',
                'discu_message': 'discuss'
            }[result['poll_type']]
            from_uin = result['value']['from_uin']
            buddy_uin = result['value'].get('send_uin', from_uin)
            msg = ''.join(
                ('[face%d]' % m[1]) if isinstance(m, list) else str(m)
                for m in result['value']['content'][1:])

            if msgType == 'buddy':
                bName = self.getBuddyByUin(buddy_uin)['name']
                INFO('来自 好友“%s” 的消息: "%s"' % (bName, msg))
            elif msgType == 'group':
                group = self.getGroupByUin(from_uin)
                gName = group['name']
                bName = group['member'].get(buddy_uin, 'unknown')
                INFO('来自 群“%s”[成员“%s”] 的消息: "%s"' % (gName, bName, msg))
            else:
                discuss = self.getDiscussByUin(from_uin)
                gName = discuss['name']
                bName = discuss['member'].get(buddy_uin, 'unknown')
                INFO('来自 讨论组“%s”[成员“%s”] 的消息: "%s"' % (gName, bName, msg))

            return (msgType, from_uin, buddy_uin, msg)

    def send(self, msgType, to_uin, msg):
        while msg:
            front, msg = Utf8Partition(msg, 600)
            self._send(msgType, to_uin, front)

    def _send(self, msgType, to_uin, msg):
        self.msgId += 1
        if self.msgId % 10 == 0:
            INFO('已连续发送10条消息,强制 sleep 10秒,请等待...')
            time.sleep(10)
        else:
            time.sleep(random.randint(3, 5))
        sendUrl = {
            'buddy': 'http://d1.web2.qq.com/channel/send_buddy_msg2',
            'group': 'http://d1.web2.qq.com/channel/send_qun_msg2',
            'discuss': 'http://d1.web2.qq.com/channel/send_discu_msg2'
        }
        sendTag = {'buddy': 'to', 'group': 'group_uin', 'discuss': 'did'}
        self.smartRequest(
            url=sendUrl[msgType],
            data={
                'r':
                JsonDumps({
                    sendTag[msgType]:
                    to_uin,
                    'content':
                    JsonDumps([
                        msg,
                        [
                            'font', {
                                'name': '宋体',
                                'size': 10,
                                'style': [0, 0, 0],
                                'color': '000000'
                            }
                        ]
                    ]),
                    'face':
                    522,
                    'clientid':
                    self.clientid,
                    'msg_id':
                    self.msgId,
                    'psessionid':
                    self.psessionid
                })
            },
            Referer=('http://d1.web2.qq.com/proxy.html?v=20151105001&'
                     'callback=1&id=2'))

        if msgType == 'buddy':
            INFO('向 好友“%s” 发消息成功', self.getBuddyByUin(to_uin)['name'])
        elif msgType == 'group':
            INFO('向 群“%s” 发消息成功', self.getGroupByUin(to_uin)['name'])
        else:
            INFO('向 讨论组“%s” 发消息成功', self.getDiscussByUin(to_uin)['name'])

    def urlGet(self, url, **kw):
        time.sleep(0.2)
        self.session.headers.update(kw)
        try:
            return self.session.get(url).content
        except (requests.exceptions.SSLError, AttributeError):
            return self.session.get(url, verify=False).content

    def smartRequest(self,
                     url,
                     data=None,
                     repeatOnDeny=2,
                     sessionObj=None,
                     **kw):
        session = sessionObj or self.session
        i, j = 0, 0
        while True:
            html, timeOut = '', False
            session.headers.update(**kw)
            try:
                if data is None:
                    resp = session.get(url)
                else:
                    resp = session.post(url, data=data)

                if url == 'https://d1.web2.qq.com/channel/poll2':
                    # DEBUG('POLL RESULT: status_code=%s, html=%s',
                    #        resp.status_code, resp.content)

                    if resp.status_code in (502, 504):
                        timeOut = True
                    else:
                        html = resp.content
                        try:
                            result = JsonLoads(html)
                        except ValueError:
                            timeOut = True

                    if timeOut:
                        session.get(
                            ('http://pinghot.qq.com/pingd?dm=w.qq.com.hot&'
                             'url=/&hottag=smartqq.im.polltimeout&hotx=9999&'
                             'hoty=9999&rand=%s') %
                            random.randint(10000, 99999))
                        continue
                else:
                    html = resp.content
                    result = JsonLoads(html)

            except (requests.ConnectionError, ValueError):
                i += 1
                errorInfo = '网络错误或url地址错误'
            else:
                retcode = result.get('retcode', result.get('errCode', -1))
                if retcode in (0, 1202, 100003):
                    return result.get('result', result)
                else:
                    j += 1
                    errorInfo = '请求被拒绝错误'

            # 出现网络错误可以多试几次 (i <= 4);
            # 若 retcode 有误,一般连续 3 次都出错就没必要再试了 (j <= 2)
            if i <= 4 and j <= repeatOnDeny:
                DEBUG('第%d次请求“%s”时出现“%s”, html:\n%s', i + j, url, errorInfo,
                      html)
            else:
                CRITICAL('第%d次请求“%s”时出现“%s”,终止 QQBot', i + j, url, errorInfo)
                raise RequestError

    # class attribute `helpInfo` will be printed at the start of `Run` method
    helpInfo = '帮助命令:"-help"'

    def Run(self):
        self.msgQueue = Queue.Queue()
        self.pullThread = threading.Thread(target=self.pullForever)
        self.pullThread.daemon = True
        self.pullThread.start()

        INFO('QQBot已启动,请用其他QQ号码向本QQ %s<%d> 发送命令来操作QQBot。%s' % \
             (self.nick,self.qqNum,self.__class__.__dict__.get('helpInfo','')))

        while True:
            try:
                pullResult = self.msgQueue.get(timeout=1)
            except Queue.Empty:
                continue
            else:
                if pullResult is None:
                    sys.exit(1)
                self.onPollComplete(*pullResult)

    def pullForever(self):
        try:
            while True:
                pullResult = self.poll()
                self.msgQueue.put(pullResult)
        finally:
            self.msgQueue.put(None)

    # override this method to build your own QQ-bot.
    def onPollComplete(self, msgType, from_uin, buddy_uin, message):
        argv = message.strip().split()
        argc = len(argv)

        if not argv:
            return

        elif argv[0] == '-help' and argc == 1:
            reply = ('欢迎使用QQBot,使用方法:\n'
                     '    -help\n'
                     '    -list buddy|group|discuss\n'
                     '    -send buddy|group|discuss {qq_or_uin} message\n'
                     '    -member group|discuss {qq_or_uin}\n'
                     '    -refetch\n'
                     '    -stop\n')

        elif argv[0] == '-list' and argc == 2:
            reply = getattr(self, argv[1] + 'Str', '')

        elif argv[0] == '-send' and argc >= 4 and argv[2].isdigit() \
                                and argv[1] in ('buddy', 'group', 'discuss'):
            qq = int(argv[2])
            if argv[1] == 'buddy':
                uin = self.getBuddyByQQ(qq)['uin']
            elif argv[1] == 'group':
                uin = self.getGroupByQQ(qq)['uin']
            else:
                uin = self.getDiscussByUin(qq)['uin']

            if uin == -1:
                reply = ('请检查接收者账号是否有误,若是新加的 好友/群/讨论组 ,' '请先运行 refetch ')
            else:
                self.send(argv[1], uin, ' '.join(argv[3:]))
                reply = '消息发送成功'

        elif argv[0] == '-member' and argc == 3 and argv[2].isdigit():
            if argv[1] == 'group':
                reply = self.getGroupByQQ(int(argv[2]))['memberStr']
            elif argv[1] == 'discuss':
                reply = self.getDiscussByUin(int(argv[2]))['memberStr']
            else:
                return
            if not reply:
                reply = ('请检查 群/讨论组 账号是否有误,若是新加的 群/讨论组 ,' '请先运行 refetch ')

        elif argv[0] == '-refetch' and argc == 1:
            self.refetch()
            reply = '重新获取 好友/群/讨论组 成功'

        elif argv[0] == '-stop' and argc == 1:
            INFO('收到 stop 命令,QQBot 即将停止')
            self.send(msgType, from_uin, 'QQBot已关闭')
            INFO('QQBot 正常停止')
            sys.exit(0)

        else:
            return

        self.send(msgType, from_uin, reply)

    def stop(self):
        sys.exit(0)