Created
August 31, 2017 08:16
-
-
Save leeyisoft/fd2a469cbdf17d3501a41cce642b8777 to your computer and use it in GitHub Desktop.
Revisions
-
leeyisoft created this gist
Aug 31, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,223 @@ #!/usr/bin/env python3 # encoding: utf-8 import requests import base64 import hashlib import os from wxpy import * from wxpy.api.chats.friend import Friend def _md5(Str): m = hashlib.md5(Str.encode(encoding='utf8')) return m.hexdigest() class WxBot(object): bot = None qr_path = 'qrcode.png' port = '' """docstring for WxBot""" def __init__(self, port, api_url, security_key): self.port = str(port) self.api_url = str(api_url) self.security_key = str(security_key) self.qr_path = './qrcode/%s_qrcode.png' % self.port self.bot = Bot(cache_path='./pkl/%s.pkl' % self.port , qr_callback=self.qr_callback ) self.bot.enable_puid(path='./pkl/%s_puid.pkl' % self.port) # print(dir(self.bot)) def qr_callback(self, **kwargs): qrcode = kwargs.get('qrcode') if not qrcode: return False with open(self.qr_path, 'wb') as fp: fp.write(qrcode) b64img = base64.b64encode(qrcode) #读取文件内容,转换为base64编码 # 下面参数顺序很不能够变 payload = { 'data' : 'data:image/png;base64,%s' % bytes.decode(b64img), # 'name': '', 'port' : self.port, 'qq' : '', 'type': 'wechat', } # print("payload", payload) sign = self.security_key for key in payload: sign += '%s=%s' % (str(key), str(payload[key])) payload['sign'] = _md5(sign) response = requests.post(self.api_url, data=payload) # print('response %s' % response.text) def login_callback(self): # 登陆成功记录端口和服务的关系 if os.path.isfile(self.qr_path): # 下面参数顺序很不能够变 payload = { 'data' : '', # 'name' : str(self.bot.self.name), 'port' : self.port, 'qq' : str(self.bot.self.puid), 'type': 'wechat', } # print('payload', payload) sign = self.security_key for key in payload: sign += '%s=%s' % (str(key), str(payload[key])) payload['sign'] = _md5(sign) response = requests.post(self.api_url, data=payload) os.remove(self.qr_path) def _list_buddy(self, params): """ 查询好友 参数 以是 sex(性别), province(省份), city(城市) 等。例如可指定 province='广东' """ response = {} keywords = params.get('keywords', '') sex = params.get('sex') city = params.get('city') province = params.get('province') sex = sex if sex else False city = city if city else False province = province if province else False if sex and city and province: response = self.bot.friends(update=True).search(keywords , sex=sex , city=city , province=province ) elif sex and city: response = self.bot.friends(update=True).search(keywords , sex=sex , city=city ) elif sex and province: response = self.bot.friends(update=True).search(keywords , sex=sex , province=province ) elif city and province: response = self.bot.friends(update=True).search(keywords , city=city , province=province ) else: response = self.bot.friends(update=True).search(keywords) # print('keywords: %s,' % keywords # , 'sex: %s,' % sex # ,'city: %s,' % city # ,'province: %s,' % province # ) def _filter_friend_raw(Friend): # print('raw', type(raw), raw) return { 'puid': Friend.puid, 'NickName': Friend.raw.get('NickName'), 'UserName': Friend.raw.get('UserName'), 'RemarkName': Friend.raw.get('RemarkName'), 'Sex': Friend.raw.get('Sex'), 'Province': Friend.raw.get('Province'), 'City': Friend.raw.get('City'), 'Signature': Friend.raw.get('Signature'), } data = [ _filter_friend_raw(Friend) for Friend in response] return {'result': data, 'count': len(data)} def _send_buddy(self, params): """ 给好友发送消息 """ buddy = params.get('buddy') content = params.get('content') # print('buddy', buddy) # print('content', content) try: f = Friend(buddy, self.bot) res = f.send(content) res = {'result': 'success'} except Exception as e: res = {'err': str(e)} # print('send/buddy', type(res), res) return res def _send_buddy_remarkname(self, params): RemarkName = params.get('RemarkName') content = params.get('content') try: my_friend = self.bot.friends().search(RemarkName) if not my_friend: my_friend = self.bot.friends(update=True).search(RemarkName) if not my_friend: raise Exception('Friend [%s] does not exist' % RemarkName) res = my_friend[0].send(content) res = {'result': 'success'} except Exception as e: res = {'err': str(e)} # print('send/buddy/remarkname', type(res), res) return res def call(self, cmd, params): response = {} try: if cmd=='list/buddy': response = self._list_buddy(params) elif cmd=='send/buddy': response = self._send_buddy(params) elif cmd=='send/buddy/remarkname': response = self._send_buddy_remarkname(params) except Exception as e: response = {'err': str(e), 'links': 'http://wxpy.readthedocs.io/zh/latest/'} raise e return response if __name__ == '__main__': try: PORT = 8110 wechat_bot = WxBot(PORT) wechat_bot.login_callback() # print(wechat_bot.bot.self.puid) # print(dir(wechat_bot.bot.self)) # exit(0) cmd = 'list/buddy' # params = {'keywords':'李', 'sex':2, 'city':'深圳', 'province':'广东'} params = {'keywords':'aaaleeyi茉莉茶'} response = wechat_bot.call(cmd, params) print(type(response), response) cmd = 'send/buddy' # [{'puid': 'f89084da', 'NickName': '茉莉茶', 'UserName': '@e63bbd48c33b4aa35b1eacfd72e0210f25f976f32fa8e5962931313b57fc09c0', 'RemarkName': '', 'Sex': 0, 'Province': '', 'City': '', 'Signature': 'molochakello'}] params = {'buddy':{'UserName': '@7639ff66321d9e8a474fdc9945c9b16ae0ac5a99f554dde77227c63b21861e82'}, 'content': 'eecccc usernme 变化33'} response = wechat_bot.call(cmd, params) print(type(response), response) cmd = 'send/buddy/remarkname' # [{'puid': 'f89084da', 'NickName': '茉莉茶', 'UserName': '@e63bbd48c33b4aa35b1eacfd72e0210f25f976f32fa8e5962931313b57fc09c0', 'RemarkName': '', 'Sex': 0, 'Province': '', 'City': '', 'Signature': 'molochakello'}] params = {'RemarkName':'aaaleeyi茉莉茶', 'content': 'dddd usernme 变化33'} response = wechat_bot.call(cmd, params) print(type(response), response) except KeyboardInterrupt: sys.exit(0) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,137 @@ #!/usr/bin/env python3 # encoding: utf-8 import sys import os import logging import socketserver import json from multiprocessing import Pool # https://github.com/serverdensity/python-daemon/blob/master/daemon.py from amqp.daemon import Daemon from common.wxpy_bot import WxBot logger_name = 'wxpy_server' HOST = '127.0.0.1' def _logger_init(logger_name, logger_path, logger_level): """ 日志初始化 """ formatter = logging.Formatter('[%(asctime)s]-[%(name)s]-[%(levelname)s]: %(message)s') logger = logging.getLogger(logger_name) logger.setLevel(logger_level) fh = logging.FileHandler(logger_path) fh.setFormatter(formatter) logger.addHandler(fh) class SingleTCPHandler(socketserver.BaseRequestHandler): "One instance per connection. Override handle(self) to customize action." def handle(self): # self.request is the client connection data = self.request.recv(102400) # clip input at 100Kb text = data.decode('utf-8') try: # 把单引号替换成双引号 text = text.replace("'",'"') req_dict = json.loads(text) cmd = req_dict.get('cmd') params = req_dict.get('params', {}) if not cmd: raise Exception('cmd is necessary parameter') response = self.server.wechat_bot.call(cmd, params) response = json.dumps(response) except Exception as e: response = json.dumps({'err': str(e)}) self.request.send(bytes(response, 'utf8')) self.request.close() class WXPYServer(socketserver.ThreadingMixIn, socketserver.TCPServer): # Ctrl-C will cleanly kill all spawned threads daemon_threads = True # much faster rebinding allow_reuse_address = True wechat_bot = None def __init__(self, server_address, RequestHandlerClass): from config import qrcode_upload_api from config import qrcode_upload_key # print('server_address', server_address) _, port = server_address self.wechat_bot = WxBot(port=port , api_url=qrcode_upload_api , security_key=qrcode_upload_key ) self.wechat_bot.login_callback() socketserver.TCPServer.__init__(self, server_address, RequestHandlerClass) class WXPYServerDaemon(Daemon): def start_server(self, port): self.log('port', port) try: server = WXPYServer((HOST, port), SingleTCPHandler) server.serve_forever() except Exception as e: self.log(e) raise e def run(self): """ 对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。 """ # 8110 ~ 8179 之间的端口计划给wxpy用; 8180 ~ 8210 之间的端口计划给qqbot用 from config import wxpy_server_ports pool_num = len(wxpy_server_ports) p = Pool(pool_num) # logging.getLogger(logger_name).info("pid[%s] run consumers: %s" % (os.getpid(), consumers)) for port in wxpy_server_ports: p.apply_async(self.start_server, args=(port,)) p.close() p.join() def restart(self): """ Restart the daemon """ self.stop() self.start() if __name__ == '__main__': if len(sys.argv) != 2: print( "ERROR: Wrong options for wxpy_server.py [start|stop|restart]") sys.exit(1) cmd = sys.argv[1] data_dir = './logs' if not os.path.exists(data_dir): print( "ERROR: pid_idr [%s] not exists!" % data_dir) sys.exit(1) pid_path = os.path.join(data_dir, 'wxpy_server.pid') log_path = os.path.join(data_dir, 'wxpy_server.log') _logger_init(logger_name, log_path, logging.INFO) wxpy_server = WXPYServerDaemon(pid_path, stdout=log_path) if cmd == "start": wxpy_server.start() elif cmd == 'stop': wxpy_server.stop() elif cmd == 'restart': wxpy_server.restart() else: print( "wxpy_server.py [start|stop|restart]") sys.exit(1)