Skip to content

Instantly share code, notes, and snippets.

@leeyisoft
Created August 31, 2017 08:16
Show Gist options
  • Save leeyisoft/fd2a469cbdf17d3501a41cce642b8777 to your computer and use it in GitHub Desktop.
Save leeyisoft/fd2a469cbdf17d3501a41cce642b8777 to your computer and use it in GitHub Desktop.
pip3 install -U wxpy \n mkdir pkl
#!/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)
#!/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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment