def get(url, api=None): """ @brief http get request @param url String @param api wechat api @return http response """ Log.debug('GET -> ' + url) request = urllib2.Request(url=url) request.add_header(*Constant.HTTP_HEADER_CONNECTION) request.add_header(*Constant.HTTP_HEADER_REFERER) if api in ['webwxgetvoice', 'webwxgetvideo']: request.add_header(*Constant.HTTP_HEADER_RANGE) while True: try: response = urllib2.urlopen(request, timeout=30) data = response.read() response.close() if api == None: Log.debug(data) return data except (KeyboardInterrupt, SystemExit): raise except: Log.error(traceback.format_exc()) time.sleep(1)
def delete_table(self, table): """ @brief Delete a table in database @param table String """ sql = "DROP TABLE if exists %s;" % table Log.debug('DB -> %s' % sql) self.execute(sql)
def create_db(self, db_name): """ @brief Creates a database @param db_name String """ if self.conf['database'] not in self.show_database(): sql = 'CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' % db_name Log.debug('DB -> %s' % sql) self.execute(sql)
def create_table(self, table, cols): """ @brief Creates a table in database @param table String @param cols String, the cols in table """ sql = "CREATE TABLE if not exists %s (%s);" % (table, cols) Log.debug('DB -> %s' % sql) self.execute(sql)
def insert(self, table, value): """ @brief Insert a row in table @param table String @param value Tuple """ sql = ("INSERT INTO %s VALUES (" + ",".join(['?'] * len(value)) + ");") % table Log.debug('DB -> %s' % sql) self.execute(sql, value)
def close(self): """ @brief close connection to database """ Log.debug('DB -> close') # 关闭数据库连接 self.conn.close()
def insertmany(self, table, values): """ @brief Insert many rows in table @param table String @param values Array of tuple """ col_name = self.table_cols[table][1:] sql = 'INSERT INTO %s(%s) VALUES (%s)' % (table, ','.join(col_name), ','.join(['%s'] * len(values[0]))) Log.debug('DB -> %s' % sql) self.execute(sql, values)
def delete(self, table, field='', condition=''): """ @brief execute sql commands, return result if it has @param table String @param field String @param condition String """ sql = "DELETE FROM %s WHERE %s=%s" % (table, field, condition) Log.debug('DB -> %s' % sql) self.execute(sql)
def delete_table(self, table): """ @brief Delete a table in database @param table String """ if table in self.table_cols: sql = "DROP TABLE IF EXISTS %s" % table Log.debug('DB -> %s' % sql) self.execute(sql) self.table_cols.pop(table)
def insert(self, table, value): """ @brief Insert a row in table @param table String @param value Tuple """ col_name = self.table_cols[table][1:] sql = "INSERT INTO %s(%s) VALUES (%s)" % (table, str(','.join(col_name)), array_join(value, ',')) Log.debug('DB -> %s' % sql) self.execute(sql)
def create_table(self, table, cols): """ @brief Creates a table in database @param table String @param cols String, the cols in table """ if table not in self.table_cols: sql = 'CREATE TABLE IF NOT EXISTS %s(id int primary key auto_increment, %s) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' % (table, cols) Log.debug('DB -> %s' % sql) self.execute(sql) self.table_cols[table] = ['id'] + [c.strip().split(' ')[0] for c in cols.split(',')]
def check_schedule_task(self): # update group member list at 00:00 am every morning print '[*] check_schedule_task 执行定时任务,同步group member list' t = time.localtime() if t.tm_hour == 0 and t.tm_min <= 1: # update group member Log.debug('update group member list everyday') self.db.delete_table(Constant.TABLE_GROUP_LIST()) self.db.delete_table(Constant.TABLE_GROUP_USER_LIST()) self.db.create_table(Constant.TABLE_GROUP_LIST(), Constant.TABLE_GROUP_LIST_COL) self.db.create_table(Constant.TABLE_GROUP_USER_LIST(), Constant.TABLE_GROUP_USER_LIST_COL) self.wechat.fetch_group_contacts()
def update(self, table, dic, condition=''): k_arr = [] v_arr = [] for (k, v) in dic.items(): k_arr.append('%s=?' % k) v_arr.append(v) sql = "UPDATE %s SET %s" % (table, ','.join(k_arr)) if condition: sql += " WHERE %s" % condition Log.debug('DB -> %s' % sql) self.execute(sql, tuple(v_arr))
def str2qr_terminal(text): """ @brief convert string to qrcode matrix and outprint @param text The string """ Log.debug(text) qr = qrcode.QRCode() print(qr) qr.border = 1 qr.add_data(text) mat = qr.get_matrix() print(mat) print_qr(mat)
def select(self, table, field='', condition=''): """ @brief select all result from table @param table String @param field String @param condition String @return result Tuple """ sql = "SELECT * FROM %s" % table if field and condition: sql += " WHERE %s='%s'" % (field, condition) Log.debug('DB -> %s' % sql) return self.execute(sql)
def save_file(filename, data, dirName): """ @brief Saves raw data to file. @param filename String @param data Binary data @param dirName String @return file path """ Log.debug('save file: ' + filename) fn = filename if not os.path.exists(dirName): os.makedirs(dirName) fn = os.path.join(dirName, filename) with open(fn, 'wb') as f: f.write(data) return fn
def save_json(filename, data, dirName, mode='w+'): """ @brief Saves dict to json file. @param filename String @param data Dict @param dirName String @return file path """ Log.debug('save json: ' + filename) fn = filename if not os.path.exists(dirName): os.makedirs(dirName) fn = os.path.join(dirName, filename) with open(fn, mode) as f: f.write(json.dumps(data, indent=4) + '\n') return fn
def insertmany(self, table, values): """ @brief Insert many rows in table @param table String @param values Array of tuple """ c = self.conn.cursor() self.lock.acquire() n = len(values[0]) sql = ("INSERT INTO %s VALUES (" + ",".join(['?'] * n) + ");") % table Log.debug('DB -> %s' % sql) try: c.executemany(sql, values) except Exception, e: Log.error(traceback.format_exc())
def select(self, table, field='', condition=''): """ @brief select all result from table @param table String @param field String @param condition String @return result Tuple """ result = [] if field and condition: cond = (condition,) sql = "SELECT * FROM %s WHERE %s=?" % (table, field) Log.debug('DB -> %s' % sql) result = self.execute(sql, cond) else: sql = "SELECT * FROM %s" % table Log.debug('DB -> %s' % sql) result = self.execute(sql) return result
def handle_mod(self, r): # ModContactCount: 变更联系人或群聊成员数目 # ModContactList: 变更联系人或群聊列表,或群名称 Log.debug('handle modify') self.handle_msg(r) for m in r['ModContactList']: if m['UserName'][:2] == '@@': # group in_list = False g_id = m['UserName'] for g in self.GroupList: # group member change if g_id == g['UserName']: g['MemberCount'] = m['MemberCount'] g['NickName'] = m['NickName'] self.GroupMemeberList[g_id] = m['MemberList'] in_list = True if self.msg_handler: self.msg_handler.handle_group_member_change(g_id, m['MemberList']) break if not in_list: # a new group self.GroupList.append(m) self.GroupMemeberList[g_id] = m['MemberList'] if self.msg_handler: self.msg_handler.handle_group_list_change(m) self.msg_handler.handle_group_member_change(g_id, m['MemberList']) elif m['UserName'][0] == '@': # user in_list = False for u in self.MemberList: u_id = m['UserName'] if u_id == u['UserName']: u = m in_list = True break # if don't have then add it if not in_list: self.MemberList.append(m)
def snapshot(self): """ @brief Save basic infos for next login. @return Bool: whether operation succeed. """ try: conf = { 'uuid': self.uuid, 'redirect_uri': self.redirect_uri, 'uin': self.uin, 'sid': self.sid, 'skey': self.skey, 'pass_ticket': self.pass_ticket, 'synckey': self.synckey, 'device_id': self.device_id, 'last_login': time.time(), } cm = ConfigManager() Log.debug('save wechat config') cm.set_wechat_config(conf) # save cookie Log.debug('save cookie') if self.cookie: self.cookie.save(ignore_discard=True) # save contacts Log.debug('save contacts') self.save_contacts() except Exception, e: Log.error(traceback.format_exc()) return False
def post(url, params, jsonfmt=True): """ @brief http post request @param url String @param params Dict, post params @param jsonfmt Bool, whether is json format @return http response """ Log.debug('POST -> ' + url) Log.debug(params) if jsonfmt: request = urllib2.Request( url=url, data=json.dumps(params, ensure_ascii=False).encode('utf8')) request.add_header(*Constant.HTTP_HEADER_CONTENTTYPE) else: request = urllib2.Request(url=url, data=urllib.urlencode(params)) while True: try: response = urllib2.urlopen(request, timeout=30) data = response.read() response.close() if jsonfmt: Log.debug(data) return json.loads(data, object_hook=_decode_data) return data except (KeyboardInterrupt, SystemExit): raise except: Log.error(traceback.format_exc()) time.sleep(1)
def recover(self): """ @brief Recover from snapshot data. @return Bool: whether operation succeed. """ cm = ConfigManager() [self.uuid, self.redirect_uri, self.uin, self.sid, self.skey, self.pass_ticket, self.synckey, device_id, self.last_login] = cm.get_wechat_config() print([self.uuid, self.redirect_uri, self.uin,\ self.sid, self.skey, self.pass_ticket,\ self.synckey, device_id, self.last_login]) if device_id: self.device_id = device_id self.base_request = { 'Uin': int(self.uin), 'Sid': self.sid, 'Skey': self.skey, 'DeviceID': self.device_id, } # set cookie Log.debug('set cookie') self.cookie = set_cookie(self.cookie_file) return True
def start(self): echo(Constant.LOG_MSG_START) print('[*] wechat.wechat.start 启动微信') run(Constant.LOG_MSG_RECOVER, self.recover) timeOut = time.time() - self.last_login echo(Constant.LOG_MSG_TRY_INIT) if self.webwxinit(): echo(Constant.LOG_MSG_SUCCESS) run(Constant.LOG_MSG_RECOVER_CONTACT, self.recover_contacts) else: echo(Constant.LOG_MSG_FAIL) while True: # first try to login by uin without qrcode print '[*] 关联登录无需二维码' echo(Constant.LOG_MSG_ASSOCIATION_LOGIN) if self.association_login(): print '[*] 关联登录,成功' echo(Constant.LOG_MSG_SUCCESS) else: print '[*] 关联登录,失败' echo(Constant.LOG_MSG_FAIL) # scan qrcode to login run(Constant.LOG_MSG_GET_UUID, self.getuuid) echo(Constant.LOG_MSG_GET_QRCODE) self.genqrcode() echo(Constant.LOG_MSG_SCAN_QRCODE) if not self.waitforlogin(): continue echo(Constant.LOG_MSG_CONFIRM_LOGIN) print '[*] 登录确认' if not self.waitforlogin(0): continue break run(Constant.LOG_MSG_LOGIN, self.login) run(Constant.LOG_MSG_INIT, self.webwxinit) run(Constant.LOG_MSG_STATUS_NOTIFY, self.webwxstatusnotify) run(Constant.LOG_MSG_GET_CONTACT, self.webwxgetcontact) echo(Constant.LOG_MSG_CONTACT_COUNT % ( self.MemberCount, len(self.MemberList) )) echo(Constant.LOG_MSG_OTHER_CONTACT_COUNT % ( len(self.GroupList), len(self.ContactList), len(self.SpecialUsersList), len(self.PublicUsersList) )) run(Constant.LOG_MSG_GET_GROUP_MEMBER, self.fetch_group_contacts) run(Constant.LOG_MSG_SNAPSHOT, self.snapshot) #------------------------ # add by haining.qin 17-05-04 # instance a MsgProcessor # msgProcessor = WeChatMsgProcessor() #------------------------ while True: print('\n[*] while 监听 微信消息') [retcode, selector] = self.synccheck() Log.debug('retcode: %s, selector: %s' % (retcode, selector)) self.exit_code = int(retcode) print('[*] 返回[retcode,selector]=',[retcode,selector]) if retcode == '1100': echo(Constant.LOG_MSG_LOGOUT) break if retcode == '1101': echo(Constant.LOG_MSG_LOGIN_OTHERWHERE) break if retcode == '1102': echo(Constant.LOG_MSG_QUIT_ON_PHONE) break elif retcode == '0': if selector == '2': r = self.webwxsync() if r is not None: try: self.handle_msg(r) except: traceback.print_exc() Log.error(traceback.format_exc()) elif selector == '7': r = self.webwxsync() elif selector == '0': time.sleep(self.time_out) elif selector == '4': print '[*] 保存群聊,修改群名,增删联系' ''''' # 保存群聊到通讯 # 修改群名 # 新增或删除联系人 # 群聊成员数目变化 ''' r = self.webwxsync() if r is not None: try: self.handle_mod(r) except: traceback.print_exc() Log.error(traceback.format_exc()) elif selector == '3' or selector == '6': break else: r = self.webwxsync() Log.debug('webwxsync: %s\n' % json.dumps(r)) # 执行定时任务 if self.msg_handler: self.msg_handler.check_schedule_task() print '[*] 判断bot是否存在? ', self.bot if self.bot: r = self.bot.time_schedule() if r: for g in self.GroupList: echo('[*] 推送-> %s: %s' % (g['NickName'], r)) g_id = g['UserName'] print '[*] 推送-> %s: %s' % (g['NickName'], r) self.webwxsendmsg(r, g_id)
def make_group_msg(self, msg): print '\n[*] make_group_msg, ', msg """ @brief Package the group message for storage. @param msg Dict: raw msg @return raw_msg Dict: packged msg """ Log.debug('make group message') print "\n[*] Personal Msg" raw_msg = { 'raw_msg': msg['raw_msg'], 'msg_id': msg['raw_msg']['MsgId'], 'group_owner_uin': '', 'group_name': '', 'group_count': '', 'from_user_name': msg['raw_msg']['FromUserName'], 'to_user_name': msg['raw_msg']['ToUserName'], 'user_attrstatus': '', 'user_display_name': '', 'user_nickname': '', 'msg_type': msg['raw_msg']['MsgType'], 'text': '', 'link': '', 'image': '', 'video': '', 'voice': '', 'emoticon': '', 'namecard': '', 'location': '', 'recall_msg_id': '', 'sys_notif': '', 'time': '', 'timestamp': '', 'log': '', } content = msg['raw_msg']['Content'].replace( '<', '<').replace('>', '>') group = None src = None if msg['raw_msg']['FromUserName'][:2] == '@@': # 接收到来自群的消息 g_id = msg['raw_msg']['FromUserName'] group = self.get_group_by_id(g_id) if re.search(":<br/>", content, re.IGNORECASE): u_id = content.split(':<br/>')[0] src = self.get_group_user_by_id(u_id, g_id) elif msg['raw_msg']['ToUserName'][:2] == '@@': # 自己发给群的消息 g_id = msg['raw_msg']['ToUserName'] u_id = msg['raw_msg']['FromUserName'] src = self.get_group_user_by_id(u_id, g_id) group = self.get_group_by_id(g_id) if src: raw_msg['user_attrstatus'] = src['AttrStatus'] raw_msg['user_display_name'] = src['DisplayName'] raw_msg['user_nickname'] = src['NickName'] if group: raw_msg['group_count'] = group['MemberCount'] raw_msg['group_owner_uin'] = group['OwnerUin'] raw_msg['group_name'] = group['ShowName'] raw_msg['timestamp'] = msg['raw_msg']['CreateTime'] t = time.localtime(float(raw_msg['timestamp'])) raw_msg['time'] = time.strftime("%Y-%m-%d %T", t) for key in [ 'text', 'link', 'image', 'video', 'voice', 'emoticon', 'namecard', 'location', 'log', 'recall_msg_id', 'sys_notif' ]: if key in msg: raw_msg[key] = msg[key] return raw_msg
def handle_group_msg(self, msg): """ @brief Recieve group messages @param msg Dict: packaged msg """ # rename media files for k in ['image', 'video', 'voice']: if msg[k]: t = time.localtime(float(msg['timestamp'])) time_str = time.strftime("%Y%m%d%H%M%S", t) # format: 时间_消息ID_群名 file_name = '/%s_%s_%s.' % (time_str, msg['msg_id'], msg['group_name']) new_name = re.sub(r'\/\w+\_\d+\.', file_name, msg[k]) Log.debug('rename file to %s' % new_name) os.rename(msg[k], new_name) msg[k] = new_name if msg['msg_type'] == 10000: # record member enter in group m = re.search(r'邀请(.+)加入了群聊', msg['sys_notif']) if m: name = m.group(1) col_enter_group = ( msg['msg_id'], msg['group_name'], msg['from_user_name'], msg['to_user_name'], name, msg['time'], ) self.db.insert(Constant.TABLE_RECORD_ENTER_GROUP, col_enter_group) # record rename group n = re.search(r'(.+)修改群名为“(.+)”', msg['sys_notif']) if n: people = n.group(1) to_name = n.group(2) col_rename_group = ( msg['msg_id'], msg['group_name'], to_name, people, msg['time'], ) self.db.insert(Constant.TABLE_RECORD_RENAME_GROUP, col_rename_group) # upadte group in GroupList for g in self.wechat.GroupList: if g['UserName'] == msg['from_user_name']: g['NickName'] = to_name break # normal group message col = ( msg['msg_id'], msg['group_owner_uin'], msg['group_name'], msg['group_count'], msg['from_user_name'], msg['to_user_name'], msg['user_attrstatus'], msg['user_display_name'], msg['user_nickname'], msg['msg_type'], msg['emoticon'], msg['text'], msg['image'], msg['video'], msg['voice'], msg['link'], msg['namecard'], msg['location'], msg['recall_msg_id'], msg['sys_notif'], msg['time'], msg['timestamp'] ) self.db.insert(Constant.TABLE_GROUP_MSG_LOG, col) text = msg['text'] if text and text[0] == '@': n = trans_coding(text).find(u'\u2005') name = trans_coding(text)[1:n].encode('utf-8') if name in [self.wechat.User['NickName'], self.wechat.User['RemarkName']]: self.handle_command(trans_coding(text)[n+1:].encode('utf-8'), msg)
def show_tables(self): c = self.conn.cursor() sql = 'SHOW TABLES' Log.debug('DB -> %s' % sql) c.execute(sql) return [r['Tables_in_'+self.conf['database']] for r in c.fetchall()]
def show_database(self): c = self.conn.cursor() sql = 'SHOW DATABASES' Log.debug('DB -> %s' % sql) c.execute(sql) return [r['Database'] for r in c.fetchall()]
def fetch_group_contacts(self): """ @brief Fetches all groups contacts. @return Bool: whether operation succeed. @note This function must be finished in 180s """ Log.debug('fetch_group_contacts') # clean database if self.msg_handler: self.msg_handler.clean_db() # sqlite # ---------------------------------------------------- # group max_thread_num max_fetch_group_num time(s) # 197 10 10 108 # 197 10 15 95 # 197 20 10 103 # 197 10 20 55 # 197 5 30 39 # 197 4 50 35 # ---------------------------------------------------- # mysql # ---------------------------------------------------- # group max_thread_num max_fetch_group_num time(s) # 197 4 50 20 # ---------------------------------------------------- max_thread_num = 4 max_fetch_group_num = 50 group_list_queue = Queue.Queue() class GroupListThread(threading.Thread): def __init__(self, group_list_queue, wechat): threading.Thread.__init__(self) self.group_list_queue = group_list_queue self.wechat = wechat def run(self): while not self.group_list_queue.empty(): g_list = self.group_list_queue.get() gid_list = [] g_dict = {} for g in g_list: gid = g['UserName'] gid_list.append(gid) g_dict[gid] = g group_member_list = self.wechat.webwxbatchgetcontact(gid_list) for member_list in group_member_list: gid = member_list['UserName'] g = g_dict[gid] g['MemberCount'] = member_list['MemberCount'] g['OwnerUin'] = member_list['OwnerUin'] self.wechat.GroupMemeberList[gid] = member_list['MemberList'] # 如果使用 Mysql 则可以在多线程里操作数据 #否则请注释下列代码在主线程里更新群列表 # ----------------------------------- # 处理群成员 # if self.wechat.msg_handler: # self.wechat.msg_handler.handle_group_member_list(gid, member_list['MemberList']) # ----------------------------------- self.group_list_queue.task_done() for g_list in split_array(self.GroupList, max_fetch_group_num): group_list_queue.put(g_list) for i in range(max_thread_num): t = GroupListThread(group_list_queue, self) t.setDaemon(True) t.start() group_list_queue.join() if self.msg_handler: # 处理消息 if self.GroupList: self.msg_handler.handle_group_list(self.GroupList) # 这个是用 sqlite 来存储群列表,sqlite 对多线程的支持不太好 # ---------------------------------------------------- # 处理群成员 for (gid, member_list) in self.GroupMemeberList.items(): self.msg_handler.handle_group_member_list(gid, member_list) # ---------------------------------------------------- return True