├── wechat-sendall ├── __init__.py ├── regards.txt └── wechat-sendall.py ├── MANIFEST.in ├── requirements.txt ├── setup.cfg ├── wechat.gif ├── .gitignore ├── setup.py ├── README.md └── LICENSE /wechat-sendall/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include wechat-sendall/*.txt -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | beautifulsoup4 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /wechat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x2c3z4/wechat-sendall/HEAD/wechat.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | contacts.txt 3 | qrcode.jpg 4 | MANIFEST 5 | dist/* 6 | build/ 7 | -------------------------------------------------------------------------------- /wechat-sendall/regards.txt: -------------------------------------------------------------------------------- 1 | 猴年送你五只猴:机灵诡秘如猕猴,健康快乐像马猴,悠闲富贵思猴子,无忧无虑比猿猴,聪明伶俐胜孙猴。 2 | 祝你在新的一年长俊点,淑女点,聪明点,运碰点,烦消点,发财点,活干点,乐找点,苦稀点,喜稠点,哭少点,笑多点,要孝点,正好祝你13点……哈哈! 3 | 祝你:位高权重责任轻,事少钱多离家近,每天睡到自然醒,别人加班你加薪,领钱数得手抽筋,靓女爱你发神经。猴年大吉祥! 4 | 祝你猴年前程一个筋斗十万八千里,兼七十二变混世才学,具八十一种创业本领。 5 | 祝你猴年:新年大吉大利、百無禁忌、五福臨門、富貴吉祥、橫財就手、財運亨通、步步高升、生意興隆、東成西就、恭喜發財! 6 | 新的猴年祝福你:老板順著你,敵人躲著你,愛人體貼你,貴人幫著你,家人護著你,彩卷偏向你,升官發財隨便你! 7 | 祝你在新的一年里,快乐多如猴毛,聪敏胜过猴脑,进步犹如猴跑,烦恼好比猴子扔玉苞! 8 | 祝你猴年长得猴美猴美的,挣得猴多猴多的,心情猴好猴好的,运气猴顺猴顺的,睡觉猴香猴香的,爱情猴甜猴甜的,总之,一切都猴蜜猴蜜的! 9 | 新年快乐 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | setup( 3 | name = 'wechat-sendall', 4 | packages = ['wechat-sendall'], # this must be the same as the name above 5 | version = '1.1', 6 | description = 'Send custom message to your friend on wechat', 7 | author = 'Alex', 8 | author_email = 'lifeng1519@gmail.com', 9 | url = 'https://github.com/vonnyfly/wechat-sendall', # use the URL to the github repo 10 | download_url = 'https://github.com/vonnyfly/wechat-sendall/tarball/1.1', # I'll explain this in a second 11 | keywords = ['wechat', 'send', 'robot'], # arbitrary keywords 12 | classifiers = [], 13 | install_requires=[ 14 | 'requests', 15 | 'beautifulsoup4', 16 | ], 17 | include_package_data = True, 18 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wechat-sendall 2 | 过年了,给好友群发微信祝福过年吧,加上好友的名字,诚意十足,居家必备。 3 | 4 | 目前只做到够用,代码很乱,欢迎补充。 5 | 6 | # 效果图 7 | ![](wechat.gif) 8 | 9 | # 使用 10 | 11 | ``` 12 | $ pip install -r requirements.txt 13 | $ python wechat-sendall.py -h 14 | Usage: wechat-sendall.py [options] 15 | 16 | send custom message to your friend on wechat, default dry run 17 | 18 | Options: 19 | -h, --help show this help message and exit 20 | -s, --sendall send message to your friend, please double check 21 | -p PORT, --port=PORT http server port listen 22 | 23 | $ cd wechat-sendall 24 | $ python wechat-sendall.py 25 | [+] Getting QR Image... 26 | [+] Please open http://192.168.1.117/qrcode.jpg or open /var/www/html/qrcode.jpg 27 | [+] Scan success, please click confirm on your mobile phone 28 | [+] Logging in ... 29 | [+] Login success 30 | [+] sending to xiao ... 31 | [*] Send to yourself success. 32 | 33 | ``` 34 | 默认将登录二维码放在当前工作目录,点开URL,扫描登录。或者直接打开文件。 35 | 36 | *注意1* 37 | ``` 38 | $ python wechat-sendall.py -s 39 | ``` 40 | 默认只发给自己,打开了'-s'就给好友发祝福了,同时也给自己发一遍。 41 | 确认发给自己没问题了,再发出去噢。。。。要不闹笑话了。 42 | 43 | *注意2* 44 | 记得提前备注好好友的名字,没有备注的话使用昵称。 45 | 46 | *注意3* 47 | 默认随机祝福喔,可以替换成自己的话。修改regards.txt文件即可,每行一句。随机选择一行。 48 | 49 | *注意4* 50 | 格式控制代码,可以修改代码来自定义。 51 | 52 | ``` 53 | # content="嗨, %s 新年快乐 %s" % (name, "[拥抱]") 54 | content="嗨, %s, %s %s" % (name, getRandomMsg(), "[拥抱]") 55 | ``` 56 | 57 | # 测试 58 | 支持Windows, Linux, MAC. 59 | 60 | # 存在的问题 61 | 1. 部分帐号存在不能给自己发送的问题,一般直接登录web 微信也存在这个问题,不知道什么原因。 62 | 2. 部分消息会没有发送出去,当前没有做重试。 63 | 64 | # 感谢 65 | 登录部分借鉴https://github.com/0x5e/wechat-deleted-friends 66 | 感谢。 67 | 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /wechat-sendall/wechat-sendall.py: -------------------------------------------------------------------------------- 1 | #--*-- coding:utf-8 --*-- 2 | import requests, requests.utils, pickle 3 | import httplib 4 | import sys 5 | import pprint 6 | from bs4 import BeautifulSoup 7 | import re, shutil, xml.dom.minidom, json 8 | import netrc 9 | import os.path, time 10 | import random 11 | from optparse import OptionParser 12 | 13 | from SimpleHTTPServer import SimpleHTTPRequestHandler 14 | from BaseHTTPServer import HTTPServer 15 | import threading 16 | 17 | s = requests.Session() 18 | s.headers.update({'User-Agent': 19 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36', 20 | 'Connection': 'keep-alive', 21 | 'Content-type': 'application/x-www-form-urlencoded'}) 22 | def debugReq(r): 23 | pp = pprint.PrettyPrinter(indent=4) 24 | pp.pprint(r.status_code) 25 | # pp.pprint(r.request.__dict__) 26 | # print >>sys.stderr, r.text 27 | print >> sys.stderr, s.cookies.get_dict() 28 | 29 | real_send = False 30 | http_port = 5678 31 | 32 | uuid = '' 33 | redirect_uri = '' 34 | base_uri = '' 35 | skey = '' 36 | wxsid = '' 37 | wxuin = '' 38 | pass_ticket = '' 39 | deviceId = 'e000000000000000' 40 | BaseRequest = {} 41 | 42 | SyncKey = [] 43 | ContactList = [] 44 | ChatContactList = [] 45 | My = {} 46 | 47 | 48 | def getUUID(): 49 | global uuid 50 | # url = "https://login.weixin.qq.com/jslogin" 51 | # payload = { 52 | # 'redirect_uri':'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage', 53 | # 'appid': 'wx782c26e4c19acffb', 54 | # 'fun': 'new', 55 | # 'lang': 'zh_CN', 56 | # '_': int(time.time()), 57 | # } 58 | # headers = {'content-type': 'text/javascript'} 59 | # r = s.get(url, data = payload, headers = headers) 60 | url = "https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=" + str( 61 | int(time.time())) 62 | r = s.get(url) 63 | # debugReq(r) 64 | # window.QRLogin.code = 200; window.QRLogin.uuid = "oZwt_bFfRg=="; 65 | regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"' 66 | pm = re.search(regx, r.text) 67 | 68 | code = pm.group(1) 69 | uuid = pm.group(2) 70 | 71 | if code == '200': 72 | return True 73 | 74 | return False 75 | 76 | 77 | def getQRImage(): 78 | path = os.path.join(os.getcwd(), "qrcode.jpg") 79 | url = "https://login.weixin.qq.com/qrcode/" + uuid 80 | r = s.get(url, stream=True) 81 | # debugReq(r) 82 | if r.status_code == 200: 83 | with open(path, 'wb') as f: 84 | r.raw.decode_content = True 85 | shutil.copyfileobj(r.raw, f) 86 | import socket 87 | ip = [l 88 | for l in 89 | ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] 90 | if not ip.startswith("127.")][:1], 91 | [[(sc.connect(('8.8.8.8', 80)), sc.getsockname()[0], sc.close()) 92 | for sc in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) 93 | if l][0][0] 94 | print "[+] Please open http://" + ip + ":" + str(http_port) + "/qrcode.jpg or open " + path 95 | 96 | time.sleep(1) 97 | 98 | 99 | def waitForLogin(): 100 | global redirect_uri, base_uri 101 | url = "https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=%s&tip=0&r=-1746241605&_=%s" % ( 102 | uuid, int(time.time())) 103 | 104 | r = s.get(url, stream=True) 105 | # debugReq(r) 106 | # print r.text 107 | 108 | data = r.text 109 | regx = r'window.code=(\d+);' 110 | pm = re.search(regx, data) 111 | 112 | code = pm.group(1) 113 | 114 | if code == '201': # 已扫描 115 | print('[+] Scan success, please click confirm on your mobile phone') 116 | tip = 0 117 | elif code == '200': # 已登录 118 | print('[+] Logging in ...') 119 | regx = r'window.redirect_uri="(\S+?)";' 120 | pm = re.search(regx, data) 121 | redirect_uri = pm.group(1) + "&fun=new&version=v2" 122 | base_uri = redirect_uri[:redirect_uri.rfind('/')] 123 | # # push_uri与base_uri对应关系(排名分先后)(就是这么奇葩..) 124 | # services = [ 125 | # ('wx2.qq.com', 'webpush2.weixin.qq.com'), 126 | # ('qq.com', 'webpush.weixin.qq.com'), 127 | # ('web1.wechat.com', 'webpush1.wechat.com'), 128 | # ('web2.wechat.com', 'webpush2.wechat.com'), 129 | # ('wechat.com', 'webpush.wechat.com'), 130 | # ('web1.wechatapp.com', 'webpush1.wechatapp.com'), 131 | # ] 132 | # push_uri = base_uri 133 | # for (searchUrl, pushUrl) in services: 134 | # if base_uri.find(searchUrl) >= 0: 135 | # push_uri = 'https://%s/cgi-bin/mmwebwx-bin' % pushUrl 136 | # break 137 | elif code == '408': # 超时 138 | pass 139 | 140 | return code 141 | 142 | 143 | def login(): 144 | global skey, wxsid, wxuin, pass_ticket, BaseRequest 145 | r = s.get(redirect_uri) 146 | # debugReq(r) 147 | # print r.text 148 | data = r.text.encode('utf-8') 149 | doc = xml.dom.minidom.parseString(data) 150 | root = doc.documentElement 151 | 152 | for node in root.childNodes: 153 | if node.nodeName == 'skey': 154 | skey = node.childNodes[0].data 155 | elif node.nodeName == 'wxsid': 156 | wxsid = node.childNodes[0].data 157 | elif node.nodeName == 'wxuin': 158 | wxuin = node.childNodes[0].data 159 | elif node.nodeName == 'pass_ticket': 160 | pass_ticket = node.childNodes[0].data 161 | 162 | if not all((skey, wxsid, wxuin, pass_ticket)): 163 | return False 164 | 165 | BaseRequest = { 166 | 'Uin': int(wxuin), 167 | 'Sid': wxsid.encode('unicode_escape'), 168 | 'Skey': skey.encode('unicode_escape'), 169 | 'DeviceID': deviceId, 170 | } 171 | 172 | return True 173 | 174 | 175 | def responseState(func, BaseResponse): 176 | ErrMsg = BaseResponse['ErrMsg'] 177 | Ret = BaseResponse['Ret'] 178 | if Ret != 0: 179 | print('func: %s, Ret: %d, ErrMsg: %s' % (func, Ret, ErrMsg)) 180 | 181 | if Ret != 0: 182 | return False 183 | 184 | return True 185 | 186 | 187 | def webwxinit(): 188 | global My, SyncKey 189 | 190 | url = base_uri + "/webwxinit?r=-1746916482&lang=zh_CN&pass_ticket=" + pass_ticket 191 | payload = {'BaseRequest': BaseRequest} 192 | headers = {'ContentType': 'application/json; charset=UTF-8'} 193 | r = s.post(url, json=payload, headers=headers) 194 | # debugReq(r) 195 | # print r.text 196 | data = r.text.encode('unicode_escape').decode('string_escape') 197 | dic = json.loads(data) 198 | My = dic['User'] 199 | SyncKey = dic['SyncKey'] 200 | state = responseState('webwxinit', dic['BaseResponse']) 201 | return state 202 | 203 | 204 | def webwxsendmsg(friend, content): 205 | clientMsgId = str(int(time.time())) 206 | url = base_uri + "/webwxsendmsg?lang=zh_CN&pass_ticket=" + pass_ticket 207 | Msg = { 208 | 'Type': '1', 209 | 'Content': content, 210 | 'ClientMsgId': clientMsgId.encode('unicode_escape'), 211 | 'FromUserName': My['UserName'].encode('unicode_escape'), 212 | 'ToUserName': friend["UserName"].encode('unicode_escape'), 213 | 'LocalID': clientMsgId.encode('unicode_escape') 214 | } 215 | payload = {'BaseRequest': BaseRequest, 'Msg': Msg} 216 | headers = {'ContentType': 'application/json; charset=UTF-8'} 217 | # print str(payload).decode('string_escape') 218 | data = json.dumps(payload, ensure_ascii=False) 219 | # r = s.post(url, json=payload, headers=headers) 220 | r = s.post(url, data = data, headers=headers) 221 | # debugReq(r) 222 | # print r.text 223 | resp = json.loads(r.text) 224 | if 'BaseResponse' in resp: 225 | if 'Ret' in resp['BaseResponse']: 226 | return int(resp['BaseResponse']['Ret']) 227 | return -1 228 | 229 | def webwxsync(): 230 | url = base_uri + "/webwxsync?sid=" + wxsid + "&skey=" + skey 231 | payload = {'BaseRequest': BaseRequest, 'SyncKey': SyncKey, 'rr' : int(time.time())} 232 | headers = {'ContentType': 'application/json; charset=UTF-8'} 233 | data = json.dumps(payload, ensure_ascii=False) 234 | r = s.post(url, data = data, headers=headers) 235 | # debugReq(r) 236 | content = r.text.encode('unicode_escape').decode('string_escape') 237 | resp = json.loads(content) 238 | return resp 239 | 240 | def parseRecvMsgs(msgs): 241 | mymsgs = [] 242 | m = {} 243 | for msg in msgs: 244 | user = findFriend('UserName', msg['FromUserName']) 245 | if user: 246 | m[u'FromUserName'] = user['NickName'] 247 | else: 248 | m[u'FromUserName'] = msg['FromUserName'] 249 | m[u'Content'] = msg['Content'] 250 | m[u'Status'] = msg['Status'] 251 | 252 | user = findFriend('UserName', msg['ToUserName']) 253 | if user: 254 | m[u'ToUserName'] = user['NickName'] 255 | else: 256 | m[u'ToUserName'] = msg['ToUserName'] 257 | m[u'MsgType'] = msg['MsgType'] 258 | mymsgs.append(m) 259 | print json.dumps(mymsgs, ensure_ascii=False) 260 | return mymsgs 261 | 262 | def webwxgetcontact(): 263 | global ContactList 264 | url = base_uri + "/webwxgetcontact?r=" + str(int( 265 | time.time())) 266 | r = s.post(url, json={}) 267 | # debugReq(r) 268 | content = r.text.encode('unicode_escape').decode('string_escape') 269 | ContactList = json.loads(content)['MemberList'] 270 | #with open('contacts.txt', 'w') as f: 271 | # f.write(content) 272 | 273 | def getChatroomList(): 274 | global ChatContactList 275 | chat_list = [] 276 | for user in ContactList: 277 | if user['UserName'].find('@@') != -1: # 群聊 278 | chat_list.append({"UserName":user['UserName'], "ChatRoomId": ""}) 279 | 280 | url = base_uri + "/webwxbatchgetcontact?type=ex&r=%d&lang=zh_CN&pass_ticket=%s"% (time.time(), pass_ticket) 281 | payload = { 282 | 'BaseRequest': BaseRequest, 283 | 'List': chat_list, 284 | 'Count': len(chat_list) 285 | } 286 | 287 | r =s.post(url, json=payload) 288 | data = r.text.encode('unicode_escape').decode('string_escape') 289 | with open('chatroom.txt', 'w') as f: 290 | f.write(data) 291 | ChatContactList = json.loads(data)["ContactList"] 292 | 293 | # def webwxbatchgetcontact(): 294 | # url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=1453704524520" 295 | def findFriend(key, value): 296 | for friend in ContactList: 297 | if friend[key] == value: 298 | # print friend['NickName'] 299 | return friend 300 | return None 301 | 302 | def getRandomMsg(): 303 | lines = open('regards.txt').read().splitlines() 304 | myline =random.choice(lines) 305 | return myline 306 | 307 | def striphtml(data): 308 | p = re.compile(r'<.*?>') 309 | return p.sub('', data) 310 | 311 | def main(): 312 | global real_send, http_port 313 | parser = OptionParser(usage='%prog [options]', 314 | description='send custom message to your friend on wechat, default dry run') 315 | parser.add_option('-s', '--sendall',action='store_true', help='send message to your friend, please double check') 316 | parser.add_option('-p', '--port',type='int', help='http server port listen') 317 | 318 | (options, args) = parser.parse_args() 319 | if options.sendall: 320 | real_send = options.sendall 321 | if options.port: 322 | http_port = options.port 323 | 324 | server = HTTPServer(('0.0.0.0', http_port), SimpleHTTPRequestHandler) 325 | thread = threading.Thread(target = server.serve_forever) 326 | thread.daemon = True 327 | print "[+] Starting Http Server" 328 | try: 329 | thread.start() 330 | except KeyboardInterrupt: 331 | server.shutdown() 332 | sys.exit(0) 333 | 334 | if not getUUID(): 335 | print "[-] UUID get fail" 336 | return 337 | print "[+] Getting QR Image..." 338 | getQRImage() 339 | while waitForLogin() != '200': 340 | pass 341 | 342 | if not login(): 343 | print "[-] Login fail" 344 | return 345 | 346 | print "[+] Login success" 347 | 348 | if not webwxinit(): 349 | print "[-] Wxinit fail" 350 | 351 | webwxgetcontact() 352 | 353 | for f in ContactList: 354 | name = striphtml(f['RemarkName'].encode('utf-8')) 355 | if len(name) == 0: 356 | name = striphtml(f['NickName'].encode('utf-8')) 357 | 358 | # content="嗨, %s 新年快乐 %s" % (name, "[拥抱]") 359 | content="嗨, %s, %s %s" % (name, getRandomMsg(), "[拥抱]") 360 | ''' 361 | = -1 : 群聊 362 | = 0 : 公众号/服务号 363 | ''' 364 | if f['UserName'].find('@@') != -1 or f['VerifyFlag'] & 8 != 0: 365 | # content = "skip " + name 366 | # print "[-] " + content 367 | # webwxsendmsg(My, content=content) 368 | continue 369 | 370 | print "[+] Prepare sending to " + name + " ..." 371 | if webwxsendmsg(My, content=content) != 0: 372 | print "[!]\tSent to yourself fail, please check your account." 373 | else: 374 | print "[*]\tSent to yourself success." 375 | 376 | 377 | if real_send: 378 | # 发给朋友,请检查好喔 379 | if webwxsendmsg(f, content=content) != 0: 380 | print "[!]\tSent to " + name + " fail, please check your account." 381 | else: 382 | print "[*]\tSent to " + name + " success." 383 | 384 | time.sleep(1) 385 | 386 | if __name__ == "__main__": 387 | main() 388 | --------------------------------------------------------------------------------