PushService.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. # -*- coding: utf-8 -*-
  2. """
  3. @Time : 2022/5/19 11:43
  4. @Auth : Locky
  5. @File :PushService.py
  6. @IDE :PyCharm
  7. """
  8. import hashlib
  9. import json
  10. import logging
  11. import os
  12. import time
  13. import apns2
  14. import jpush
  15. import requests
  16. from pyfcm import FCMNotification
  17. from AnsjerPush.config import APP_BUNDLE_DICT, APNS_MODE, BASE_DIR, APNS_CONFIG, FCM_CONFIG, JPUSH_CONFIG, XMPUSH_CONFIG \
  18. , VIVOPUSH_CONFIG, OPPOPUSH_CONFIG, MEIZUPUSH_CONFIG
  19. from Model.models import UidPushModel
  20. from Service.CommonService import CommonService
  21. from Service.VivoPushService.push_admin.APIMessage import PushMessage
  22. from Service.VivoPushService.push_admin.APISender import APISender
  23. class PushObject:
  24. # 推送对象
  25. @staticmethod
  26. def get_msg_title(nickname):
  27. """
  28. 获取推送消息标题
  29. @param nickname: 设备名
  30. @return: msg_title
  31. """
  32. return nickname
  33. @staticmethod
  34. def get_gateway_msg_text(n_time, tz, lang, alarm):
  35. """
  36. 获取网关推送消息内容
  37. @param n_time: 当前时间
  38. @param tz: 时区
  39. @param lang: 语言
  40. @param alarm: 警报
  41. @return: msg_text
  42. """
  43. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  44. if lang == 'cn':
  45. msg_text = '{} 日期:{}'.format(alarm, n_date)
  46. else:
  47. msg_text = '{} date:{}'.format(alarm, n_date)
  48. return msg_text
  49. @staticmethod
  50. def get_ai_msg_text(channel, n_time, lang, tz, label):
  51. """
  52. 获取AI推送内容
  53. @param channel: 通道
  54. @param n_time: 当前时间
  55. @param lang: 语言
  56. @param tz: 时区
  57. @param label: 识别到的标签
  58. @return: ai_msg_text
  59. """
  60. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  61. if lang == 'cn':
  62. msg = '摄像头AI识别到了{}'.format(label)
  63. ai_msg_text = '{msg} 通道:{channel} 日期:{date}'.format(msg=msg, channel=channel, date=n_date)
  64. else:
  65. msg = 'Camera AI recognizes {}'.format(label)
  66. ai_msg_text = '{msg} channel:{channel} date:{date}'.format(msg=msg, channel=channel, date=n_date)
  67. return ai_msg_text
  68. @staticmethod
  69. def get_low_power_msg_text(channel, n_time, lang, tz, electricity, is_sys=0):
  70. """
  71. 获取低电量推送内容
  72. @param channel: 通道
  73. @param n_time: 当前时间
  74. @param lang: 语言
  75. @param tz: 时区
  76. @param electricity: 电量
  77. @param is_sys: 是否为系统消息
  78. @return: low_power_msg_text
  79. """
  80. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  81. if lang == 'cn':
  82. alarm = '剩余电量 ' + electricity
  83. if is_sys:
  84. low_power_msg_text = '{} 通道:{}'.format(alarm, channel)
  85. else:
  86. low_power_msg_text = '{} 通道:{} 日期:{}'.format(alarm, channel, n_date)
  87. else:
  88. alarm = 'Battery remaining ' + electricity
  89. if is_sys:
  90. low_power_msg_text = '{} channel:{}'.format(alarm, channel)
  91. else:
  92. low_power_msg_text = '{} channel:{} date:{}'.format(alarm, channel, n_date)
  93. return low_power_msg_text
  94. @staticmethod
  95. def ios_apns_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  96. uid='', channel='1', launch_image=None):
  97. """
  98. ios apns 推送
  99. @param nickname: 设备昵称
  100. @param app_bundle_id: app包id
  101. @param token_val: 推送token
  102. @param n_time: 当前时间
  103. @param event_type: 事件类型
  104. @param msg_title: 推送标题
  105. @param msg_text: 推送内容
  106. @param uid: uid
  107. @param channel: 通道
  108. @param launch_image: 推送图片链接
  109. @return: None
  110. """
  111. logger = logging.getLogger('info')
  112. try:
  113. pem_path = os.path.join(BASE_DIR, APNS_CONFIG[app_bundle_id]['pem_path'])
  114. logger.info('apns推送app_bundle_id:{}, pem_path:{}'.format(app_bundle_id, pem_path))
  115. cli = apns2.APNSClient(mode=APNS_MODE, client_cert=pem_path)
  116. alert = apns2.PayloadAlert(title=msg_title, body=msg_text, launch_image=launch_image)
  117. push_data = {'alert': 'Motion', 'msg': '', 'sound': '', 'zpush': '1', 'uid': uid, 'channel': channel,
  118. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  119. 'image_url': launch_image
  120. }
  121. payload = apns2.Payload(alert=alert, custom=push_data, sound='default', category='myCategory',
  122. mutable_content=True)
  123. n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
  124. res = cli.push(n=n, device_token=token_val, topic=app_bundle_id)
  125. logger.info('IOS推送响应状态码{}'.format(res.status_code))
  126. assert res.status_code == 200
  127. except Exception as e:
  128. logger.info('--->IOS推送异常{}'.format(repr(e)))
  129. return repr(e)
  130. @staticmethod
  131. def android_fcm_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  132. uid='', channel='1', image=''):
  133. """
  134. android fcm 推送
  135. @param nickname: 设备昵称
  136. @param app_bundle_id: app包id
  137. @param token_val: 推送token
  138. @param n_time: 当前时间
  139. @param event_type: 事件类型
  140. @param msg_title: 推送标题
  141. @param msg_text: 推送内容
  142. @param uid: uid
  143. @param channel: 通道
  144. @param image: 推送图片链接
  145. @return: None
  146. """
  147. logger = logging.getLogger('info')
  148. try:
  149. serverKey = FCM_CONFIG[app_bundle_id]
  150. push_service = FCMNotification(api_key=serverKey)
  151. push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
  152. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  153. 'uid': uid, 'channel': channel
  154. }
  155. result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
  156. message_body=msg_text, data_message=push_data,
  157. extra_kwargs={'default_sound': True,
  158. 'default_vibrate_timings': True,
  159. 'default_light_settings': True,
  160. }
  161. )
  162. logger.info('fcm推送结果:{}'.format(result))
  163. except Exception as e:
  164. return repr(e)
  165. @staticmethod
  166. def android_jpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text):
  167. """
  168. android 极光 推送
  169. @param nickname: 设备昵称
  170. @param app_bundle_id: app包id
  171. @param token_val: 推送token
  172. @param n_time: 当前时间
  173. @param event_type: 事件类型
  174. @param msg_title: 推送标题
  175. @param msg_text: 推送内容
  176. @return: None
  177. """
  178. try:
  179. app_key = JPUSH_CONFIG[app_bundle_id]['Key']
  180. master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
  181. # 换成各自的app_key和master_secret
  182. _jpush = jpush.JPush(app_key, master_secret)
  183. push = _jpush.create_push()
  184. push.audience = jpush.registration_id(token_val)
  185. push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  186. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname
  187. }
  188. android = jpush.android(title=msg_title, big_text=msg_text, alert=msg_text, extras=push_data,
  189. priority=1, style=1, alert_type=7
  190. )
  191. push.notification = jpush.notification(android=android)
  192. push.platform = jpush.all_
  193. res = push.send()
  194. assert res.status_code == 200
  195. except Exception as e:
  196. return repr(e)
  197. @staticmethod
  198. def android_xmpush(channel_id, nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  199. uid='', channel='1', image=''):
  200. """
  201. android 小米 推送
  202. @param channel_id: 通知通道
  203. @param nickname: 设备昵称
  204. @param app_bundle_id: app包id
  205. @param token_val: 推送token
  206. @param n_time: 当前时间
  207. @param event_type: 事件类型
  208. @param msg_title: 推送标题
  209. @param msg_text: 推送内容
  210. @param uid: uid
  211. @param channel: 通道
  212. @param image: 推送图片链接
  213. @return: None
  214. """
  215. logger = logging.getLogger('info')
  216. try:
  217. url = 'https://api.xmpush.xiaomi.com/v3/message/regid'
  218. app_secret = XMPUSH_CONFIG[app_bundle_id]
  219. payload = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  220. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  221. 'uid': uid, 'channel': channel
  222. }
  223. data = {
  224. 'title': msg_title,
  225. 'description': msg_text,
  226. 'payload': 'payload',
  227. 'restricted_package_name': app_bundle_id,
  228. 'registration_id': token_val,
  229. 'extra.channel_id': channel_id
  230. }
  231. # if image:
  232. # data['extra.notification_style_type'] = 2
  233. # data['extra.notification_bigPic_uri'] = image
  234. headers = {
  235. 'Authorization': 'key={}'.format(app_secret)
  236. }
  237. response = requests.post(url, data=data, headers=headers)
  238. logger.info("小米推送返回值:{}".format(response.json()))
  239. assert response.status_code == 200
  240. except Exception as e:
  241. return repr(e)
  242. @staticmethod
  243. def android_vivopush(token_val, n_time, event_type, msg_title, msg_text, app_bundle_id='', uid='', channel='1',
  244. image='', nickname='', appBundleId=''):
  245. """
  246. vivo 推送(不支持图片)
  247. @param app_bundle_id: app包名
  248. @param appBundleId: app包名
  249. @param token_val: 推送token
  250. @param event_type: 事件类型
  251. @param msg_title: 推送标题
  252. @param msg_text: 推送内容
  253. @param n_time: 当前时间
  254. @param nickname: 设备昵称
  255. @param uid: uid
  256. @param image: 推送图片链接
  257. @param channel: 通道
  258. @return: None
  259. """
  260. logger = logging.getLogger('info')
  261. try:
  262. app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
  263. # 获取redis里面的authToken
  264. if msg_title == '':
  265. msg_title = APP_BUNDLE_DICT[app_bundle_id]
  266. app_id = VIVOPUSH_CONFIG[app_bundle_id]['ID']
  267. app_key = VIVOPUSH_CONFIG[app_bundle_id]['Key']
  268. app_secret = VIVOPUSH_CONFIG[app_bundle_id]['Secret']
  269. sender = APISender(app_secret)
  270. rec = sender.get_token(app_id, app_key)
  271. # 鉴权接口调用获得authToken
  272. sender_send = APISender(app_secret)
  273. sender_send.set_token(rec['authToken'])
  274. push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
  275. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  276. 'uid': uid, 'channel': channel
  277. }
  278. # 获取唯一标识符
  279. uid_push_qs = UidPushModel.objects.filter(token_val=token_val).values('m_code')
  280. m_code = uid_push_qs[0]['m_code'] if uid_push_qs[0]['m_code'] else ''
  281. # 推送 push_mode: 推送模式 (0:正式推送;1:测试推送,默认为0)
  282. # 推送 event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
  283. # 推送 skip_type: 跳转类型(1:打开 APP 首页 2:打开链接 3:自定义 4:打开 app 内指定页面)
  284. message = PushMessage() \
  285. .reg_id(token_val) \
  286. .title(msg_title) \
  287. .content(msg_text) \
  288. .push_mode(1) \
  289. .notify_type(3) \
  290. .skip_type(1) \
  291. .request_id(m_code) \
  292. .classification(0) \
  293. .client_custom_map(**push_data) \
  294. .message_dict()
  295. rec = sender_send.send(message)
  296. logger.info('vivo推送结果:{}'.format(rec))
  297. return rec
  298. except Exception as e:
  299. logger.info('vivo推送异常:{}'.format(e))
  300. @staticmethod
  301. def android_oppopush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  302. uid='', channel='1', image=''):
  303. """
  304. android oppo 推送
  305. @param nickname: 设备昵称
  306. @param app_bundle_id: app包id
  307. @param token_val: 推送token
  308. @param n_time: 当前时间
  309. @param event_type: 事件类型
  310. @param msg_title: 推送标题
  311. @param msg_text: 推送内容
  312. @param uid: uid
  313. @param channel: 通道
  314. @param image: 推送图片链接
  315. @return: None
  316. """
  317. logger = logging.getLogger('info')
  318. try:
  319. """
  320. android 国内oppo APP消息提醒推送
  321. """
  322. app_key = OPPOPUSH_CONFIG[app_bundle_id]['Key']
  323. master_secret = OPPOPUSH_CONFIG[app_bundle_id]['Secret']
  324. url = 'https://api.push.oppomobile.com/'
  325. now_time = str(round(time.time() * 1000))
  326. # 1、实例化一个sha256对象
  327. sha256 = hashlib.sha256()
  328. # 2、调用update方法进行加密
  329. sha256.update((app_key + now_time + master_secret).encode('utf-8'))
  330. # 3、调用hexdigest方法,获取加密结果
  331. sign = sha256.hexdigest()
  332. # 获取auth_token
  333. get_token_url = url + 'server/v1/auth'
  334. post_data = {
  335. 'app_key': app_key,
  336. 'sign': sign,
  337. 'timestamp': now_time
  338. }
  339. headers = {'Content-Type': 'application/x-www-form-urlencoded'}
  340. response = requests.post(get_token_url, data=post_data, headers=headers)
  341. result = response.json()
  342. # 发送推送
  343. push_url = url + 'server/v1/message/notification/unicast'
  344. extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  345. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  346. 'uid': uid, 'channel': channel
  347. }
  348. message = {
  349. "target_type": 2,
  350. "target_value": token_val,
  351. "notification": {
  352. "title": msg_title,
  353. "content": msg_text,
  354. }
  355. }
  356. push_data = {
  357. 'auth_token': result['data']['auth_token'],
  358. 'message': json.dumps(message)
  359. }
  360. response = requests.post(push_url, data=push_data, headers=headers)
  361. logger.info("oppo推送返回值:{}".format(response.json()))
  362. assert response.status_code == 200
  363. except Exception as e:
  364. return repr(e)
  365. @staticmethod
  366. def android_meizupush(token_val, n_time, event_type, msg_title, msg_text, uid='', channel='1',
  367. app_bundle_id='', appBundleId='', nickname='', image=''):
  368. """
  369. android 魅族推送(不支持图片)
  370. @param app_bundle_id: app包名
  371. @param appBundleId: app包名
  372. @param token_val: 推送token
  373. @param event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
  374. @param msg_title: 推送标题
  375. @param msg_text: 推送内容
  376. @param n_time: 当前时间
  377. @param nickname: 设备昵称
  378. @param uid: uid
  379. @param image: 推送图片链接
  380. @param channel: 通道
  381. @return: None
  382. """
  383. logger = logging.getLogger('info')
  384. try:
  385. # 获取包和AppSecret
  386. app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
  387. appId = MEIZUPUSH_CONFIG[app_bundle_id]['ID']
  388. appSecret = MEIZUPUSH_CONFIG[app_bundle_id]['AppSecret']
  389. url = 'https://server-api-push.meizu.com/garcia/api/server/push/varnished/pushByPushId'
  390. extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  391. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  392. 'uid': uid, 'channel': channel
  393. }
  394. if msg_title == '':
  395. msg_title = APP_BUNDLE_DICT[app_bundle_id]
  396. # 拼接发送内容
  397. messageJson = '{"clickTypeInfo":{"activity":"","clickType":0,"customAttribute":""},"extra":{},'
  398. noticeBarInfo = ('"noticeBarInfo": {"title": "%s", "content": "%s"},' % (msg_title, msg_text))
  399. noticeExpandInfo = '"noticeExpandInfo":{"noticeExpandType":0},"pushTimeInfo":{"validTime":24}}'
  400. messageJson += noticeBarInfo
  401. messageJson += noticeExpandInfo
  402. data_meizu = {
  403. 'appId': appId,
  404. 'pushIds': token_val,
  405. 'messageJson': messageJson
  406. }
  407. # 魅族MD5加密,生成密钥
  408. sign = CommonService.getMD5Sign(data=data_meizu, key=appSecret)
  409. data = {
  410. 'appId': appId,
  411. 'messageJson': messageJson,
  412. 'sign': sign,
  413. 'pushIds': token_val,
  414. }
  415. # 进行推送
  416. response = requests.post(url, data=data)
  417. logger.info("魅族推送结果:{}".format(response.json()))
  418. return response.status_code
  419. except Exception as e:
  420. return repr(e)