#!/usr/bin/env python # -*- coding:utf-8 -*- # project: 3月 # author: NinEveN # date: 2021/3/29 import logging import random from django.http.response import HttpResponse from rest_framework.pagination import PageNumberPagination from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_xml.parsers import XMLParser from api.models import ThirdWeChatUserInfo, UserInfo, UserCertificationInfo from api.utils.auth import ExpiringTokenAuthentication from api.utils.mp.chat import reply, receive from api.utils.mp.wechat import check_signature, WxMsgCrypt, get_userinfo_from_openid, WxTemplateMsg from api.utils.response import BaseResponse from api.utils.serializer import ThirdWxSerializer from api.utils.storage.caches import set_wx_ticket_login_info_cache, get_wx_ticket_login_info_cache from api.views.login import get_login_type from config import WEB_DOMAIN logger = logging.getLogger(__name__) GOOD_XX = [ '您是一位有恒心有毅力的人,我很佩服您!', '越有内涵的人越虚怀若谷,像您这样有内涵的人我十分敬佩!', '你像天上的月亮,也像那闪烁的星星,可惜我不是诗人,否则,当写一万首诗来形容你的美丽!', '据考证,你是世界上最大的宝藏,里面藏满了金子、钻石和名画!', '虽然你没有一簇樱唇两排贝齿,但你的谈吐高雅脱俗,机智过人,令我折服!', '您是一位有恒心有毅力的人,我很佩服您!', '我很荣幸,认识你这样有内涵的漂亮朋友!', '春花秋月,是诗人们歌颂的情景,可是我对于它,却感到十分平凡。只有你嵌着梨涡的笑容,才是我眼中最美的景象!', '你像一片轻柔的云在我眼前飘来飘去,你清丽秀雅的脸上荡漾着春天般美丽的笑容。在你那双又大又亮的眼睛里,我总能捕捉到你的宁静,你的热烈,你的聪颖,你的敏感!', '人生旅程上,您丰富我的心灵,开发我得智力,为我点燃了希望的光芒,谢谢您!', ] class TextXMLParser(XMLParser): media_type = 'text/xml' # 微信解析的是 text/xml def reply_login_msg(rec_msg, to_user, from_user, ): content = f'还未绑定用户,请通过手机或者邮箱登录账户之后进行绑定' u_data_id = -1 wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() wx_ticket_info = get_wx_ticket_login_info_cache(rec_msg.Ticket) if wx_user_obj: u_data_id = wx_user_obj.user_id.pk content = f'用户 {wx_user_obj.user_id.first_name} 登录成功' WxTemplateMsg().login_success_msg(to_user, wx_user_obj.nickname, wx_user_obj.user_id.first_name) else: wx_user_info = update_or_create_wx_userinfo(to_user, None, False) WxTemplateMsg().login_failed_msg(to_user, wx_user_info.get('nickname', '')) if wx_ticket_info and wx_ticket_info.get('ip_addr'): ip_addr = wx_ticket_info.get('ip_addr') logger.info(f"{content} ip:{ip_addr}") set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': u_data_id}) reply_msg = reply.TextMsg(to_user, from_user, content) return reply_msg.send() def update_or_create_wx_userinfo(to_user, user_obj, create=True): code, wx_user_info = get_userinfo_from_openid(to_user) logger.info(f"get openid:{to_user} info:{to_user} code:{code}") if code: wx_user_info = { 'openid': wx_user_info.get('openid'), 'nickname': wx_user_info.get('nickname'), 'sex': wx_user_info.get('sex'), 'subscribe_time': wx_user_info.get('subscribe_time'), 'head_img_url': wx_user_info.get('headimgurl'), 'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}", 'subscribe': wx_user_info.get('subscribe'), } if create: ThirdWeChatUserInfo.objects.update_or_create(user_id=user_obj, openid=to_user, defaults=wx_user_info) return wx_user_info class ValidWxChatToken(APIView): parser_classes = (XMLParser, TextXMLParser) def get(self, request): params = request.query_params return HttpResponse(check_signature(params)) def post(self, request): params = request.query_params data = request.data encrypt_obj = WxMsgCrypt() ret, encrypt_xml = encrypt_obj.decrypt_msg(data, params.get("msg_signature"), params.get("timestamp"), params.get("nonce")) logger.info(f"code:{ret}, result {encrypt_xml}") result = "success" if ret == 0: content = '欢迎使用fly应用分发平台,感谢您的关注' rec_msg = receive.parse_xml(encrypt_xml) if isinstance(rec_msg, receive.Msg): to_user = rec_msg.FromUserName from_user = rec_msg.ToUserName if rec_msg.MsgType == 'text': content = random.choices([*GOOD_XX, content, rec_msg.Content.decode('utf-8')])[0] reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() elif rec_msg.MsgType == 'image': media_id = rec_msg.MediaId reply_msg = reply.ImageMsg(to_user, from_user, media_id) result = reply_msg.send() else: result = reply.Msg().send() logger.info(f"replay msg: {result}") return HttpResponse(result) elif isinstance(rec_msg, receive.EventMsg): to_user = rec_msg.FromUserName from_user = rec_msg.ToUserName if rec_msg.Event == 'CLICK': # 公众号点击事件 if rec_msg.Eventkey == 'good': content = random.choices(GOOD_XX)[0] reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() logger.info(f"replay msg: {result}") return HttpResponse(result) elif rec_msg.Eventkey == 'flyapps': reply_msg = reply.TextMsg(to_user, from_user, WEB_DOMAIN) result = reply_msg.send() logger.info(f"replay msg: {result}") return HttpResponse(result) elif rec_msg.Eventkey in ['query_bind', 'unbind']: wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() if rec_msg.Eventkey == 'query_bind': if wx_user_obj: user_obj = wx_user_obj.user_id content = f'绑定用户 {user_obj.first_name} ' if user_obj.email: content += f' 登录邮箱:{user_obj.email} ' if user_obj.mobile: content += f' 登录手机:{user_obj.mobile}' user_cert_obj = UserCertificationInfo.objects.filter(user_id=user_obj, status=1).first() if user_cert_obj: name = user_cert_obj.name else: name = user_obj.first_name WxTemplateMsg().bind_query_success_msg(to_user, wx_user_obj.nickname, user_obj.first_name, name, user_obj.mobile, user_obj.email) else: content = '暂无登录绑定信息' wx_user_info = update_or_create_wx_userinfo(to_user, None, False) WxTemplateMsg().query_bind_info_failed_msg(to_user, wx_user_info.get('nickname'), "查询登录绑定", content) elif rec_msg.Eventkey == 'unbind': if wx_user_obj: content = f'解绑用户 {wx_user_obj.user_id.first_name} 成功' WxTemplateMsg().unbind_success_msg(to_user, wx_user_obj.nickname, wx_user_obj.user_id.first_name) ThirdWeChatUserInfo.objects.filter(openid=to_user).delete() else: content = f'暂无登录绑定信息' wx_user_info = update_or_create_wx_userinfo(to_user, None, False) WxTemplateMsg().query_bind_info_failed_msg(to_user, wx_user_info.get('nickname'), "解除登录绑定", content) logger.info(f"to_user:{to_user} from_user:{from_user} reply msg: {content}") reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() elif rec_msg.Event in ['subscribe', 'unsubscribe']: # 订阅 reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() if rec_msg.Eventkey == 'qrscene_web.login': # 首次关注,登录认证操作 if rec_msg.Event == 'subscribe': result = reply_login_msg(rec_msg, to_user, from_user, ) if rec_msg.Event == 'unsubscribe': ThirdWeChatUserInfo.objects.filter(openid=to_user).update(subscribe=False) elif rec_msg.Event == 'SCAN': if rec_msg.Eventkey == 'web.login': # 已经关注,然后再次扫码,登录认证操作 result = reply_login_msg(rec_msg, to_user, from_user, ) elif rec_msg.Eventkey.startswith('web.bind.'): uid = rec_msg.Eventkey.split('.')[-1] wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() user_obj = UserInfo.objects.filter(uid=uid).first() if wx_user_obj: if user_obj and user_obj.uid == wx_user_obj.user_id.uid: content = f'账户 {wx_user_obj.user_id.first_name} 已经绑定成功,感谢您的使用' update_or_create_wx_userinfo(to_user, user_obj) WxTemplateMsg().bind_success_msg(to_user, wx_user_obj.nickname, user_obj.first_name) else: content = f'账户已经被 {wx_user_obj.user_id.first_name} 绑定' WxTemplateMsg().bind_failed_msg(to_user, wx_user_obj.nickname, content) else: if user_obj: wx_user_info = update_or_create_wx_userinfo(to_user, user_obj) content = f'账户绑定 {user_obj.first_name} 成功' WxTemplateMsg().bind_success_msg(to_user, wx_user_info.get('nickname', ''), user_obj.first_name) if user_obj: set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk}) reply_msg = reply.TextMsg(to_user, from_user, content) result = reply_msg.send() else: logger.error('密文解密失败') logger.info(f"replay msg: {result}") return HttpResponse("success") class PageNumber(PageNumberPagination): page_size = 10 # 每页显示多少条 page_size_query_param = 'size' # URL中每页显示条数的参数 page_query_param = 'page' # URL中页码的参数 max_page_size = None # 最大页码数限制 class ThirdWxAccount(APIView): authentication_classes = [ExpiringTokenAuthentication, ] def get(self, request): res = BaseResponse() if get_login_type().get('third', '').get('wxp'): # wx_open_id = request.query_params.get("openid", None) wx_obj_lists = ThirdWeChatUserInfo.objects.filter(user_id=request.user) # if wx_open_id: # wx_obj_lists = wx_obj_lists.filter(openid=wx_open_id) page_obj = PageNumber() info_serializer = page_obj.paginate_queryset(queryset=wx_obj_lists.order_by("-subscribe_time"), request=request, view=self) wx_user_info = ThirdWxSerializer(info_serializer, many=True, ) res.data = wx_user_info.data res.count = wx_obj_lists.count() return Response(res.dict) def delete(self, request): openid = request.query_params.get("openid") user_id = request.query_params.get("user_id") if get_login_type().get('third', '').get('wxp') and openid and user_id: ThirdWeChatUserInfo.objects.filter(user_id_id=user_id, openid=openid).delete() return self.get(request)