# -*- coding: utf-8 -*- # !/usr/bin/env python from __future__ import absolute_import, unicode_literals import logging import random import string import time import requests from library.wechatpy.client import api from library.wechatpy.client.base import BaseWeChatClient from library.wechatpy.utils import WeChatSigner from library.wechatbase.exceptions import WeChatException, WechatNetworkException logger = logging.getLogger(__name__) class WeChatClient(BaseWeChatClient): """ 微信 API 操作类 通过这个类可以操作微信 API,发送主动消息、群发消息和创建自定义菜单等。 """ API_BASE_URL = 'https://api.weixin.qq.com/cgi-bin/' card = api.WeChatCard() customservice = api.WeChatCustomService() datacube = api.WeChatDataCube() device = api.WeChatDevice() group = api.WeChatGroup() invoice = api.WeChatInvoice() material = api.WeChatMaterial() media = api.WeChatMedia() menu = api.WeChatMenu() merchant = api.WeChatMerchant() message = api.WeChatMessage() misc = api.WeChatMisc() poi = api.WeChatPoi() qrcode = api.WeChatQRCode() scan = api.WeChatScan() semantic = api.WeChatSemantic() shakearound = api.WeChatShakeAround() tag = api.WeChatTag() template = api.WeChatTemplate() subscribe = api.WeChatNewtmpl() user = api.WeChatUser() wifi = api.WeChatWiFi() wxa = api.WeChatWxa() marketing = api.WeChatMarketing() def __init__(self, appid, secret, session, timeout=None, auto_retry=True): super(WeChatClient, self).__init__( appid, session, timeout, auto_retry ) self.appid = appid self.secret = secret @property def nonce_str(self): char = string.ascii_letters + string.digits return "".join(random.choice(char) for _ in range(32)) def jsapi_sign(self, url): """ 生成签名给js使用 """ jsapi_ticket = self.jsapi_ticket timestamp = str(int(time.time())) nonce_str = self.nonce_str data = [ 'noncestr={noncestr}'.format(noncestr=nonce_str), 'jsapi_ticket={jsapi_ticket}'.format(jsapi_ticket=jsapi_ticket), 'timestamp={timestamp}'.format(timestamp=timestamp), 'url={url}'.format(url=url), ] signer = WeChatSigner(delimiter=b'&') signer.add_data(*data) return { 'sign': signer.signature, 'timestamp': timestamp, 'noncestr': nonce_str, 'appId': self.appid, 'jsapi_ticket': jsapi_ticket } def refresh_jsapi_ticket(self): """ 获取微信 JS-SDK ticket :return: 返回的 JSON 数据包 """ jsapi_ticket_response = self.get( 'ticket/getticket', params={'type': 'jsapi'} ) ticket = jsapi_ticket_response['ticket'] expires_in = int(jsapi_ticket_response['expires_in']) if expires_in < 600: expires_in = expires_in / 2 else: expires_in = expires_in - 600 self.session.set(self.jsapi_ticket_key, ticket, expires_in) return ticket def refresh_access_token(self): logger.info('Fetching access token for {}'.format(self.appid)) with requests.sessions.Session() as _session: res = _session.get( url='https://api.weixin.qq.com/cgi-bin/token', params={ 'grant_type': 'client_credential', 'appid': self.appid, 'secret': self.secret }, timeout=self.timeout) try: res.raise_for_status() except requests.RequestException as reqe: raise WechatNetworkException( errCode='HTTP{}'.format(res.status_code), errMsg=reqe.message, client=self, request=reqe.request, response=reqe.response ) result = res.json() if 'errcode' in result and result['errcode'] != 0: raise WeChatException( errCode=result['errcode'], errMsg=result['errmsg'], client=self, request=res.request, response=res ) expires_in = 7200 - 600 if 'expires_in' in result: expires_in = result['expires_in'] if expires_in < 600: expires_in = expires_in / 2 else: expires_in = expires_in - 600 self.session.set( self.access_token_key, result['access_token'], expires_in ) return result['access_token'] class WeChatComponentClient(WeChatClient): """ 开放平台代公众号调用客户端 """ def __init__(self, appid, component, session=None, timeout=None): # 未用到secret,所以这里没有 super(WeChatComponentClient, self).__init__(appid, "", session, timeout) self.appid = appid self.component = component def refresh_access_token(self): """ 获取 access token 详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list\ &t=resource/res_list&verify=1&id=open1419318587&token=&lang=zh_CN :return: 返回的 JSON 数据包 """ result = self.component.refresh_authorizer_token(self.appid) expires_in = 7200 - 600 if 'expires_in' in result: expires_in = result['expires_in'] if expires_in < 600: expires_in = expires_in / 2 else: expires_in = expires_in - 600 self.session.set( self.access_token_key, result['authorizer_access_token'], expires_in ) return result['authorizer_access_token']