def __init__(self, app_id, app_secret, token, encoding_aes_key=None): self.app_id = app_id if app_id else WX_OPEN_CONFIG['APP_ID'] self.app_secret = app_secret if app_secret else WX_OPEN_CONFIG[ 'APP_SECRET'] self.token = token if token else WX_OPEN_CONFIG['TOKEN'] self.encoding_aes_key = encoding_aes_key if encoding_aes_key else WX_OPEN_CONFIG[ 'ENCODING_AES_KEY'] # 加解密能力 if self.encoding_aes_key: self.crypt = WXBizMsgCrypt(self.token, self.encoding_aes_key, self.app_id) # 会在 check_signature 方法中赋值 self.signature = None self.timestamp = None self.nonce = None WX_OPEN_CONFIG['APP_ID'] = self.app_id WX_OPEN_CONFIG['APP_SECRET'] = self.app_secret WX_OPEN_CONFIG['TOKEN'] = self.token WX_OPEN_CONFIG['ENCODING_AES_KEY'] = self.encoding_aes_key # 启动计划任务 task.task_client_start()
def __init__(self, component_app_id, component_app_secret, component_token, component_encoding_aes_key): self.component_app_id = component_app_id if component_app_id else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_ID'] self.component_app_secret = component_app_secret if component_app_secret else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_SECRET'] self.component_token = component_token if component_token else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_TOKEN'] self.component_encoding_aes_key = component_encoding_aes_key if component_encoding_aes_key else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_ENCODING_AES_KEY'] # 加解密能力 self.crypt = WXBizMsgCrypt(self.component_token, self.component_encoding_aes_key, self.component_app_id) # API 路由能力 self.router = WeChatThirdPlatformRouter(self.component_app_id, self.component_app_secret, self.component_access_token) # 会在 check_signature 方法中赋值 self.signature = None self.timestamp = None self.nonce = None WX_OPEN_THIRD_CONFIG['COMPONENT_APP_ID'] = self.component_app_id WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_SECRET'] = self.component_app_secret WX_OPEN_THIRD_CONFIG['COMPONENT_TOKEN'] = self.component_token WX_OPEN_THIRD_CONFIG[ 'COMPONENT_ENCODING_AES_KEY'] = self.component_encoding_aes_key # 启动计划任务 task.task_third_start()
def get_request(request): """开启回调模式,微信服务器会发送http-GET给企业的URL并验证. 企业号将发送GET请求到填写的URL上,GET请求携带四个参数,企业在获取时需要做urldecode处理. 企业通过参数msg_signature对请求进行校验,如果确认此次GET请求来自企业号,那么企业应该对echostr参数解密并原样返回echostr明文. (不能加引号,不能带bom头,不能带换行符),则接入验证生效,回调模式才能开启。 """ wxbmc = WXBizMsgCrypt(TOKEN, EncodingAESKey, CORPID) msg_signature = request.GET.get('msg_signature') timestamp = request.GET.get('timestamp') nonce = request.GET.get('nonce') # echostr首次校验时必带 echostr = request.GET.get('echostr') ret, reply_echostr = wxbmc.VerifyURL(msg_signature, timestamp, nonce, echostr) if ret != 0: return ret return reply_echostr
def wx_message(): req = request.args signature = req.get('signature', '') timestamp = req.get('timestamp', '') nonce = req.get('nonce', '') if not check_signature(signature, timestamp, nonce): logging.warning("check signature fail") return '' logging.debug("req args:%s", req) logging.debug("data:%s", request.data) msg_signature = req.get('msg_signature') if not msg_signature: logging.warning("msg signature is None") return '' try: data = Parse.parse(request.data) encrypted_data = data.get("Encrypt") wx_crypt = WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, APPID) r, xml = wx_crypt.DecryptTicket(request.data, msg_signature, timestamp, nonce) logging.debug("ret:%s xml:%s", r, xml) if r == 0 and xml: data = Parse.parse(xml) except Exception, e: logging.error("exception:%s", e) wx_crypt = WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, APPID) r, xml = wx_crypt.DecryptTicket(request.data, msg_signature, timestamp, nonce) logging.debug("ret:%s xml:%s", r, xml) data = Parse.parse(xml)
def render(self): xml_response = xmltodict.unparse(self.dict()) if WX_OPEN_CONFIG['ENCODING_AES_KEY']: encrypt = WXBizMsgCrypt(WX_OPEN_CONFIG['token'], WX_OPEN_CONFIG['ENCODING_AES_KEY'], WX_OPEN_CONFIG['APP_ID']) ret, xml_response = encrypt.EncryptMsg(xml_response, self._get_nonce()) return xml_response
import urllib2 from werkzeug.contrib.cache import SimpleCache # from wechatpy.enterprise import create_reply, parse_message # from wechatpy.enterprise.crypto import WeChatCrypto app = Flask(__name__) # 假设企业在企业微信后台上设置的参数如下 sAgent_id = "1000002" sCorpID = "ww532b5887083931ac" sToken = "hJqcu3uJ9Tn2gXPmxx2w9kkCkCE2EPYo" sEncodingAESKey = "6qkdMrq68nTKduznJYO1A37W2oEgpkMUvkttRToqhUt" sSecret = "S0zjZTT7lnk48cC5y6PWVXpZJVKtGly2c1zJt5Fm1Rs" wxcpt = WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID) cache = SimpleCache() def getToken(): params_dic = {'corpid': sCorpID, 'corpsecret': sSecret} url_params = urllib.urlencode(params_dic) url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + url_params try: req_obj = urllib2.Request(url) except KeyError: raise KeyError result = urllib2.urlopen(req_obj) access_token_dic = json.load(result) return access_token_dic['access_token'], access_token_dic['expires_in']
def receive(wx_appid): db = g._db rds = g.im_rds req = request.args signature = req.get('signature', '') timestamp = req.get('timestamp', '') nonce = req.get('nonce', '') if not check_signature(signature, timestamp, nonce): logging.warning("check signature fail") return '' msg_signature = req.get('msg_signature') if not msg_signature: logging.warning("msg signature is None") return '' wx_crypt = WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, APPID) r, xml = wx_crypt.DecryptMsg(request.data, msg_signature, timestamp, nonce) logging.debug("receive wx message:%s %s %s", wx_appid, r, xml) data = Parse.parse(xml) msg_type = data.get('MsgType') # 接收到事件 if msg_type == 'event': logging.debug("event:%s", data.get('Event')) event = data.get('Event') session = data.get('SessionFrom') openid = data.get('FromUserName') gh_id = data.get('ToUserName') if event == 'user_enter_tempsession' and session: #设置来自小程序的微信用户名 u = get_user(rds, db, gh_id, openid) if u: logging.debug("set user name:%s %s %s", gh_id, openid, session) WXUser.set_user_name(rds, u.appid, u.uid, session) return '' # 接收到其他消息 else: openid = data.get('FromUserName') gh_id = data.get('ToUserName') # 消息格式检查 if msg_type not in ('text', 'image', 'voice', 'location'): msg = Reply.text(openid, gh_id, '该消息格式不支持, 目前只支持文本,图片,语音,位置') _, m = wx_crypt.EncryptMsg(msg, nonce, timestamp) return m logging.debug("msg:%s %s %s", openid, gh_id, msg_type) u = get_user(rds, db, gh_id, openid) logging.debug("appid:%s uid:%s store id:%s seller id:%s", u.appid, u.uid, u.store_id, u.seller_id) if msg_type == 'text': content = data.get('Content') logging.debug("text content:%s", content) obj = {"text": content} elif msg_type == 'location': x = data.get("Location_X") y = data.get("Location_Y") obj = {"location": {"latitude": x, "longitude": y}} elif msg_type == 'image': access_token = get_access_token(rds, db, u.wx_appid) if not access_token: logging.error("can't get access token") return '' mp = WXMPAPI(access_token) media = mp.get_media(data.get("MediaId")) try: width, height = get_image_size(media) except UnknownImageFormat, e: width = 0 height = 0 ext = ".jpg" name = md5.new(media).hexdigest() path = "/images/" + name + ext r = FS.upload(path, media) if not r: logging.error("fs upload image error") return '' url = config.IM_URL + "/images/" + name + ext image2 = {"url": url, "width": width, "height": height} obj = {"image": url, "image2": image2} logging.debug("image url:%s width:%s height:%s", url, width, height) elif msg_type == 'voice': access_token = get_access_token(rds, db, u.wx_appid) if not access_token: logging.error("can't get access token") return '' mp = WXMPAPI(access_token) media = mp.get_media(data.get("MediaId")) md5_value = md5.new(media).hexdigest() path = "/audios/" + md5_value r = FS.upload(path, media) if not r: logging.error("fs upload audio error") return '' duration = amr_duration(media) url = config.IM_URL + "/audios/" + md5_value obj = {"audio": {"url": url, "duration": duration}}
def post_request(request): """使用回调模式,用户通过微信企业号给企业的URL通过http-POST发送消息,企业对此进行回复. 企业号在回调企业URL时,会对消息体本身做AES加密,以XML格式POST到企业应用的URL上; 企业在被动响应时,也需要对数据加密,以XML格式返回给微信。 """ wxbmc = WXBizMsgCrypt(TOKEN, EncodingAESKey, CORPID) postdata = request.body msg_signature = request.GET.get('msg_signature') timestamp = request.GET.get('timestamp') nonce = request.GET.get('nonce') ret, xml_content = wxbmc.DecryptMsg(postdata, msg_signature, timestamp, nonce) if ret != 0: return ret request_map = MessageUtil.parse_xml(xml_content) receive_basic_object = BasicReceive(request_map) MsgType = receive_basic_object.MsgType ToUserName = receive_basic_object.ToUserName FromUserName = receive_basic_object.FromUserName # Response to different request message type. if MsgType == REC_MESSAGE_TYPE_TEXT: receive_text_object = TextMsg(request_map) receive_content = receive_text_object.Content response_content = handle_tuling_robot(receive_content, FromUserName) if not response_content: response_content = u"Useless text!" response_text_object = Text(FromUserName, ToUserName, response_content) ret, encryptmsg = wxbmc.EncryptMsg(response_text_object.response(), nonce, timestamp) if ret != 0: return BasicResponse().response() return encryptmsg elif MsgType == REC_MESSAGE_TYPE_IMAGE: receive_image_object = ImageMsg(request_map) PicUrl = receive_image_object.PicUrl response_content = handle_how_old(PicUrl) if not response_content: response_content = u"Useless image!" response_image_object = Text(FromUserName, ToUserName, response_content) ret, encryptmsg = wxbmc.EncryptMsg( response_image_object.response(), nonce, timestamp) if ret != 0: return BasicResponse().response() return encryptmsg elif MsgType == REC_MESSAGE_TYPE_VOICE: return BasicResponse().response() elif MsgType == REC_MESSAGE_TYPE_VIDEO: return BasicResponse().response() elif MsgType == REC_MESSAGE_TYPE_SHORTVIDEO: return BasicResponse().response() elif MsgType == REC_MESSAGE_TYPE_LOCATION: return BasicResponse().response() elif MsgType == REC_MESSAGE_TYPE_LINK: return BasicResponse().response() elif MsgType == REC_MESSAGE_TYPE_EVENT: Event = request_map.get(u'Event') if Event == EVENT_SUBSCRIBE: return BasicResponse().response() elif Event == EVENT_UNSUBSCRIBE: return BasicResponse().response() else: return BasicResponse.response() else: return BasicResponse().response()
class WeChatClient: """ 微信公众号 """ def __init__(self, app_id, app_secret, token, encoding_aes_key=None): self.app_id = app_id if app_id else WX_OPEN_CONFIG['APP_ID'] self.app_secret = app_secret if app_secret else WX_OPEN_CONFIG[ 'APP_SECRET'] self.token = token if token else WX_OPEN_CONFIG['TOKEN'] self.encoding_aes_key = encoding_aes_key if encoding_aes_key else WX_OPEN_CONFIG[ 'ENCODING_AES_KEY'] # 加解密能力 if self.encoding_aes_key: self.crypt = WXBizMsgCrypt(self.token, self.encoding_aes_key, self.app_id) # 会在 check_signature 方法中赋值 self.signature = None self.timestamp = None self.nonce = None WX_OPEN_CONFIG['APP_ID'] = self.app_id WX_OPEN_CONFIG['APP_SECRET'] = self.app_secret WX_OPEN_CONFIG['TOKEN'] = self.token WX_OPEN_CONFIG['ENCODING_AES_KEY'] = self.encoding_aes_key # 启动计划任务 task.task_client_start() def process(self, raw_data): """ 消息处理 :param raw_data: 原生微信返回的数据格式 :return: """ # 解密消息 if self.encoding_aes_key: ret, raw_data = self.crypt.DecryptMsg(raw_data, self.signature, self.timestamp, self.nonce) message = parse_message(raw_data) if message.msg_type == 'event': if funcs.get(message.event.lower()): return funcs.get(message.event.lower())(message) else: if funcs.get(message.msg_type.lower()): return funcs.get(message.msg_type.lower())(message) print(UnimplementedMsgMethodException(method=message.msg_type)) return SuccessReply().render() def check_signature(self, signature, timestamp, nonce): """ 签名校验 :param signature: 微信加密签名 :param timestamp: 时间戳 :param nonce: 随机数 :return: """ # 校验参数 if not all([signature, timestamp, nonce]): raise MissingParametersException() # 计算签名 li = [self.token, timestamp, nonce] li.sort() # 字典排序 tmp_str = ''.join(li) # 拼接字符串 _signature = hashlib.sha1(tmp_str.encode('utf-8')).hexdigest() # 与微信服务器进行签名对比 if _signature != signature: raise InvalidSignatureException() self.signature = signature self.timestamp = timestamp self.nonce = nonce return True @property def access_token(self): return memory_cache.get(constants.ACCESS_TOKEN)
class WeChatThirdClient: """ 微信第三方平台 """ def __init__(self, component_app_id, component_app_secret, component_token, component_encoding_aes_key): self.component_app_id = component_app_id if component_app_id else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_ID'] self.component_app_secret = component_app_secret if component_app_secret else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_SECRET'] self.component_token = component_token if component_token else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_TOKEN'] self.component_encoding_aes_key = component_encoding_aes_key if component_encoding_aes_key else WX_OPEN_THIRD_CONFIG[ 'COMPONENT_ENCODING_AES_KEY'] # 加解密能力 self.crypt = WXBizMsgCrypt(self.component_token, self.component_encoding_aes_key, self.component_app_id) # API 路由能力 self.router = WeChatThirdPlatformRouter(self.component_app_id, self.component_app_secret, self.component_access_token) # 会在 check_signature 方法中赋值 self.signature = None self.timestamp = None self.nonce = None WX_OPEN_THIRD_CONFIG['COMPONENT_APP_ID'] = self.component_app_id WX_OPEN_THIRD_CONFIG[ 'COMPONENT_APP_SECRET'] = self.component_app_secret WX_OPEN_THIRD_CONFIG['COMPONENT_TOKEN'] = self.component_token WX_OPEN_THIRD_CONFIG[ 'COMPONENT_ENCODING_AES_KEY'] = self.component_encoding_aes_key # 启动计划任务 task.task_third_start() def process(self, raw_data): """ 消息处理 :param raw_data: 原生微信返回的数据格式 :return: """ # 解密消息 ret, raw_data = self.crypt.DecryptMsg(raw_data, self.signature, self.timestamp, self.nonce) # 存储 ComponentVerifyTicket if raw_data['InfoType'] == 'component_verify_ticket': memory_cache.set(constants.COMPONENT_VERIFY_TICKET, raw_data['ComponentVerifyTicket']) return SuccessReply().render() message = parse_message(raw_data) to_user_name = message.to_user_name content = message.content if message.msg_type == 'event': # 全网发布检测(事件级别) if to_user_name == constants.TEST_WHOLE_NETWORK_PUBLISH_USERNAME: event_type = message.event content = event_type + 'from_callback' return TextReply(message, content).render() # 事件消息注册 if funcs.get(message.event.lower()): return funcs.get(message.event.lower())(message) else: # 全网发布检测(消息级别) if to_user_name == constants.TEST_WHOLE_NETWORK_PUBLISH_USERNAME: if message.msg_type.lower() == 'text': # 测试公众号处理用户消息 if content == 'TESTCOMPONENT_MSG_TYPE_TEXT': content += '_callback' return TextReply(message, content).render() # 测试公众号使用客服消息接口处理用户消息 elif content.startswith('QUERY_AUTH_CODE:'): query_auth_code = content.split(":")[1] result = self.router.api_query_auth(query_auth_code) authorizer_access_token = result['authorization_info'][ 'authorizer_access_token'] from_user_name = message.from_user_name content = query_auth_code + '_from_api' from client.request import CustomerRequest return CustomerRequest( authorizer_access_token).send_text( from_user_name, content) # 常规消息注册 if funcs.get(message.msg_type.lower()): return funcs.get(message.msg_type.lower())(message) print(UnimplementedMsgMethodException(method=message.msg_type)) return SuccessReply().render() def check_signature(self, signature, timestamp, nonce): """ 签名校验 :param signature: 微信加密签名 :param timestamp: 时间戳 :param nonce: 随机数 :return: """ # 校验参数 if not all([signature, timestamp, nonce]): raise MissingParametersException() # 计算签名 li = [self.component_token, timestamp, nonce] li.sort() # 字典排序 tmp_str = ''.join(li) # 拼接字符串 _signature = hashlib.sha1(tmp_str.encode('utf-8')).hexdigest() # 与微信服务器进行签名对比 if _signature != signature: raise InvalidSignatureException() self.signature = signature self.timestamp = timestamp self.nonce = nonce return True @property def component_access_token(self): return memory_cache.get(constants.COMPONENT_ACCESS_TOKEN)