#!/usr/bin/env python
# coding=utf-8
from__future__importprint_function
importos
try:
fromurllibimporturlencode
exceptImportError:
fromurllib.parse importurlencode
try:
importurllib2aswdf_urllib
fromcookielibimportCookieJar
exceptImportError:
importurllib.request aswdf_urllib
fromhttp.cookiejar importCookieJar
importre
importtime
importxml.dom.minidom
importjson
importsys
importmath
importsubprocess
importssl
DEBUG= False
MAX_GROUP_NUM= 35# 每组人数
INTERFACE_CALLING_INTERVAL= 16# 接口调用时间间隔, 值设为13时亲测出现"操作太频繁"
MAX_PROGRESS_LEN= 50
QRImagePath= os.path.join(os.getcwd(),'qrcode.jpg')
tip= 0
uuid= ''
base_uri= ''
redirect_uri= ''
skey= ''
wxsid= ''
wxuin= ''
pass_ticket= ''
deviceId= 'e000000000000000'
BaseRequest= {}
ContactList= []
My= []
SyncKey= ''
try:
xrange
range= xrange
except:
# python 3
pass
defgetRequest(url,data=None):
try:
data= data.encode('utf-8')
except:
pass
finally:
returnwdf_urllib.Request(url=url,data=data)
defgetUUID():
globaluuid
url= 'https://login.weixin.qq.com/jslogin'
params= {
'appid': 'wx782c26e4c19acffb',
'fun': 'new',
'lang': 'zh_CN',
'_': int(time.time()),
}
request= getRequest(url=url,data=urlencode(params))
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
# window.QRLogin.code = 200; window.QRLogin.uuid = "oZwt_bFfRg==";
regx= r'window.QRLogin.code = (d+); window.QRLogin.uuid = "(S+?)"'
pm= re.search(regx,data)
code= pm.group(1)
uuid= pm.group(2)
ifcode== '200':
returnTrue
returnFalse
defshowQRImage():
globaltip
url= 'https://login.weixin.qq.com/qrcode/'+ uuid
params= {
't': 'webwx',
'_': int(time.time()),
}
request= getRequest(url=url,data=urlencode(params))
response= wdf_urllib.urlopen(request)
tip= 1
f= open(QRImagePath,'wb')
f.write(response.read())
f.close()
ifsys.platform.find('darwin')>= 0:
subprocess.call(['open',QRImagePath])
elifsys.platform.find('linux')>= 0:
subprocess.call(['xdg-open',QRImagePath])
else:
os.startfile(QRImagePath)
defwaitForLogin():
globaltip,base_uri,redirect_uri
url= 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s'% (
tip,uuid,int(time.time()))
request= getRequest(url=url)
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
# window.code=500;
regx= r'window.code=(d+);'
pm= re.search(regx,data)
code= pm.group(1)
ifcode== '201': # 已扫描
print('成功扫描,请在手机上点击确认以登录')
tip= 0
elifcode== '200': # 已登录
print('正在登录...')
regx= r'window.redirect_uri="(S+?)";'
pm= re.search(regx,data)
redirect_uri= pm.group(1)+ '&fun=new'
base_uri= redirect_uri[:redirect_uri.rfind('/')]
# closeQRImage
ifsys.platform.find('darwin')>= 0: # for OSX with Preview
os.system("osa -e 'quit app "Preview"'")
elifcode== '408': # 超时
pass
# elif code == '400' or code == '500':
returncode
deflogin():
globalskey,wxsid,wxuin,pass_ticket,BaseRequest
request= getRequest(url=redirect_uri)
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
'''
<error>
<ret>0</ret>
<message>OK</message>
<skey>xxx</skey>
<wxsid>xxx</wxsid>
<wxuin>xxx</wxuin>
<pass_ticket>xxx</pass_ticket>
<isgrayscale>1</isgrayscale>
</error>
'''
doc= xml.dom.minidom.parseString(data)
root= doc.documentElement
fornode inroot.childNodes:
ifnode.nodeName== 'skey':
skey= node.childNodes[0].data
elifnode.nodeName== 'wxsid':
wxsid= node.childNodes[0].data
elifnode.nodeName== 'wxuin':
wxuin= node.childNodes[0].data
elifnode.nodeName== 'pass_ticket':
pass_ticket= node.childNodes[0].data
# print('skey: %s, wxsid: %s, wxuin: %s, pass_ticket: %s' % (skey, wxsid,
# wxuin, pass_ticket))
ifnotall((skey,wxsid,wxuin,pass_ticket)):
returnFalse
BaseRequest= {
'Uin': int(wxuin),
'Sid': wxsid,
'Skey': skey,
'DeviceID': deviceId,
}
returnTrue
defwebwxinit():
url= base_uri+
'/webwxinit?pass_ticket=%s&skey=%s&r=%s'% (
pass_ticket,skey,int(time.time()))
params= {
'BaseRequest': BaseRequest
}
request= getRequest(url=url,data=json.dumps(params))
request.add_header('ContentType','application/json; charset=UTF-8')
response= wdf_urllib.urlopen(request)
data= response.read()
ifDEBUG:
f= open(os.path.join(os.getcwd(),'webwxinit.json'),'wb')
f.write(data)
f.close()
data= data.decode('utf-8','replace')
# print(data)
globalContactList,My,SyncKey
dic= json.loads(data)
ContactList= dic['ContactList']
My= dic['User']
SyncKeyList= []
foritem indic['SyncKey']['List']:
SyncKeyList.append('%s_%s'% (item['Key'],item['Val']))
SyncKey= '|'.join(SyncKeyList)
ErrMsg= dic['BaseResponse']['ErrMsg']
ifDEBUG:
print("Ret: %d, ErrMsg: %s"% (dic['BaseResponse']['Ret'],ErrMsg))
Ret= dic['BaseResponse']['Ret']
ifRet!= 0:
returnFalse
returnTrue
defwebwxgetcontact():
url= base_uri+
'/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s'% (
pass_ticket,skey,int(time.time()))
request= getRequest(url=url)
request.add_header('ContentType','application/json; charset=UTF-8')
response= wdf_urllib.urlopen(request)
data= response.read()
ifDEBUG:
f= open(os.path.join(os.getcwd(),'webwxgetcontact.json'),'wb')
f.write(data)
f.close()
# print(data)
data= data.decode('utf-8','replace')
dic= json.loads(data)
MemberList= dic['MemberList']
# 倒序遍历,不然删除的时候出问题..
SpecialUsers= ["newsapp","fmessage","filehelper","weibo","qqmail","tmessage","qmessage","qqsync","floatbottle","lbsapp","shakeapp","medianote","qqfriend","readerapp","blogapp","facebookapp","masssendapp",
"meishiapp","feedsapp","voip","blogappweixin","weixin","brandsessionholder","weixinreminder","wxid_novlwrv3lqwv11","gh_22b87fa7cb3c","officialaccounts","notification_messages","wxitil","userexperience_alarm"]
foriinrange(len(MemberList)- 1,-1,-1):
Member= MemberList[i]
ifMember['VerifyFlag']& 8!= 0: # 公众号/服务号
MemberList.remove(Member)
elifMember['UserName']inSpecialUsers: # 特殊账号
MemberList.remove(Member)
elifMember['UserName'].find('@@')!= -1: # 群聊
MemberList.remove(Member)
elifMember['UserName']== My['UserName']: # 自己
MemberList.remove(Member)
returnMemberList
defcreateChatroom(UserNames):
# MemberList = []
# for UserName in UserNames:
# MemberList.append({'UserName': UserName})
MemberList= [{'UserName': UserName}forUserName inUserNames]
url= base_uri+
'/webwxcreatechatroom?pass_ticket=%s&r=%s'% (
pass_ticket,int(time.time()))
params= {
'BaseRequest': BaseRequest,
'MemberCount': len(MemberList),
'MemberList': MemberList,
'Topic': '',
}
request= getRequest(url=url,data=json.dumps(params))
request.add_header('ContentType','application/json; charset=UTF-8')
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
dic= json.loads(data)
ChatRoomName= dic['ChatRoomName']
MemberList= dic['MemberList']
DeletedList= []
forMember inMemberList:
ifMember['MemberStatus']== 4: # 被对方删除了
DeletedList.append(Member['UserName'])
ErrMsg= dic['BaseResponse']['ErrMsg']
ifDEBUG:
print("Ret: %d, ErrMsg: %s"% (dic['BaseResponse']['Ret'],ErrMsg))
returnChatRoomName,DeletedList
defdeleteMember(ChatRoomName,UserNames):
url= base_uri+
'/webwxupdatechatroom?fun=delmember&pass_ticket=%s'% (pass_ticket)
params= {
'BaseRequest': BaseRequest,
'ChatRoomName': ChatRoomName,
'DelMemberList': ','.join(UserNames),
}
request= getRequest(url=url,data=json.dumps(params))
request.add_header('ContentType','application/json; charset=UTF-8')
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
dic= json.loads(data)
ErrMsg= dic['BaseResponse']['ErrMsg']
Ret= dic['BaseResponse']['Ret']
ifDEBUG:
print("Ret: %d, ErrMsg: %s"% (Ret,ErrMsg))
ifRet!= 0:
returnFalse
returnTrue
defaddMember(ChatRoomName,UserNames):
url= base_uri+
'/webwxupdatechatroom?fun=addmember&pass_ticket=%s'% (pass_ticket)
params= {
'BaseRequest': BaseRequest,
'ChatRoomName': ChatRoomName,
'AddMemberList': ','.join(UserNames),
}
request= getRequest(url=url,data=json.dumps(params))
request.add_header('ContentType','application/json; charset=UTF-8')
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
dic= json.loads(data)
MemberList= dic['MemberList']
DeletedList= []
forMember inMemberList:
ifMember['MemberStatus']== 4: # 被对方删除了
DeletedList.append(Member['UserName'])
ErrMsg= dic['BaseResponse']['ErrMsg']
ifDEBUG:
print("Ret: %d, ErrMsg: %s"% (dic['BaseResponse']['Ret'],ErrMsg))
returnDeletedList
defsyncCheck():
url= base_uri+ '/synccheck?'
params= {
'skey': BaseRequest['SKey'],
'sid': BaseRequest['Sid'],
'uin': BaseRequest['Uin'],
'deviceId': BaseRequest['DeviceID'],
'synckey': SyncKey,
'r': int(time.time()),
}
request= getRequest(url=url+ urlencode(params))
response= wdf_urllib.urlopen(request)
data= response.read().decode('utf-8','replace')
# print(data)
# window.synccheck={retcode:"0",selector:"2"}
defmain():
try:
ssl._create_default_https_context= ssl._create_unverified_context
opener= wdf_urllib.build_opener(
wdf_urllib.HTTPCookieProcessor(CookieJar()))
wdf_urllib.install_opener(opener)
except:
pass
ifnotgetUUID():
print('获取uuid失败')
return
showQRImage()
time.sleep(1)
whilewaitForLogin()!= '200':
pass
os.remove(QRImagePath)
ifnotlogin():
print('登录失败')
return
ifnotwebwxinit():
print('初始化失败')
return
MemberList= webwxgetcontact()
MemberCount= len(MemberList)
print('通讯录共%s位好友'% MemberCount)
ChatRoomName= ''
result= []
d= {}
forMember inMemberList:
d[Member['UserName']]= (Member['NickName'].encode(
'utf-8'),Member['RemarkName'].encode('utf-8'))
print('开始查找...')
group_num= int(math.ceil(MemberCount/ float(MAX_GROUP_NUM)))
foriinrange(0,group_num):
UserNames= []
forjinrange(0,MAX_GROUP_NUM):
ifi* MAX_GROUP_NUM+ j>= MemberCount:
break
Member= MemberList[i* MAX_GROUP_NUM+ j]
UserNames.append(Member['UserName'])
# 新建群组/添加成员
ifChatRoomName== '':
(ChatRoomName,DeletedList)= createChatroom(UserNames)
else:
DeletedList= addMember(ChatRoomName,UserNames)
DeletedCount= len(DeletedList)
ifDeletedCount> 0:
result+= DeletedList
# 删除成员
deleteMember(ChatRoomName,UserNames)
# 进度条
progress_len= MAX_PROGRESS_LEN
progress= '-'* progress_len
progress_str= '%s'% ''.join(
map(lambdax: '#',progress[:(progress_len* (i+ 1))/ group_num]))
print(''.join(
['[',progress_str,''.join('-'* (progress_len- len(progress_str))),']']))
print('新发现你被%d人删除'% DeletedCount)
foriinrange(DeletedCount):
ifd[DeletedList[i]][1]!= '':
print(d[DeletedList[i]][0]+ '(%s)'% d[DeletedList[i]][1])
else:
print(d[DeletedList[i]][0])
ifi!= group_num- 1:
print('正在继续查找,请耐心等待...')
# 下一次进行接口调用需要等待的时间
time.sleep(INTERFACE_CALLING_INTERVAL)
# todo 删除群组
print('n结果汇总完毕,20s后可重试...')
resultNames= []
forrinresult:
ifd[r][1]!= '':<