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 Reply(self, rep): rep = STR2BYTES(str(rep) + '\r\n') try: self.sock.sendall(rep) except socket.error: WARN('回复 %s 失败', self.name) finally: self.sock.close()
def readConfFile(self): conf = ast.literal_eval(sampleConfStr)['默认配置'] confPath = self.ConfPath() if os.path.exists(confPath): try: with open(confPath, 'rb') as f: cusConf = ast.literal_eval(BYTES2STR(f.read())) if type(cusConf) is not dict: raise ConfError('文件内容必须是一个dict') elif self.user is None: pass elif self.user not in cusConf: raise ConfError('用户 %s 不存在' % self.user) elif type(cusConf[self.user]) is not dict: raise ConfError('用户 %s 的配置必须是一个 dict' % self.user) else: for k, v in list(cusConf[self.user].items()): if k not in conf: raise ConfError('不存在的配置选项 %s.%s ' % (self.user, k)) elif type(v) is not type(conf[k]): raise ConfError( '%s.%s 必须是一个 %s' % (self.user, k, type(conf[k]).__name__)) else: conf[k] = v except (IOError, SyntaxError, ValueError, ConfError) as e: PRINT('配置文件 %s 错误: %s\n' % (confPath, e), end='') sys.exit(1) else: try: with open(confPath, 'wb') as f: f.write(STR2BYTES(sampleConfStr)) except IOError: pass if self.user is not None: PRINT('用户 %s 不存在\n' % self.user, end='') sys.exit(1) for k, v in list(conf.items()): if getattr(self, k, None) is None: setattr(self, k, v) if self.mailAccount and not self.mailAuthCode: msg = '请输入 %s 的 IMAP/SMTP 服务授权码: ' % self.mailAccount self.mailAuthCode = RAWINPUT(msg)
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 response(self, request): request = BYTES2STR(request) url = None if request.startswith('GET /'): end = request.find('\r\n') if end != -1 and request[:end - 3].endswith(' HTTP/'): url = request[5:end - 9].rstrip('/') resp = b'' if (url is not None) and (url != 'favicon.ico'): try: with open(self.qrcodePath, 'rb') as f: png = f.read() except Exception as e: ERROR('读取二维码文件 %s 出错:%s', SYSTEMSTR2STR(self.qrcodePath), e) else: resp = (b'HTTP/1.1 200 OK\r\n' + b'Connection: close\r\n' + b'Content-Length: ' + STR2BYTES(str(len(png))) + b'\r\n' + b'Content-Type: image/png\r\n\r\n' + png) return resp
def readConfFile(self): confPath = self.ConfPath() strConfPath = SYSTEMSTR2STR(confPath) conf = rootConf.copy() if os.path.exists(confPath): try: with open(confPath, 'rb') as f: cusConf = ast.literal_eval(BYTES2STR(f.read())) if type(cusConf) is not dict: raise ConfError('文件内容必须是一个 dict') if type(cusConf.get('默认配置', {})) is not dict: raise ConfError('默认配置必须是一个 dict') if self.user is not None: if self.user not in cusConf: raise ConfError('用户 %s 不存在' % self.user) elif type(cusConf[self.user]) is not dict: raise ConfError('用户 %s 的配置必须是一个 dict' % self.user) else: names = ['默认配置', self.user] else: names = ['默认配置'] for name in names: for k, v in list(cusConf.get(name, {}).items()): if k in deprecatedConfKeys: PRINT('被废弃的配置选项 %s ,将忽略此选项' % k) elif k not in conf: raise ConfError('不存在的配置选项 %s.%s ' % (name, k)) elif type(v) is not type(conf[k]): t = type(conf[k]).__name__ raise ConfError('%s.%s 必须是一个 %s' % (name, k, t)) else: conf[k] = v except (IOError, SyntaxError, ValueError, ConfError) as e: PRINT('配置文件 %s 错误: %s\n' % (strConfPath, e), end='') sys.exit(1) else: PRINT('未找到配置文件“%s”,将使用默认配置' % strConfPath) try: with open(confPath, 'wb') as f: f.write(STR2BYTES(sampleConfStr)) except IOError: pass else: PRINT('已创建一个默认配置文件“%s”' % strConfPath) if self.user is not None: PRINT('用户 %s 不存在\n' % self.user, end='') sys.exit(1) for k, v in list(conf.items()): if getattr(self, k, None) is None: setattr(self, k, v) if self.pluginPath and not os.path.isdir(STR2SYSTEMSTR( self.pluginPath)): PRINT('配置文件 %s 错误: 插件目录 “%s” 不存在\n' % \ (strConfPath, self.pluginPath), end='') sys.exit(1) if self.mailAccount and not self.mailAuthCode: msg = '请输入 %s 的 IMAP/SMTP 服务授权码: ' % self.mailAccount self.mailAuthCode = RAWINPUT(msg) if self.cmdQrcode: try: import PIL import wcwidth except ImportError: PRINT('您已选择以文本模式显示二维码,请先安装 pillow, wcwidth 库') sys.exit(1)
def fetchGroupMemberTable(self, group): result = self.smartRequest( url = ('http://s.web2.qq.com/api/get_group_info_ext2?gcode=%s' '&vfwebqq=%s&t={rand}') % (group.gcode, self.vfwebqq), Referer = ('http://s.web2.qq.com/proxy.html?v=20130916001' '&callback=1&id=1'), expectedKey = 'minfo', repeatOnDeny = 5 ) cardDict = collections.defaultdict(list) nickDict = collections.defaultdict(list) _qqDict = {} if group.qq != '#NULL': r = self.smartRequest( url='http://qinfo.clt.qq.com/cgi-bin/qun_info/get_group_members_new', Referer='http://qinfo.clt.qq.com/member.html', data={'gc': group.qq, 'u': self.uin , 'bkn': self.bkn} ) for m in r['mems']: qq = str(m['u']) nick = HTMLUnescape(m['n']) card = HTMLUnescape(r.get('cards', {}).get(qq, '')) mark = HTMLUnescape(r.get('remarks', {}).get(qq, '')) name = card or nick join_time = r.get('join', {}).get(qq, 0) last_speak_time = r.get('times', {}).get(qq, 0) is_buddy = m['u'] in r.get('friends', []) if r['owner'] == m['u'] : role, role_id = '群主', 0 elif m['u'] in r.get('adm', []): role, role_id = '管理员', 1 else: role, role_id = '普通成员', 2 level = r.get('lv', {}).get(qq, {}).get('l', 0) levelname = HTMLUnescape(r.get('levelname', {}).get('lvln' + str(level), '')) point = r.get('lv', {}).get(qq, {}).get('p', 0) memb = [qq, None, nick, mark, card, name, join_time, last_speak_time, role, role_id, is_buddy, level, levelname, point] if card: cardDict[STR2BYTES(card)[:18]].append(memb) nickDict[nick].append(memb) _qqDict[qq] = memb membss, unresolved = [], [] ucDict = dict((str(it['muin']), it['card']) for it in result.get('cards', {})) for m, inf in zip(result['ginfo']['members'], result['minfo']): uin, nick = str(m['muin']), str(inf['nick']) card = ucDict.get(uin, '') if not PY3: card = card.replace('\xc2\xa0', ' ') nick = nick.replace('\xc2\xa0', ' ') else: card = card.replace('\xa0', ' ') nick = nick.replace('\xa0', ' ') name = card or nick membs = nickDict.get(nick, []) if len(membs) == 1: memb = membs[0] else: membs = cardDict.get(STR2BYTES(card)[:18], []) if len(membs) == 1: memb = membs[0] else: memb = None if memb is None: unresolved.append('成员“%s”(uin=%s)' % (name, uin)) memb = ['#NULL', uin, nick, mark, card, name, -1, -1, '#NULL', -1, -1, -1, '#NULL', -1] else: memb[1] = uin _qqDict.pop(memb[0], None) membss.append(memb) if unresolved: unresolved.sort() WARN('因存在重名或名称中含特殊字符,无法绑定 %s 中以下' '成员的真实QQ号:\n\t%s', group, '\n\t'.join(unresolved)) for memb in _qqDict.values(): memb[1] = '-' + memb[0] membss.append(memb) return membss