优化代码,增加余额不足微信模板消息

pull/37/head
nineven 3 years ago
parent 5edf7cd65a
commit 5eca01278b
  1. 31
      fir_ser/api/views/thirdlogin.py
  2. 43
      fir_ser/common/base/magic.py
  3. 6
      fir_ser/common/cache/storage.py
  4. 150
      fir_ser/common/libs/mp/wechat.py
  5. 2
      fir_ser/config.py
  6. 1
      fir_ser/fir_ser/settings.py
  7. 24
      fir_ser/xsign/utils/supersignutils.py

@ -52,10 +52,10 @@ def reply_login_msg(rec_msg, to_user, from_user, ):
if wx_user_obj: if wx_user_obj:
u_data_id = wx_user_obj.user_id.pk u_data_id = wx_user_obj.user_id.pk
content = f'用户 {wx_user_obj.user_id.first_name} 登录成功' 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: else:
wx_user_info = update_or_create_wx_userinfo(to_user, None, False) 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'): if wx_ticket_info and wx_ticket_info.get('ip_addr'):
ip_addr = 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() wx_user_obj = ThirdWeChatUserInfo.objects.filter(openid=to_user).first()
user_obj = UserInfo.objects.filter(uid=uid).first() user_obj = UserInfo.objects.filter(uid=uid).first()
if wx_user_obj: 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: if user_obj and user_obj.uid == wx_user_obj.user_id.uid:
content = f'账户 {wx_user_obj.user_id.first_name} 已经绑定成功,感谢您的使用' content = f'账户 {wx_user_obj.user_id.first_name} 已经绑定成功,感谢您的使用'
update_or_create_wx_userinfo(to_user, user_obj) 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: else:
content = f'账户已经被 {wx_user_obj.user_id.first_name} 绑定' 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: else:
if user_obj: if user_obj:
wx_user_info = update_or_create_wx_userinfo(to_user, user_obj) wx_user_info = update_or_create_wx_userinfo(to_user, user_obj)
content = f'账户绑定 {user_obj.first_name} 成功' content = f'账户绑定 {user_obj.first_name} 成功'
WxTemplateMsg().bind_success_msg(to_user, wx_user_info.get('nickname', ''), WxTemplateMsg(to_user, wx_user_info.get('nickname', '')).bind_success_msg(user_obj.first_name)
user_obj.first_name)
if user_obj: if user_obj:
set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk}) set_wx_ticket_login_info_cache(rec_msg.Ticket, {'pk': user_obj.pk})
reply_msg = reply.TextMsg(to_user, from_user, content) reply_msg = reply.TextMsg(to_user, from_user, content)
@ -174,26 +174,27 @@ class ValidWxChatToken(APIView):
name = user_cert_obj.name name = user_cert_obj.name
else: else:
name = user_obj.first_name name = user_obj.first_name
WxTemplateMsg().bind_query_success_msg(to_user, wx_user_obj.nickname, WxTemplateMsg(to_user, wx_user_obj.nickname).bind_query_success_msg(user_obj.first_name,
user_obj.first_name, name, user_obj.mobile, name,
user_obj.email) user_obj.mobile,
user_obj.email)
else: else:
content = '暂无登录绑定信息' content = '暂无登录绑定信息'
wx_user_info = update_or_create_wx_userinfo(to_user, None, False) 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'), WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg(
"查询登录绑定", content) "查询登录绑定", content)
elif rec_msg.Eventkey == 'unbind': elif rec_msg.Eventkey == 'unbind':
if wx_user_obj: if wx_user_obj:
content = f'解绑用户 {wx_user_obj.user_id.first_name} 成功' content = f'解绑用户 {wx_user_obj.user_id.first_name} 成功'
WxTemplateMsg().unbind_success_msg(to_user, wx_user_obj.nickname, WxTemplateMsg(to_user, wx_user_obj.nickname).unbind_success_msg(
wx_user_obj.user_id.first_name) wx_user_obj.user_id.first_name)
ThirdWeChatUserInfo.objects.filter(openid=to_user).delete() ThirdWeChatUserInfo.objects.filter(openid=to_user).delete()
else: else:
content = f'暂无登录绑定信息' content = f'暂无登录绑定信息'
wx_user_info = update_or_create_wx_userinfo(to_user, None, False) 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'), WxTemplateMsg(to_user, wx_user_info.get('nickname')).query_bind_info_failed_msg(
"解除登录绑定", content) "解除登录绑定", content)
logger.info(f"to_user:{to_user} from_user:{from_user} reply 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) reply_msg = reply.TextMsg(to_user, from_user, content)

@ -3,6 +3,7 @@
# project: 12月 # project: 12月
# author: NinEveN # author: NinEveN
# date: 2021/12/22 # date: 2021/12/22
import datetime
import logging import logging
import time import time
from functools import wraps from functools import wraps
@ -72,3 +73,45 @@ def magic_wrapper(func, *args, **kwargs):
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper 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()

@ -227,3 +227,9 @@ class WxLoginBindCache(RedisCacheBase):
def __init__(self, unique_key): def __init__(self, unique_key):
self.cache_key = f"{CACHE_KEY_TEMPLATE.get('wx_login_bind_key')}_{unique_key}" self.cache_key = f"{CACHE_KEY_TEMPLATE.get('wx_login_bind_key')}_{unique_key}"
super().__init__(self.cache_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)

@ -174,12 +174,16 @@ class WxMsgCrypt(WxMsgCryptBase):
class WxTemplateMsg(object): 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'): if not Config.THIRDLOGINCONF.get('active'):
return False, f'weixin status is disabled' 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()}' msg_uri = f'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={get_wx_access_token_cache()}'
data = { data = {
"touser": to_user, "touser": self.to_user,
"template_id": template_id, "template_id": template_id,
# "url": "http://weixin.qq.com/download", # "url": "http://weixin.qq.com/download",
"topcolor": "#FF0000", "topcolor": "#FF0000",
@ -187,11 +191,11 @@ class WxTemplateMsg(object):
} }
req = requests.post(msg_uri, json=data) req = requests.post(msg_uri, json=data)
if req.status_code == 200: 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}") logger.error(f"send msg from openid failed {req.status_code} {req.text}")
return False, 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 系统帐号yin.xiaogang
@ -207,7 +211,7 @@ class WxTemplateMsg(object):
msg_id = 'EJhBbxJvHdWnwwexaqb0lCC2sM7D7WMex5-yJvTL5sU' msg_id = 'EJhBbxJvHdWnwwexaqb0lCC2sM7D7WMex5-yJvTL5sU'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”进行了网站登录操作", "value": f"您的微信账户“{self.wx_nick_name}”进行了网站登录操作",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -227,14 +231,14 @@ class WxTemplateMsg(object):
"color": "#173177" "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() now_time = get_format_time()
msg_id = '9rChJFw6nR0Wbp7SXsImh99qm6Dj1hrRWJo1NpEJ_3g' msg_id = '9rChJFw6nR0Wbp7SXsImh99qm6Dj1hrRWJo1NpEJ_3g'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”登录失败", "value": f"您的微信账户“{self.wx_nick_name}”登录失败",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -250,14 +254,14 @@ class WxTemplateMsg(object):
"color": "#173177" "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() now_time = get_format_time()
msg_id = 'twMMQn9AKZKevbZBYh8EcFMk7BnC5Y09FmDkZQEH43w' msg_id = 'twMMQn9AKZKevbZBYh8EcFMk7BnC5Y09FmDkZQEH43w'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”绑定成功", "value": f"您的微信账户“{self.wx_nick_name}”绑定成功",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -273,14 +277,14 @@ class WxTemplateMsg(object):
"color": "#173177" "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() now_time = get_format_time()
msg_id = 'WIrRuHiDG0f976seBAmY-rjSil0AiT9E5l0PHrPnsfs' msg_id = 'WIrRuHiDG0f976seBAmY-rjSil0AiT9E5l0PHrPnsfs'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”绑定失败", "value": f"您的微信账户“{self.wx_nick_name}”绑定失败",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -296,14 +300,14 @@ class WxTemplateMsg(object):
"color": "#173177" "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() now_time = get_format_time()
msg_id = 'RabYMg8-jPGhonk957asbW17iLHSLp8BfEXnyesRZ60' msg_id = 'RabYMg8-jPGhonk957asbW17iLHSLp8BfEXnyesRZ60'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”已经解除绑定", "value": f"您的微信账户“{self.wx_nick_name}”已经解除绑定",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -323,13 +327,13 @@ class WxTemplateMsg(object):
"color": "#173177" "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' msg_id = 'yU15jLNSULagJTff01X67mDtDytBSs3iBpOBi8c7dvs'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}”绑定信息结果", "value": f"您的微信账户“{self.wx_nick_name}”绑定信息结果",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -353,18 +357,18 @@ class WxTemplateMsg(object):
"color": "#173177" "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() now_time = get_format_time()
msg_id = 'uCxjYt216zRAv_sPZihKk4xp7-6pLmRW1oNLLW7L3oI' msg_id = 'uCxjYt216zRAv_sPZihKk4xp7-6pLmRW1oNLLW7L3oI'
content_data = { content_data = {
"first": { "first": {
"value": f"您的微信账户“{wx_nick_name}{action_msg}失败了", "value": f"您的微信账户“{self.wx_nick_name}{action_msg}失败了",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
"value": wx_nick_name, "value": self.wx_nick_name,
"color": "#173177" "color": "#173177"
}, },
"keyword2": { "keyword2": {
@ -384,13 +388,13 @@ class WxTemplateMsg(object):
"color": "#173177" "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' msg_id = 'vRCegZatP18LAe9ytLirwfL1CFyzaCQwM89hMAKsUAA'
content_data = { content_data = {
"first": { "first": {
"value": f"您好,“{wx_nick_name}", "value": f"您好,“{self.wx_nick_name}",
"color": "#173177" "color": "#173177"
}, },
"keyword1": { "keyword1": {
@ -406,7 +410,97 @@ class WxTemplateMsg(object):
"color": "#173177" "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): class WxWebLogin(object):

@ -251,7 +251,7 @@ class IPACONF(object):
# 'http': '47.243.172.202:17897', # 'http': '47.243.172.202:17897',
# 'https': '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 = { MOBILE_CONFIG_SIGN_SSL = {
# 描述文件是否签名,默认是关闭状态;如果开启,并且ssl_key_path 和 ssl_pem_path 正常,则使用填写的ssl进行签名,否则默认不签名 # 描述文件是否签名,默认是关闭状态;如果开启,并且ssl_key_path 和 ssl_pem_path 正常,则使用填写的ssl进行签名,否则默认不签名
'open': True, 'open': True,

@ -263,6 +263,7 @@ CACHE_KEY_TEMPLATE = {
'task_state_key': 'task_state', 'task_state_key': 'task_state',
'pending_state_key': 'pending_state', 'pending_state_key': 'pending_state',
'wx_login_bind_key': 'wx_login_bind', 'wx_login_bind_key': 'wx_login_bind',
'notify_loop_msg_key': 'notify_loop_msg',
'user_can_download_key': 'user_can_download', 'user_can_download_key': 'user_can_download',
'download_times_key': 'app_download_times', 'download_times_key': 'app_download_times',
'make_token_key': 'make_token', 'make_token_key': 'make_token',

@ -694,7 +694,7 @@ class IosUtils(object):
if device_obj.status not in ['ENABLED', 'DISABLED']: if device_obj.status not in ['ENABLED', 'DISABLED']:
developer_obj.status = 5 developer_obj.status = 5
developer_obj.save(update_fields=['status']) 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, '' return True, ''
@staticmethod @staticmethod
@ -1179,19 +1179,29 @@ class IosUtils(object):
return status, result return status, result
@staticmethod @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) app_api_obj = get_api_obj(developer_obj)
status, result = app_api_obj.get_device() status, result = app_api_obj.get_device()
# 获取设备列表的时候,有时候会发生灵异事件,尝试多次获取 【也可能是未知bug导致】 udid_developer_obj_list = UDIDsyncDeveloper.objects.filter(developerid=developer_obj).values_list('udid')
if status and isinstance(result, list) and len(result) == 0: 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) 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() status, result = app_api_obj.get_device()
if status and developer_obj.issuer_id: 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_result_list = [device.udid for device in result]
udid_enabled_result_list = [device.udid for device in result if device.status == 'ENABLED'] udid_enabled_result_list = [device.udid for device in result if device.status == 'ENABLED']

Loading…
Cancel
Save