def waitForAuth(self, conf): qrcodeManager = QrcodeManager(conf) try: 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('二维码已失效, 重新获取二维码') qrcodeManager.Show(self.getQrcode()) elif '登录成功' in authStatus: INFO('已获授权') items = authStatus.split(',') self.nick = str(items[-1].split("'")[1]) self.qq = str(int(self.session.cookies['superuin'][1:])) self.urlPtwebqq = items[2].strip().strip("'") break else: CRITICAL('获取二维码扫描状态时出错, html="%s"', authStatus) sys.exit(1) finally: qrcodeManager.Destroy()
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
def smartRequest(self, url, data=None, timeoutRetVal=None, repeateOnDeny=2, **kw): nCE, nTO, nUE, nDE = 0, 0, 0, 0 while True: url = url.format(rand=repr(random.random())) html = '' errorInfo = '' self.session.headers.update(**kw) try: if data is None: resp = self.session.get(url, verify=self.httpsVerify) else: resp = self.session.post(url, data=data, verify=self.httpsVerify) except requests.ConnectionError as e: nCE += 1 errorInfo = '网络错误 %s' % e else: html = resp.content if resp.status_code in (502, 504, 404): self.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) ) if url == 'https://d1.web2.qq.com/channel/poll2': return {'errmsg': ''} nTO += 1 errorInfo = '超时' else: try: result = JsonLoads(html) except ValueError: nUE += 1 errorInfo = ' URL 地址错误' else: retcode = result.get('retcode', result.get('errCode', result.get('ec', -1))) if retcode in (0, 1202, 100003, 100100): return result.get('result', result) else: nDE += 1 errorInfo = '请求被拒绝错误' # 出现网络错误、超时、 URL 地址错误可以多试几次 # 若网络没有问题但 retcode 有误,一般连续 3 次都出错就没必要再试了 if nCE < 5 and nTO < 20 and nUE < 5 and nDE <= repeateOnDeny: DEBUG('第%d次请求“%s”时出现“%s”, html=%s', nCE+nTO+nUE+nDE, url, errorInfo, repr(html)) time.sleep(0.5) elif nTO == 20 and timeoutRetVal: # by @killerhack return timeoutRetVal else: CRITICAL('第%d次请求“%s”时出现“%s”', nCE+nTO+nUE+nDE, url, errorInfo) raise QSession.Error
def readConf(cls): conf = ast.literal_eval(defaultConfStr) cls.userDefInfo = conf['userInfo']['DEFAULT'] confPath = cls.ConfPath() if os.path.exists(confPath): try: with open(confPath) as f: cusConf = ast.literal_eval(f.read()) if type(cusConf) is not dict: raise ValueError('Must be a dict') for k, v in conf.items(): if k in cusConf: if type(v) is not type(cusConf[k]): raise ValueError('key: %s' % k) conf[k] = cusConf[k] if type(conf['userInfo']) is not dict: raise ValueError('key: userInfo') for k, v in conf['userInfo'].items(): if type(k) is not str or type(v) is not dict: raise ValueError('key: userInfo.%s' % k) except IOError: CRITICAL('读取配置文件出现 IOError') sys.exit(1) except (SyntaxError, ValueError) as e: CRITICAL('配置文件语法或格式错误,%s', e) sys.exit(1) else: try: with open(confPath, 'w') as f: f.write(defaultConfStr) except IOError: pass cls.__dict__.update(conf)
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 __init__(self, name, port, tmpDir, runInBackgroud=True): assert type(port) is int or (type(port) is str and port.isdigit()) assert os.path.isdir(tmpDir) self.name, self.port, self.tmpDir = name, int(port), tmpDir self._indexHTML = '<html><body>QQBOT-HTTP-SERVER</body></html>' self._indexURL = 'http://localhost:%s/qqbot' % port if not self.isRunning(): if runInBackgroud: args = 'python', __file__, str(port), tmpDir if CallInNewConsole(args): CRITICAL('无法运行命令"%s",二维码 HTTP 服务器启动失败' % ' '.join(args)) sys.exit(0) else: time.sleep(1.0) INFO('已在后台开启二维码 HTTP 服务器') else: print >>sys.stderr, ' * QQBot\'s QRCODE HTTP server' self.run() else: INFO('二维码 HTTP 服务器正在后台运行')
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 fetchGroups(self, contacts, silence=True): if not silence: INFO('登录 Step7 - 获取群列表') INFO('=' * 60) for i in range(5): 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')) if 'gmarklist' in result: break else: ERROR('获取群列表出错,等待 3 秒后再次尝试一次') time.sleep(3) else: CRITICAL('无法获取到群列表') raise QSession.Error markDict = dict((d['uin'], d['markname']) for d in result['gmarklist']) qqResult = self.smartRequest( url='http://qun.qq.com/cgi-bin/qun_mgr/get_group_list', data={'bkn': self.bkn}, Referer='http://qun.qq.com/member.html') qqDict = defaultdict(list) for k in ('create', 'manage', 'join'): for d in qqResult.get(k, []): name = d['gn'].replace(' ', ' ').replace('&', '&') qqDict[name].append(d['gc']) for info in result['gnamelist']: uin = info['gid'] name = info['name'] mark = markDict.get(uin, '') qqlist = qqDict.get(name, []) if len(qqlist) == 1: qq = qqlist.pop() else: qq = self.fetchGroupQQ(uin) for x in qqlist: if (qq - x) % 1000000 == 0: qq = x break try: qqlist.remove(qq) except ValueError: pass members = self.fetchGroupMember(info['code']) c = contacts.Add('group', str(uin), name, str(qq), '', mark, members) if not silence: INFO(repr(c)) for uin, name in members.items(): INFO(' 成员: %s, uin%s', name, uin) INFO('=' * 60) if not silence: INFO('获取群列表成功,共 %d 个群' % len(result))
def smartRequest(self, url, data=None, repeatOnDeny=2, sessionObj=None, **kw): session = sessionObj or self.session nCE, nTO, nUE, nDE = 0, 0, 0, 0 while True: url = url.format(rand=repr(random.random())) html, errorInfo = '', '' session.headers.update(**kw) try: if data is None: resp = session.get(url) else: resp = session.post(url, data=data) except requests.ConnectionError as e: nCE += 1 errorInfo = '网络错误 %s' % e else: html = resp.content if resp.status_code in (502, 504): 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)) if url == 'https://d1.web2.qq.com/channel/poll2': return {'errmsg': ''} nTO += 1 errorInfo = '超时' else: try: result = JsonLoads(html) except ValueError: nUE += 1 errorInfo = ' URL 地址错误' else: retcode = \ result.get('retcode', result.get('errCode', -1)) if retcode in (0, 1202, 100003): return result.get('result', result) else: nDE += 1 errorInfo = '请求被拒绝错误' # 出现网络错误或超时可以多试几次 (nCE < 5, nTO < 20); # 根据腾讯服务器的改标,增加了尝试次数 # 若出现 URL 地址错误或 retcode 有误,一般连续 3 次都出错就没必要再试了 if nCE < 5 and nTO < 20 and nUE < 3 and nDE <= repeatOnDeny: DEBUG('第%d次请求“%s”时出现“%s”, html=%s', nCE + nTO + nUE + nDE, url, errorInfo, repr(html)) else: if nTO == 20 and url.startswith( 'http://s.web2.qq.com/api/get_friend_uin2'): # 针对某个好友获取不到的情况,先返回一个空值,以确保成功登陆 # 防止因个别好友无法获得触发SystemExit,如果是因为其他原因则退出 return {'account': -1} CRITICAL('第%d次请求“%s”时出现“%s”,终止 QQBot', nCE + nTO + nUE + nDE, url, errorInfo) raise RequestError # (SystemExit)
def smartRequest(self, url, data=None, repeatOnDeny=2, sessionObj=None, **kw): session = sessionObj or self.session nCE, nUE, nDE = 0, 0, 0 while True: html, errorInfo = '', '' session.headers.update(**kw) try: if data is None: resp = session.get(url) else: resp = session.post(url, data=data) except requests.ConnectionError: nCE += 1 errorInfo = '网络错误' else: if url == 'https://d1.web2.qq.com/channel/poll2': timeOut = False 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)) return {'errmsg': ''} else: html = resp.content try: result = JsonLoads(html) except ValueError: nUE += 1 errorInfo = 'URL地址错误' if not errorInfo: retcode = result.get('retcode', result.get('errCode', -1)) if retcode in (0, 1202, 100003): return result.get('result', result) else: nDE += 1 errorInfo = '请求被拒绝错误' # 出现网络错误或URL地址错误可以多试几次 (nCE<=4 and nUE<=3); # 若 retcode 有误,一般连续 3 次都出错就没必要再试了(nDE<=2) if nCE <= 4 and nUE <= 3 and nDE <= repeatOnDeny: DEBUG('第%d次请求“%s”时出现“%s”, html=%s', nCE + nUE + nDE, url, errorInfo, repr(html)) else: CRITICAL('第%d次请求“%s”时出现“%s”,终止 QQBot', nCE + nUE + nDE, url, errorInfo) raise RequestError # (SystemExit)