diff --git a/fir_ser/api/views/thirdlogin.py b/fir_ser/api/views/thirdlogin.py index 3564d1d..5da29a4 100644 --- a/fir_ser/api/views/thirdlogin.py +++ b/fir_ser/api/views/thirdlogin.py @@ -52,10 +52,10 @@ def reply_login_msg(rec_msg, to_user, from_user, ): 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) + WxTemplateMsg(to_user, wx_user_obj.nickname).login_success_msg(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', '')) + WxTemplateMsg(to_user, wx_user_info.get('nickname', '')).login_failed_msg() if wx_ticket_info and wx_ticket_info.get('ip_addr'): ip_addr = wx_ticket_info.get('ip_addr') @@ -89,19 +89,19 @@ def wx_bind_utils(rec_msg, to_user, from_user, content): wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first() user_obj = UserInfo.objects.filter(uid=uid).first() if wx_user_obj: + wx_template_msg_obj = WxTemplateMsg(to_user, wx_user_obj.nickname) 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) + wx_template_msg_obj.bind_success_msg(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) + wx_template_msg_obj.bind_failed_msg(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) + WxTemplateMsg(to_user, wx_user_info.get('nickname', '')).bind_success_msg(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) @@ -174,26 +174,27 @@ class ValidWxChatToken(APIView): 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) + WxTemplateMsg(to_user, wx_user_obj.nickname).bind_query_success_msg(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) + WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg( + "查询登录绑定", 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) + WxTemplateMsg(to_user, wx_user_obj.nickname).unbind_success_msg( + 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) + WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg( + "解除登录绑定", content) logger.info(f"to_user:{to_user} from_user:{from_user} reply msg: {content}") reply_msg = reply.TextMsg(to_user, from_user, content) diff --git a/fir_ser/common/base/magic.py b/fir_ser/common/base/magic.py index 76f386b..f2203bb 100644 --- a/fir_ser/common/base/magic.py +++ b/fir_ser/common/base/magic.py @@ -3,6 +3,7 @@ # project: 12月 # author: NinEveN # date: 2021/12/22 +import datetime import logging import time from functools import wraps @@ -72,3 +73,45 @@ def magic_wrapper(func, *args, **kwargs): return func(*args, **kwargs) return wrapper + + +def magic_notify(notify_rules, timeout=30 * 24 * 60 * 60): + """ + :param notify_rules: + :param timeout: + :return: + """ + now_time = datetime.datetime.now().date() + for notify_rule in notify_rules: + notify_cache = notify_rule['cache'] + if notify_rule['func'](): + notify_data = notify_cache.get_storage_cache() + if notify_data is None: + notify_cache.set_storage_cache([now_time + datetime.timedelta(days=i) for i in notify_rule['notify']], + timeout) + magic_notify(notify_rules) + elif isinstance(notify_data, list): + if len(notify_data) == 0: + return + else: + notify_data.append(now_time) + notify_data.sort() + is_today = False + if notify_data[0] == notify_data[1]: + is_today = True + notify_data = list(set(notify_data)) + notify_data.sort() + n_index = notify_data.index(now_time) + if n_index == 0 and not is_today: + return + notify_data = notify_data[n_index + 1:] + + for func in notify_rule['notify_func']: + try: + func() + except Exception as e: + logger.error(f'func {func.__name__} exec failed Exception:{e}') + notify_cache.set_storage_cache(notify_data, timeout) + + else: + notify_cache.del_storage_cache() diff --git a/fir_ser/common/cache/storage.py b/fir_ser/common/cache/storage.py index 71e9052..e66b547 100644 --- a/fir_ser/common/cache/storage.py +++ b/fir_ser/common/cache/storage.py @@ -227,3 +227,9 @@ class WxLoginBindCache(RedisCacheBase): def __init__(self, unique_key): self.cache_key = f"{CACHE_KEY_TEMPLATE.get('wx_login_bind_key')}_{unique_key}" super().__init__(self.cache_key) + + +class NotifyLoopCache(RedisCacheBase): + def __init__(self, uid, unique_key): + self.cache_key = f"{CACHE_KEY_TEMPLATE.get('notify_loop_msg_key')}_{uid}_{unique_key}" + super().__init__(self.cache_key) diff --git a/fir_ser/common/libs/mp/wechat.py b/fir_ser/common/libs/mp/wechat.py index 7dd2d10..bb9504b 100644 --- a/fir_ser/common/libs/mp/wechat.py +++ b/fir_ser/common/libs/mp/wechat.py @@ -174,12 +174,16 @@ class WxMsgCrypt(WxMsgCryptBase): class WxTemplateMsg(object): - def send_msg(self, to_user, template_id, content): + def __init__(self, to_user, wx_nick_name): + self.to_user = to_user + self.wx_nick_name = wx_nick_name + + def send_msg(self, template_id, content): if not Config.THIRDLOGINCONF.get('active'): return False, f'weixin status is disabled' msg_uri = f'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={get_wx_access_token_cache()}' data = { - "touser": to_user, + "touser": self.to_user, "template_id": template_id, # "url": "http://weixin.qq.com/download", "topcolor": "#FF0000", @@ -187,11 +191,11 @@ class WxTemplateMsg(object): } req = requests.post(msg_uri, json=data) if req.status_code == 200: - return True, format_req_json(req.json(), self.send_msg, to_user, template_id, content) + return True, format_req_json(req.json(), self.send_msg, self.to_user, template_id, content) logger.error(f"send msg from openid failed {req.status_code} {req.text}") return False, req.text - def login_success_msg(self, to_user, wx_nick_name, username): + def login_success_msg(self, username): """ 您进行了微信扫一扫登录操作 系统帐号:yin.xiaogang @@ -207,7 +211,7 @@ class WxTemplateMsg(object): msg_id = 'EJhBbxJvHdWnwwexaqb0lCC2sM7D7WMex5-yJvTL5sU' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”进行了网站登录操作", + "value": f"您的微信账户“{self.wx_nick_name}”进行了网站登录操作", "color": "#173177" }, "keyword1": { @@ -227,14 +231,14 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def login_failed_msg(self, to_user, wx_nick_name): + def login_failed_msg(self): now_time = get_format_time() msg_id = '9rChJFw6nR0Wbp7SXsImh99qm6Dj1hrRWJo1NpEJ_3g' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”登录失败", + "value": f"您的微信账户“{self.wx_nick_name}”登录失败", "color": "#173177" }, "keyword1": { @@ -250,14 +254,14 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def bind_success_msg(self, to_user, wx_nick_name, username): + def bind_success_msg(self, username): now_time = get_format_time() msg_id = 'twMMQn9AKZKevbZBYh8EcFMk7BnC5Y09FmDkZQEH43w' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”绑定成功", + "value": f"您的微信账户“{self.wx_nick_name}”绑定成功", "color": "#173177" }, "keyword1": { @@ -273,14 +277,14 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def bind_failed_msg(self, to_user, wx_nick_name, msg): + def bind_failed_msg(self, msg): now_time = get_format_time() msg_id = 'WIrRuHiDG0f976seBAmY-rjSil0AiT9E5l0PHrPnsfs' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”绑定失败", + "value": f"您的微信账户“{self.wx_nick_name}”绑定失败", "color": "#173177" }, "keyword1": { @@ -296,14 +300,14 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def unbind_success_msg(self, to_user, wx_nick_name, username): + def unbind_success_msg(self, username): now_time = get_format_time() msg_id = 'RabYMg8-jPGhonk957asbW17iLHSLp8BfEXnyesRZ60' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”已经解除绑定", + "value": f"您的微信账户“{self.wx_nick_name}”已经解除绑定", "color": "#173177" }, "keyword1": { @@ -323,13 +327,13 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def bind_query_success_msg(self, to_user, wx_nick_name, username, name, mobile, email): + def bind_query_success_msg(self, username, name, mobile, email): msg_id = 'yU15jLNSULagJTff01X67mDtDytBSs3iBpOBi8c7dvs' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}”绑定信息结果", + "value": f"您的微信账户“{self.wx_nick_name}”绑定信息结果", "color": "#173177" }, "keyword1": { @@ -353,18 +357,18 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def query_bind_info_failed_msg(self, to_user, wx_nick_name, action_msg, failed_msg): + def query_bind_info_failed_msg(self, action_msg, failed_msg): now_time = get_format_time() msg_id = 'uCxjYt216zRAv_sPZihKk4xp7-6pLmRW1oNLLW7L3oI' content_data = { "first": { - "value": f"您的微信账户“{wx_nick_name}” {action_msg}失败了", + "value": f"您的微信账户“{self.wx_nick_name}” {action_msg}失败了", "color": "#173177" }, "keyword1": { - "value": wx_nick_name, + "value": self.wx_nick_name, "color": "#173177" }, "keyword2": { @@ -384,13 +388,13 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) - def auth_code_msg(self, to_user, wx_nick_name, code, expire_date): + def auth_code_msg(self, code, expire_date): msg_id = 'vRCegZatP18LAe9ytLirwfL1CFyzaCQwM89hMAKsUAA' content_data = { "first": { - "value": f"您好,“{wx_nick_name}”", + "value": f"您好,“{self.wx_nick_name}”", "color": "#173177" }, "keyword1": { @@ -406,7 +410,97 @@ class WxTemplateMsg(object): "color": "#173177" }, } - return self.send_msg(to_user, msg_id, content_data) + return self.send_msg(msg_id, content_data) + + def something_not_enough_msg(self, title, username, balance_msg, desc_msg): + msg_id = '34UZQuncRei2t6kRN6k4FGRiwzxU8GyvufnrV3hqEHI' + content_data = { + "first": { + "value": title, + "color": "#173177" + }, + "keyword1": { + "value": username, + "color": "#173177" + }, + "keyword2": { + "value": balance_msg, + "color": "#173177" + }, + "remark": { + "value": desc_msg, + "color": "#173177" + }, + } + return self.send_msg(msg_id, content_data) + + def download_times_not_enough_msg(self, username, download_times, desc_msg="感谢您的关注"): + return self.something_not_enough_msg(f"您好,“{self.wx_nick_name}”,您当前账户下载次数不足,望您尽快充值!", username, + f"{download_times} 下载次数", desc_msg) + + def apple_developer_devices_not_enough_msg(self, username, devices_count, desc_msg="感谢您的关注"): + return self.something_not_enough_msg(f"您好,“{self.wx_nick_name}”,您当前账户签名余额不足,望您尽快添加!", username, + f"{devices_count} 设备数", desc_msg) + + def cert_expired_msg(self, developer_id, cert_id, expired_time): + msg_id = '59sF_30TZ3gB6ugE7BHzv2-LDBnh_3cOn6R-85bvZ0E' + content_data = { + "first": { + "value": f'你好,“{self.wx_nick_name}“,您苹果开发者证书即将到期', + "color": "#173177" + }, + "keyword1": { + "value": developer_id, + "color": "#173177" + }, + "keyword2": { + "value": cert_id, + "color": "#173177" + }, + "keyword3": { + "value": expired_time, + "color": "#173177" + }, + "remark": { + "value": "为了保证您开发者可用,请您尽快更新开发者证书,感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(msg_id, content_data) + + def pay_success_msg(self, product_name, price, pay_type, pay_time, order, description): + msg_id = 'LjlbeavVnGk5j2BhSblPudt_ts3gw9b_ydXS_C1uW6g' + content_data = { + "first": { + "value": f'你好,“{self.wx_nick_name}“,下载次数充值成功', + "color": "#173177" + }, + "keyword1": { + "value": product_name, + "color": "#173177" + }, + "keyword2": { + "value": price, + "color": "#173177" + }, + "keyword3": { + "value": pay_type, + "color": "#173177" + }, + "keyword4": { + "value": pay_time, + "color": "#173177" + }, + "keyword5": { + "value": order, + "color": "#173177" + }, + "remark": { + "value": f"{description},感谢您的关注", + "color": "#173177" + }, + } + return self.send_msg(msg_id, content_data) class WxWebLogin(object): diff --git a/fir_ser/config.py b/fir_ser/config.py index 92c0c5d..bed1f21 100644 --- a/fir_ser/config.py +++ b/fir_ser/config.py @@ -251,7 +251,7 @@ class IPACONF(object): # 'http': '47.243.172.202:17897', # 'https': '47.243.172.202:17897' } - APPLE_DEVELOPER_API_TIMEOUT = 120 # 访问苹果api超时时间,默认3分钟 + APPLE_DEVELOPER_API_TIMEOUT = 60 # 访问苹果api超时时间,默认3分钟 MOBILE_CONFIG_SIGN_SSL = { # 描述文件是否签名,默认是关闭状态;如果开启,并且ssl_key_path 和 ssl_pem_path 正常,则使用填写的ssl进行签名,否则默认不签名 'open': True, diff --git a/fir_ser/fir_ser/settings.py b/fir_ser/fir_ser/settings.py index d13aa16..ad4dad7 100644 --- a/fir_ser/fir_ser/settings.py +++ b/fir_ser/fir_ser/settings.py @@ -263,6 +263,7 @@ CACHE_KEY_TEMPLATE = { 'task_state_key': 'task_state', 'pending_state_key': 'pending_state', 'wx_login_bind_key': 'wx_login_bind', + 'notify_loop_msg_key': 'notify_loop_msg', 'user_can_download_key': 'user_can_download', 'download_times_key': 'app_download_times', 'make_token_key': 'make_token', diff --git a/fir_ser/xsign/utils/supersignutils.py b/fir_ser/xsign/utils/supersignutils.py index 4bc2b24..02585f8 100644 --- a/fir_ser/xsign/utils/supersignutils.py +++ b/fir_ser/xsign/utils/supersignutils.py @@ -694,7 +694,7 @@ class IosUtils(object): if device_obj.status not in ['ENABLED', 'DISABLED']: developer_obj.status = 5 developer_obj.save(update_fields=['status']) - return False, f'device status unexpected. device_obj:{device_obj}' + return False, f'issuer_id:{developer_obj.issuer_id} device status unexpected. device_obj:{device_obj}' return True, '' @staticmethod @@ -1179,19 +1179,29 @@ class IosUtils(object): return status, result @staticmethod - def get_device_from_developer(developer_obj): + def get_device_from_developer(developer_obj, err_return=False): app_api_obj = get_api_obj(developer_obj) status, result = app_api_obj.get_device() - # 获取设备列表的时候,有时候会发生灵异事件,尝试多次获取 【也可能是未知bug导致】 - if status and isinstance(result, list) and len(result) == 0: + udid_developer_obj_list = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).values_list('udid') + udid_developer_list = [x[0] for x in udid_developer_obj_list if len(x) > 0] + udid_result_list = [] + if status: + udid_result_list = [device.udid for device in result] + + udid_same = set(udid_result_list) & set(udid_developer_list) + same_p = (len(udid_same) / len(udid_result_list) + len(udid_same) / len(udid_developer_list)) / 2 + # 获取设备列表的时候,有时候会发生灵异事件,尝试多次获取 【也可能是未知bug导致】【已经存在,获取的数据并不是该用户数据】 + if status and ((isinstance(result, list) and len(result) == 0) or same_p < 0.8): time.sleep(2) + logger.warning(f'{developer_obj.issuer_id} get device may be wrong. try again. online result {result}') + logger.warning(f'{developer_obj.issuer_id} get device may be wrong. db result {udid_developer_list}') status, result = app_api_obj.get_device() if status and developer_obj.issuer_id: - IosUtils.check_device_status(developer_obj, result) + status1, msg = IosUtils.check_device_status(developer_obj, result) + if not status1 and err_return: + return status1, {'return_info': msg} - udid_developer_obj_list = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).values_list('udid') - udid_developer_list = [x[0] for x in udid_developer_obj_list if len(x) > 0] udid_result_list = [device.udid for device in result] udid_enabled_result_list = [device.udid for device in result if device.status == 'ENABLED']