微信公众号模板消息推送(附上完整代码)

官方文档

会用到的调用函数


import logging
import requests
import time
from pickle import dumps, loads

from requests.compat import json as _json

from django.conf import settings

from werobot.client import Client
from redis.client import StrictRedis

redis = StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB,
        max_connections=settings.REDIS_MAX_CONNECTIONS, password=settings.REDIS_PASSWORD)
# 验证返回值函数
class ClientException(Exception):
    pass
def check_error(json):
    """
    检测微信公众平台返回值中是否包含错误的返回码。
    如果返回码提示有错误,抛出一个 :class:`ClientException` 异常。否则返回 True 。
    """
    if "errcode" in json and json["errcode"] != 0:
        raise ClientException("{}: {}".format(json["errcode"], json["errmsg"]))
    return json


class Wechatopen(object):
    '''
    存储access_token

    '''
    # 定义redis的key
    key = 'access_token_hash'
    def __init__(self):
        self._token = None
        self.token_expires_at = None
        self.appid = settings.WECHAT_APP_ID
        self.appsecret = settings.WECHAT_APP_SECRET

    def request(self, method, url, **kwargs):
        if "params" not in kwargs:
            kwargs["params"] = {"access_token": self.token}
        if isinstance(kwargs.get("data", ""), dict):
            body = _json.dumps(kwargs["data"], ensure_ascii=False)
            body = body.encode('utf8')
            kwargs["data"] = body

        r = requests.request(
            method=method,
            url=url,
            **kwargs
        )
        r.raise_for_status()
        json = r.json()
        if check_error(json):
            return json

    def get(self, url, **kwargs):
        return self.request(
            method="get",
            url=url,
            **kwargs
        )

    def get_access_token(self):
        """
        判断现有的token是否过期。
        用户需要多进程或者多机部署可以手动重写这个函数
        来自定义token的存储,刷新策略。

        :return: 返回token
        """
        # 从redis中取出access_token和过期的额时间,并赋值给self._token和self.token_expires_at
        stream = redis.get(self.key)
        if stream:
            self._token,self.token_expires_at = loads(stream)
        if self._token:
            now = time.time()
            if self.token_expires_at - now > 60:
                return self._token
        # 如果过期的话,重新生成access_token,重置_token和token_expires_at
        json = self.grant_token()
        self._token = json["access_token"]
        self.token_expires_at = int(time.time()) + json["expires_in"]
        # 存入redis
        access_token_val = dumps([self._token,self.token_expires_at])
        redis.set(self.key,access_token_val)
        return self._token

    def grant_token(self):
        """
        获取 Access Token。

        :return: 返回的 JSON 数据包
        """
        result = self.get(
            url="https://api.weixin.qq.com/cgi-bin/token",
            params={
                "grant_type": "client_credential",
                "appid": self.appid,
                "secret": self.appsecret
            }
        )
        return result


class PreWechatOpenAPIClient(Client):
    """
    微信第三方平台调用对应授权过来的API
    """
    def __init__(self, app_id):
        self.app_id = app_id

    # def get_access_token(self):
    #     """
    #     判断现有的token是否过期。
    #     用户需要多进程或者多机部署可以手动重写这个函数
    #     来自定义token的存储,刷新策略。
    #     :return: 返回token
    #     """
    #     return open_client.get_access_token_by_appid(self.app_id)

# shixf  重写
    def get_access_token(self):
        """
        判断现有的token是否过期。
        用户需要多进程或者多机部署可以手动重写这个函数
        来自定义token的存储,刷新策略。

        :return: 返回token
        """
        wechatopen = Wechatopen()
        return wechatopen.get_access_token()

    def add_template(self, short_template_id='OPENTM204599900'):
        """
        获得模板ID
        """
        return self.post(
                url="https://api.weixin.qq.com/cgi-bin/template/api_add_template",
                params={
                    "access_token": self.token
                },
                data={
                    "template_id_short": short_template_id
                }
            )

    # 重写self.token
    @property
    def token(self):
        return self.get_access_token()

    def get_all_private_template(self):
        """
        获取模板列表
        """
        return self.get(
                url="https://api.weixin.qq.com/cgi-bin/template/get_all_private_template",
                params={
                    "access_token": self.token
                }
            )

    def del_template(self, template_id):
        """
        删除模板
        """
        return self.post(
                url="https://api.weixin.qq.com/cgi-bin/template/del_private_template",
                params={
                    "access_token": self.token
                },
                data={
                    "template_id": template_id
                }
            )

    def get_industry(self):
        """
        获取所属行业
        """
        return self.get(
                url='https://api.weixin.qq.com/cgi-bin/template/get_industry',
                params={
                    "access_token": self.token
                }
            )

    def set_industry(self, industry_id1="1", industry_id2="2"):
        """
        设置所属行业
        """
        return self.post(
                url="https://api.weixin.qq.com/cgi-bin/template/api_set_industry",
                params={
                    "access_token": self.token
                },
                data={
                    "industry_id1": industry_id1,
                    "industry_id2": industry_id2
                }
            )

    def get_template_by_name(self, title="使用自己的", short_template_id='使用自己的'):
        """
        根据名称获取模板id
        """
        template_response = self.get_all_private_template()
        template_list = template_response.get("template_list")
        logger.info("template_response: {}".format(template_response))
        for template in template_list:
            template_title = template.get("title")
            if title == template_title:
                return template

        # 如果未添加对应课程模板
        template_id = self.add_template(short_template_id)
        return template_id

要为哪门课程微信推送,传递课程的外键id


def course_notify(self, request, pk=None):

	videoItem = VideoInfo.objects.filter(id = pk).first()
	logger.info("send course_notify: : {}".format(pk))
	logger.info("videoItem: {}".format(videoItem))
	
	if videoItem.start_time < datetime.now():
		raise NotFound(detail='时间超时,这门课程已经过了开课时间,无法提醒')
	
	if not videoItem:
		raise NotFound(detail='课程未找到')
	
	if videoItem.course_notify_date:
	  # 如果当前时间比上一次手动发送微信的提醒时间加上一天时间还小,并且微信通知次数大于等于3此,那么说明一天时间内三次机会已经用完
		if(datetime.now() < videoItem.course_notify_date + timedelta(days=1) ) and (videoItem.course_notify >= 3):
			raise NotFound(detail='您今天只有3次发送提醒的机会, 您已全部用完')
	  # 如果当前时间比上一次手动发送微信的提醒时间加上一天时间大,则将微信通知次数变为0
 		elif datetime.now() > (videoItem.course_notify_date + timedelta(days=1)):
			videoItem.course_notify = 0
	# 如果当前时间比上一次手动发送微信的提醒时间加上一天时间大,并且微信通知次数小于3次,则将微信提醒时间重置为当前时间,将微信通知次数加上1
	date_now = datetime.strptime(datetime.strftime(datetime.now(), "%Y-%m-%d"),"%Y-%m-%d")
	videoItem.course_notify_date = date_now
	videoItem.course_notify = videoItem.course_notify + 1
	videoItem.save()
	
	# 发送模板信息
	tem_course_notify(pk)
	# 异步发送模板信息有错误,待修改
	# course_notify.apply_async(args=[pk])
	
	
	return Response({'result':"success","message": '发送成功'}, status=status.HTTP_200_OK)

发送模板信息

def tem_course_notify(course_id):
    """
    手机短信,微信模板消息提醒
    """
    logger.info("course notify starting {}...".format(course_id))
    video = VideoInfo.objects.filter(id=course_id).select_related().first()
    title = video.title

    enterprise = video.enterprise
    if enterprise.wechat_appid:
        client = PreWechatOpenAPIClient(enterprise.wechat_appid)
    else:
        client = PreWechatOpenAPIClient(settings.WECHAT_APP_ID)
    # config = {
    #    "APP_ID": settings.WECHAT_APP_ID,
    #    "APP_SECRET": settings.WECHAT_APP_SECRET
    #}
    # client = Client(config)

    # 通知全部课程
    apply_user_list = []

    # 特殊情形,
    if settings.PROJECT_NAME == 'learning':
        apply_userinfo_list = UserInfo.objects.all()
        apply_user_list = [apply_user.owner for apply_user in apply_userinfo_list]
    else:
        if video.enterprise.subscribe_url:
            logger.info("all of video")
            all_of_video = VideoInfo.objects.filter(enterprise_id=video.enterprise_id, active=ACTIVE.ACTIVE_ON.value)
            all_of_video_ids = [video.video_id for video in all_of_video]
            logger.info("all of video id: {}".format(all_of_video_ids))
            apply_userinfo_list = MyCourse.objects.filter(course_id__in=all_of_video_ids)

            apply_user_list = [apply_user.user.owner for apply_user in apply_userinfo_list]
        else:
        	# 上面的if else等等乱七八糟都不会用到,直接走这里
            # 得到当前课程报名用户
            apply_userinfo_list = MyCourse.objects.filter(course_id=video.video_id)

            apply_user_list = [apply_user.user.owner for apply_user in apply_userinfo_list]

    # 得到对应代理子帐号公众平台openid
    oauth_list = Oauth.objects.filter(user__in=apply_user_list, enterprise_id=video.enterprise_id).select_related()

    detail_url = reverse("courseIntroduce") + "?id={}&enterprise_id={}".format(course_id, video.enterprise_id)
    url = settings.WECHAT_WEB_DOMAIN + detail_url
    phone_list = []
    logger.info("oauth_list count: {}".format(len(oauth_list)))

    try:
        wechat_template = client.get_template_by_name()
    except Exception as e:
        wechat_template = None
        logger.info("get template by name error: {}".format(str(e)))
    # 获取到模板id
    WECHAT_TEMPLATE_ID = wechat_template.get("template_id") if wechat_template else WECHAT_TEMPLATE_ID
    for oauth in oauth_list:
        phone_list.append(oauth.user.owner.tel)
        # 组织微信所需数据
        data = {
               "first": {
                   "value":"您报名的在线课程马上就要开始了",
                   "color":"#333333"
               },
               "keyword1":{
                   "value": title,
                   "color":"#999999"
               },
               "keyword2": {
                   "value": oauth.user.owner.name,
                   "color":"#999999"
               },
               "remark":{
                   "value":"请点击详情进入课程页面",
                   "color":"#489a5b"
               }
           }

        openid = oauth.openid
        logger.info("openid {}, name:{}, id:{}, title:{}".format(openid, oauth.user.owner.name, course_id, title))
        if openid:
            try:
                response = client.send_template_message(openid, WECHAT_TEMPLATE_ID, data, url=url)
                logger.info("wechat response: {}".format(response))
            except Exception as e:
                logger.info("send template message: {}".format(str(e)))

    logger.info("done...")

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值