Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ dist
*.egg-info
*.pyc
*.db
*.swp
.idea
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# 变更说明

作者:[@raptorz](https://github.com/raptorz)

* 支持python3
* 符合PEP8
* 增加token管理,包括js_ticket
* 增加模板消息发送功能
* 可选是否验证https证书(原版为不验证)
* event_map改为可扩展

# 微信公众号Python-SDK

作者: [@jeff_kit](http://twitter.com/jeff_kit)
Expand Down
44 changes: 35 additions & 9 deletions wechat/crypt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#encoding=utf-8
# encoding=utf-8

import base64
import string
Expand All @@ -8,10 +8,21 @@
import struct
from Crypto.Cipher import AES
import xml.etree.cElementTree as ET
import sys
import socket
reload(sys)
sys.setdefaultencoding('utf-8')
import sys


if sys.version < "3":
reload(sys)
sys.setdefaultencoding('utf-8')
PY3 = False

def str2bytes(s):
return s
else:
def str2bytes(s):
return s.encode("utf-8") if isinstance(s, str) else s


WXBizMsgCrypt_OK = 0
WXBizMsgCrypt_ValidateSignature_Error = -40001
Expand Down Expand Up @@ -63,6 +74,13 @@ def getSHA1(self, token, timestamp, nonce, encrypt):
except Exception:
return WXBizMsgCrypt_ComputeSignature_Error, None

@staticmethod
def getSignature(token, timestamp, nonce):
sign_ele = [token, timestamp, nonce]
sign_ele.sort()
s = "".join(sign_ele)
return hashlib.sha1(str2bytes(s)).hexdigest()


class XMLParse:
"""提供提取消息格式中的密文及生成回复消息格式的接口"""
Expand All @@ -82,7 +100,7 @@ def extract(self, xmltext):
xml_tree = ET.fromstring(xmltext)
encrypt = xml_tree.find("Encrypt")
touser_name = xml_tree.find("ToUserName")
if touser_name != None:
if touser_name is not None:
touser_name = touser_name.text
return WXBizMsgCrypt_OK, encrypt.text, touser_name
except Exception:
Expand Down Expand Up @@ -139,7 +157,7 @@ class Prpcrypt(object):
"""提供接收和推送给公众平台消息的加解密接口"""

def __init__(self, key):
#self.key = base64.b64decode(key+"=")
# self.key = base64.b64decode(key+"=")
self.key = key
# 设置加解密模式为AES的CBC模式
self.mode = AES.MODE_CBC
Expand Down Expand Up @@ -178,8 +196,8 @@ def decrypt(self, text, appid):
try:
pad = ord(plain_text[-1])
# 去掉补位字符串
#pkcs7 = PKCS7Encoder()
#plain_text = pkcs7.encode(plain_text)
# pkcs7 = PKCS7Encoder()
# plain_text = pkcs7.encode(plain_text)
# 去除16位随机字符串
content = plain_text[16:-pad]
xml_len = socket.ntohl(struct.unpack("I", content[:4])[0])
Expand All @@ -202,17 +220,21 @@ def get_random_str(self):

class WXBizMsgCrypt(object):
def __init__(self, sToken, sEncodingAESKey, sCorpId):
(sToken, sEncodingAESKey, sCorpId) = \
map(str2bytes, (sToken, sEncodingAESKey, sCorpId))
try:
self.key = base64.b64decode(sEncodingAESKey+"=")
assert len(self.key) == 32
except:
throw_exception("[error]: EncodingAESKey unvalid !",
FormatException)
#return WXBizMsgCrypt_IllegalAesKey)
# return WXBizMsgCrypt_IllegalAesKey)
self.m_sToken = sToken
self.m_sCorpid = sCorpId

def VerifyURL(self, sMsgSignature, sTimeStamp, sNonce, sEchoStr):
(sMsgSignature, sTimeStamp, sNonce, sEchoStr) = \
map(str2bytes, (sMsgSignature, sTimeStamp, sNonce, sEchoStr))
sha1 = SHA1()
ret, signature = sha1.getSHA1(self.m_sToken,
sTimeStamp, sNonce, sEchoStr)
Expand All @@ -225,6 +247,8 @@ def VerifyURL(self, sMsgSignature, sTimeStamp, sNonce, sEchoStr):
return ret, sReplyEchoStr

def EncryptMsg(self, sReplyMsg, sNonce, timestamp=None):
(sReplyMsg, sNonce, timestamp) = \
map(str2bytes, (sReplyMsg, sNonce, timestamp))
pc = Prpcrypt(self.key)
ret, encrypt = pc.encrypt(sReplyMsg, self.m_sCorpid)
if ret != 0:
Expand All @@ -241,6 +265,8 @@ def EncryptMsg(self, sReplyMsg, sNonce, timestamp=None):
return ret, xmlParse.generate(encrypt, signature, timestamp, sNonce)

def DecryptMsg(self, sPostData, sMsgSignature, sTimeStamp, sNonce):
(sPostData, sMsgSignature, sTimeStamp, sNonce) = \
map(str2bytes, (sPostData, sMsgSignature, sTimeStamp, sNonce))
xmlParse = XMLParse()
ret, encrypt, touser_name = xmlParse.extract(sPostData)
if ret != 0:
Expand Down
22 changes: 10 additions & 12 deletions wechat/enterprise.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#encoding=utf-8
# encoding=utf-8

import requests
import time
Expand All @@ -9,6 +9,8 @@
WxVideoResponse, WxNewsResponse, APIError, WxEmptyResponse
from .official import WxApplication as BaseApplication, WxBaseApi
from .crypt import WXBizMsgCrypt
import sys


__all__ = ['WxRequest', 'WxResponse', 'WxArticle', 'WxImage',
'WxVoice', 'WxVideo', 'WxLink', 'WxTextResponse',
Expand Down Expand Up @@ -60,7 +62,7 @@ def process(self, params, xml=None, token=None, corp_id=None,
self.pre_process()
rsp = func(self.req)
self.post_process()
result = rsp.as_xml().encode('UTF-8')
result = rsp.as_xml().encode('UTF-8')

if not result:
return ''
Expand Down Expand Up @@ -114,7 +116,7 @@ def get_access_token(self, url=None, **kwargs):
params.update(kwargs)
rsp = requests.get(url or self.api_entry + 'cgi-bin/gettoken',
params=params,
verify=False)
verify=WxBaseApi.VERIFY)
return self._process_response(rsp)

def departments(self):
Expand Down Expand Up @@ -335,16 +337,12 @@ def delete_menu(self, agentid):
# OAuth2
def authorize_url(self, appid, redirect_uri, response_type='code',
scope='snsapi_base', state=None):
# 变态的微信实现,参数的顺序也有讲究。。艹!这个实现太恶心,太恶心!
url = 'https://open.weixin.qq.com/connect/oauth2/authorize?'
rd_uri = urllib.urlencode({'redirect_uri': redirect_uri})
url += 'appid=%s&' % appid
url += rd_uri
url += '&response_type=' + response_type
url += '&scope=' + scope
params = dict(appid=appid, redirect_uri=redirect_uri, response_type=response_type, scope=scope)
if state:
url += '&state=' + state
return url + '#wechat_redirect'
params['state'] = state
url = '?'.join(['https://open.weixin.qq.com/connect/oauth2/authorize', urllib.urlencode(sorted(params.items()))])
url = '#'.join([url, 'wechat_redirect'])
return url

def get_user_info(self, agentid, code):
return self._get('cgi-bin/user/getuserinfo',
Expand Down
7 changes: 6 additions & 1 deletion wechat/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
#encoding=utf-8
# encoding=utf-8

from xml.dom import minidom
import collections
import time
import sys

if sys.version > "3":
long = int
unicode = str


def kv2element(key, value, doc):
Expand Down
Loading