DetectControllerV2.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. import json
  2. import logging
  3. import os
  4. import apns2
  5. import boto3
  6. import botocore
  7. import jpush as jpush
  8. from botocore import client
  9. from django.http import JsonResponse
  10. from django.views.generic.base import View
  11. from pyfcm import FCMNotification
  12. from AnsjerPush.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
  13. from AnsjerPush.config import JPUSH_CONFIG, FCM_CONFIG, APNS_CONFIG, BASE_DIR, APNS_MODE
  14. from Object.RedisObject import RedisObject
  15. from Service.CommonService import CommonService
  16. from Service.DevicePushService import DevicePushService
  17. from Service.PushService import PushObject
  18. # 移动侦测V2接口
  19. class NotificationV2View(View):
  20. def get(self, request, *args, **kwargs):
  21. request.encoding = 'utf-8'
  22. return self.validation(request.GET)
  23. def post(self, request, *args, **kwargs):
  24. request.encoding = 'utf-8'
  25. return self.validation(request.POST)
  26. def validation(self, request_dict):
  27. """
  28. 设备触发报警消息推送
  29. @param request_dict:uidToken 加密uid
  30. @param request_dict:etk 加密uid
  31. @param request_dict:channel 设备通道号
  32. @param request_dict:n_time 设备触发报警时间
  33. @param request_dict:event_type 设备事件类型
  34. @param request_dict:is_st 文件类型(0:无,1:图片,2:视频)
  35. @param request_dict:region 文件存储区域(1:国外,2国内)
  36. @param request_dict:electricity 电量值
  37. """
  38. logger = logging.getLogger('info')
  39. logger.info('移动侦测V2接口参数:{}'.format(request_dict))
  40. uidToken = request_dict.get('uidToken', None)
  41. etk = request_dict.get('etk', None)
  42. channel = request_dict.get('channel', '1')
  43. n_time = request_dict.get('n_time', None)
  44. event_type = request_dict.get('event_type', None)
  45. is_st = request_dict.get('is_st', None)
  46. region = request_dict.get('region', None)
  47. electricity = request_dict.get('electricity', '')
  48. time_token = request_dict.get('time_token', None)
  49. uid = request_dict.get('uid', None)
  50. # 往来检测 1:来,2:离开
  51. dealings_type = request_dict.get('dealingsType', 0)
  52. # 检测类型 0:普通,1:算法
  53. detection = int(request_dict.get('detection', 0))
  54. if not all([channel, n_time]):
  55. return JsonResponse(status=200, data={
  56. 'code': 444,
  57. 'msg': 'param is wrong'})
  58. if not region or not is_st:
  59. return JsonResponse(status=200, data={'code': 404, 'msg': 'no region or is_st'})
  60. # 时间戳token校验
  61. if time_token:
  62. if not CommonService.check_time_stamp_token(time_token, n_time):
  63. return JsonResponse(status=200, data={'code': 13, 'msg': 'Timestamp token verification failed'})
  64. try:
  65. is_st = int(is_st)
  66. region = int(region)
  67. event_type = int(event_type)
  68. if not uid:
  69. uid = DevicePushService.decode_uid(etk, uidToken) # 解密uid
  70. if len(uid) != 20 and len(uid) != 14:
  71. return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong uid'})
  72. req_limiting = '{uid}_{channel}_{event_type}_ptl' \
  73. .format(uid=uid, channel=channel, event_type=event_type)
  74. is_sys_msg = self.is_sys_msg(int(event_type)) # 判断事件类型是否是系统消息
  75. if is_sys_msg:
  76. push_interval = '{uid}_{channel}_{event_type}_flag' \
  77. .format(uid=uid, channel=channel, event_type=event_type)
  78. else:
  79. push_interval = '{uid}_{channel}_flag'.format(uid=uid, channel=channel)
  80. redisObj = RedisObject(db=6)
  81. cache_req_limiting = redisObj.get_data(key=req_limiting) # 获取请求限流缓存数据
  82. cache_app_push = redisObj.get_data(key=push_interval) # 获取APP推送消息时间间隔缓存数据
  83. logger.info('消息推送- 限流key: {}, 推送间隔key: {}'.
  84. format(cache_req_limiting, cache_app_push))
  85. if event_type != 606:
  86. if cache_req_limiting: # 限流存在则直接返回
  87. return JsonResponse(status=200, data={'code': 0, 'msg': 'Push again in one minute'})
  88. redisObj.set_data(key=req_limiting, val=1, expire=60) # 当缓存不存在限流数据 重新设置一分钟请求一次
  89. uid_push_qs = DevicePushService.query_uid_push(uid, event_type) # 查询uid_set与push数据列表
  90. if not uid_push_qs.exists():
  91. logger.info('消息推送-{}uid_push数据不存在'.format(uid))
  92. return JsonResponse(status=200, data={'code': 176, 'msg': 'no uid_push data'})
  93. ai_type = uid_push_qs.first()['uid_set__ai_type']
  94. device_type = uid_push_qs.first()['uid_set__device_type']
  95. logger.info('ai_type: {}, device_type: {}'.format(ai_type, device_type))
  96. # 将uid_set以及uid_push 转数组列表
  97. uid_set_push_list = DevicePushService.cache_uid_push(uid_push_qs)
  98. nickname = uid_set_push_list[0]['uid_set__nickname']
  99. nickname = uid if not nickname else nickname
  100. # APP消息提醒推送间隔
  101. detect_interval = uid_set_push_list[0]['uid_set__detect_interval']
  102. if event_type != 606:
  103. if not cache_app_push:
  104. # 缓存APP提醒推送间隔 默认1分钟提醒一次
  105. DevicePushService.cache_push_detect_interval(redisObj, push_interval, detect_interval,
  106. uid_set_push_list[0]['uid_set__new_detect_interval'])
  107. else:
  108. cache_app_push = ''
  109. bucket = ''
  110. aws_s3_client = ''
  111. if is_st == 1 or is_st == 3: # 使用aws s3
  112. aws_s3_client = s3_client(region=region)
  113. bucket = 'foreignpush' if region == 1 else 'push'
  114. kwag_args = {
  115. 'uid': uid,
  116. 'channel': channel,
  117. 'event_type': event_type,
  118. 'n_time': n_time,
  119. }
  120. params = {'nickname': nickname, 'uid': uid, 'kwag_args': kwag_args, 'is_st': is_st, 'region': region,
  121. 'is_sys_msg': is_sys_msg, 'channel': channel, 'event_type': event_type, 'n_time': n_time,
  122. 'electricity': electricity, 'bucket': bucket, 'aws_s3_client': aws_s3_client,
  123. 'app_push': cache_app_push, 'storage_location': 2, 'ai_type': ai_type, 'device_type': device_type,
  124. 'dealings_type': int(dealings_type), 'detection': detection}
  125. logger.info('推送数据参数:{}'.format(params))
  126. # 推送消息,生成推送数据列表
  127. result = DevicePushService.save_msg_push(uid_set_push_list, **params)
  128. # 保存推送数据
  129. DevicePushService.save_sys_msg(is_sys_msg, result['local_date_time'],
  130. result['sys_msg_list'], result['new_device_info_list'])
  131. params['aws_s3_client'] = aws_s3_client
  132. params['uid_set_push_list'] = uid_set_push_list
  133. params['code_dict'] = result
  134. result_dict = DevicePushService.get_push_url(**params) # 获取S3对象上传链接
  135. return JsonResponse(status=200, data=result_dict)
  136. except Exception as e:
  137. logger.info('V2推送接口异常, errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  138. data = {
  139. 'errLine': e.__traceback__.tb_lineno,
  140. 'errMsg': repr(e),
  141. }
  142. return JsonResponse(status=200, data=json.dumps(data), safe=False)
  143. @classmethod
  144. def get_combo_msg_type(cls, ai_type, event_type):
  145. """
  146. 获取组合类型,需判断组合类型
  147. """
  148. logger = logging.getLogger('info')
  149. try:
  150. if ai_type == 0:
  151. return event_type
  152. logger.info('LOG------算法小店组合类型十进制值:{}'.format(event_type))
  153. # 设备十进制算法说明:1:移动侦测,2:人形,4:车型,8:人脸,16:宠物,32:异响,64:区域闯入,
  154. # 128:区域闯出,256:徘徊检测,512:长时间无人检测
  155. event_dict = {
  156. 1: 51,
  157. 2: 57,
  158. 4: 58,
  159. 16: 59,
  160. 8: 60,
  161. 32: 61,
  162. 64: 62,
  163. 128: 63,
  164. 256: 64,
  165. 512: 65
  166. }
  167. event_val = event_dict.get(event_type, 0)
  168. # event_val == 0 没有匹配到单个值则认为组合类型
  169. # 如是3,则转为二进制11,代表(1+2)触发了移动侦测+人形侦测
  170. if event_val == 0:
  171. val = cls.dec_to_bin(event_type)
  172. return int(val)
  173. else:
  174. return int(event_val)
  175. except Exception as e:
  176. logger.info('推送错误异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
  177. return event_type
  178. @staticmethod
  179. def dec_to_bin(num):
  180. """
  181. 十进制转二进制
  182. """
  183. result = ""
  184. while num != 0:
  185. ret = num % 2
  186. num //= 2
  187. result = str(ret) + result
  188. return result
  189. def push_thread_test(self, push_type, aws_s3_client, bucket, key, uid, appBundleId, token_val, event_type, n_time,
  190. msg_title, msg_text, channel):
  191. logger = logging.getLogger('info')
  192. logger.info('推送图片测试:{} {} {} {} {} {} {} {}'.format(push_type, uid, appBundleId, token_val, event_type, n_time,
  193. msg_title, msg_text))
  194. try:
  195. image_url = aws_s3_client.generate_presigned_url('get_object', Params={'Bucket': bucket, 'Key': key},
  196. ExpiresIn=300)
  197. logger.info('推送图片url:{}'.format(image_url))
  198. if push_type == 0:
  199. PushObject.ios_apns_push(uid, appBundleId, token_val, n_time, event_type, msg_title, msg_text,
  200. uid, channel, image_url)
  201. elif push_type == 1:
  202. PushObject.android_fcm_push(uid, appBundleId, token_val, n_time, event_type, msg_title,
  203. msg_text, uid, channel, image_url)
  204. except Exception as e:
  205. logger.info('推送图片测试异常:{}'.format(e))
  206. @staticmethod
  207. def is_sys_msg(event_type):
  208. """
  209. 判断是否属于系统消息
  210. @return: True | False
  211. """
  212. event_type_list = [702, 703, 704]
  213. if event_type in event_type_list:
  214. return True
  215. return False
  216. def get_msg_text(self, channel, n_time, lang, tz, event_type, electricity='', is_sys=0):
  217. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  218. etype = int(event_type)
  219. if lang == 'cn':
  220. if etype == 704:
  221. msg_type = '剩余电量 ' + electricity
  222. elif etype == 702:
  223. msg_type = '摄像头休眠'
  224. elif etype == 703:
  225. msg_type = '摄像头唤醒'
  226. else:
  227. msg_type = ''
  228. if is_sys:
  229. send_text = '{msg_type} 通道:{channel}'.format(msg_type=msg_type, channel=channel)
  230. else:
  231. send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
  232. else:
  233. if etype == 704:
  234. msg_type = 'Battery remaining ' + electricity
  235. elif etype == 702:
  236. msg_type = 'Camera sleep'
  237. elif etype == 703:
  238. msg_type = 'Camera wake'
  239. else:
  240. msg_type = ''
  241. if is_sys:
  242. send_text = '{msg_type} channel:{channel}'. \
  243. format(msg_type=msg_type, channel=channel)
  244. else:
  245. send_text = '{msg_type} channel:{channel} date:{date}'. \
  246. format(msg_type=msg_type, channel=channel, date=n_date)
  247. return send_text
  248. def do_jpush(self, uid, channel, appBundleId, token_val, event_type, n_time,
  249. msg_title, msg_text):
  250. app_key = JPUSH_CONFIG[appBundleId]['Key']
  251. master_secret = JPUSH_CONFIG[appBundleId]['Secret']
  252. _jpush = jpush.JPush(app_key, master_secret)
  253. push = _jpush.create_push()
  254. push.audience = jpush.registration_id(token_val)
  255. push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
  256. "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
  257. android = jpush.android(alert=msg_text, priority=1, style=1, alert_type=7,
  258. big_text=msg_text, title=msg_title,
  259. extras=push_data)
  260. push.notification = jpush.notification(android=android)
  261. push.platform = jpush.all_
  262. res = push.send()
  263. print(res)
  264. return res.status_code
  265. def do_fcm(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
  266. logger = logging.getLogger('info')
  267. try:
  268. serverKey = FCM_CONFIG[appBundleId]
  269. except Exception as e:
  270. logger.info('------fcm_error:{}'.format(repr(e)))
  271. return 'serverKey abnormal'
  272. push_service = FCMNotification(api_key=serverKey)
  273. data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
  274. "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
  275. result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
  276. message_body=msg_text, data_message=data,
  277. extra_kwargs={
  278. 'default_vibrate_timings': True,
  279. 'default_sound': True,
  280. 'default_light_settings': True
  281. })
  282. return result
  283. def do_apns(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title,
  284. msg_text):
  285. logger = logging.getLogger('info')
  286. logger.info("进来do_apns函数了")
  287. logger.info(token_val)
  288. logger.info(APNS_MODE)
  289. logger.info(os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
  290. try:
  291. cli = apns2.APNSClient(mode=APNS_MODE,
  292. client_cert=os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
  293. push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
  294. "received_at": n_time, "sound": "", "uid": uid, "zpush": "1", "channel": channel}
  295. alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
  296. payload = apns2.Payload(alert=alert, custom=push_data, sound="default")
  297. # return uid, channel, appBundleId, str(token_val), event_type, n_time, msg_title,msg_text
  298. n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
  299. res = cli.push(n=n, device_token=token_val, topic=appBundleId)
  300. print(res.status_code)
  301. logger.info("apns_推送状态:")
  302. logger.info(res.status_code)
  303. if res.status_code == 200:
  304. return res.status_code
  305. else:
  306. print('apns push fail')
  307. print(res.reason)
  308. logger.info('apns push fail')
  309. logger.info(res.reason)
  310. return res.status_code
  311. except (ValueError, ArithmeticError):
  312. return 'The program has a numeric format exception, one of the arithmetic exceptions'
  313. except Exception as e:
  314. print(repr(e))
  315. print('do_apns函数错误行号', e.__traceback__.tb_lineno)
  316. logger.info('do_apns错误:{}'.format(repr(e)))
  317. return repr(e)
  318. def s3_client(region):
  319. if region == 2: # 国内
  320. aws_s3_client = boto3.client(
  321. 's3',
  322. aws_access_key_id=AWS_ACCESS_KEY_ID[0],
  323. aws_secret_access_key=AWS_SECRET_ACCESS_KEY[0],
  324. config=botocore.client.Config(signature_version='s3v4'),
  325. region_name='cn-northwest-1'
  326. )
  327. else: # 国外
  328. aws_s3_client = boto3.client(
  329. 's3',
  330. aws_access_key_id=AWS_ACCESS_KEY_ID[1],
  331. aws_secret_access_key=AWS_SECRET_ACCESS_KEY[1],
  332. config=botocore.client.Config(signature_version='s3v4'),
  333. region_name='us-east-1'
  334. )
  335. return aws_s3_client