PushService.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  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 jpush
  14. import requests
  15. import jwt
  16. import httpx
  17. from firebase_admin import messaging
  18. from firebase_admin.messaging import UnregisteredError
  19. from pyfcm import FCMNotification
  20. from AnsjerPush.config import APP_BUNDLE_DICT, BASE_DIR, APNS_CONFIG, FCM_CONFIG, JPUSH_CONFIG, XMPUSH_CONFIG, \
  21. VIVOPUSH_CONFIG, OPPOPUSH_CONFIG, MEIZUPUSH_CONFIG, CONFIG_INFO, HONORPUSH_CONFIG, DATA_PUSH_EVENT_TYPE_LIST, \
  22. CONFIG_TEST
  23. from Model.models import UidPushModel
  24. from Object.enums.ConstantEnum import ConstantEnum
  25. from Service.CommonService import CommonService
  26. from Service.VivoPushService.push_admin.APIMessage import PushMessage
  27. from Service.VivoPushService.push_admin.APISender import APISender
  28. from AnsjerPush.config import LOGGER
  29. TIME_LOGGER = logging.getLogger('time')
  30. class PushObject:
  31. # 推送对象
  32. @staticmethod
  33. def get_msg_title(nickname):
  34. """
  35. 获取推送消息标题
  36. @param nickname: 设备名
  37. @return: msg_title
  38. """
  39. return nickname
  40. @staticmethod
  41. def get_gateway_msg_text(n_time, tz, lang, alarm):
  42. """
  43. 获取网关推送消息内容
  44. @param n_time: 当前时间
  45. @param tz: 时区
  46. @param lang: 语言
  47. @param alarm: 警报
  48. @return: msg_text
  49. """
  50. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  51. if lang == 'cn':
  52. msg_text = '{} 日期:{}'.format(alarm, n_date)
  53. else:
  54. msg_text = '{} date:{}'.format(alarm, n_date)
  55. return msg_text
  56. @staticmethod
  57. def get_ai_msg_text(channel, n_time, lang, tz, label):
  58. """
  59. 获取AI推送内容
  60. @param channel: 通道
  61. @param n_time: 当前时间
  62. @param lang: 语言
  63. @param tz: 时区
  64. @param label: 识别到的标签
  65. @return: ai_msg_text
  66. """
  67. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  68. if lang == 'cn':
  69. msg = '摄像头AI识别到了{}'.format(label)
  70. ai_msg_text = '{msg} 通道:{channel} 日期:{date}'.format(msg=msg, channel=channel, date=n_date)
  71. else:
  72. msg = 'Camera AI recognizes {}'.format(label)
  73. ai_msg_text = '{msg} channel:{channel} date:{date}'.format(msg=msg, channel=channel, date=n_date)
  74. return ai_msg_text
  75. @staticmethod
  76. def get_low_power_msg_text(channel, n_time, lang, tz, electricity, is_sys=0):
  77. """
  78. 获取低电量推送内容
  79. @param channel: 通道
  80. @param n_time: 当前时间
  81. @param lang: 语言
  82. @param tz: 时区
  83. @param electricity: 电量
  84. @param is_sys: 是否为系统消息
  85. @return: low_power_msg_text
  86. """
  87. n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
  88. if lang == 'cn':
  89. alarm = '剩余电量 ' + electricity
  90. if is_sys:
  91. low_power_msg_text = '{} 通道:{}'.format(alarm, channel)
  92. else:
  93. low_power_msg_text = '{} 通道:{} 日期:{}'.format(alarm, channel, n_date)
  94. else:
  95. alarm = 'Battery remaining ' + electricity
  96. if is_sys:
  97. low_power_msg_text = '{} channel:{}'.format(alarm, channel)
  98. else:
  99. low_power_msg_text = '{} channel:{} date:{}'.format(alarm, channel, n_date)
  100. return low_power_msg_text
  101. @staticmethod
  102. def ios_apns_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  103. uid='', channel='1', launch_image=None):
  104. """
  105. ios apns 推送
  106. @param nickname: 设备昵称
  107. @param app_bundle_id: app包id
  108. @param token_val: 推送token
  109. @param n_time: 当前时间
  110. @param event_type: 事件类型
  111. @param msg_title: 推送标题
  112. @param msg_text: 推送内容
  113. @param uid: uid
  114. @param channel: 通道
  115. @param launch_image: 推送图片链接
  116. @return: bool
  117. """
  118. pem_path = os.path.join(BASE_DIR, APNS_CONFIG[app_bundle_id]['pem_path'])
  119. LOGGER.info('IOS推送: uid:{}, app_bundle_id:{}, pem_path:{}, msg_text:'.format(
  120. uid, app_bundle_id, pem_path, msg_text))
  121. try:
  122. apns_url = "https://api.sandbox.push.apple.com" if CONFIG_INFO == CONFIG_TEST else "https://api.push.apple.com"
  123. url = f"{apns_url}/3/device/{token_val}"
  124. jump_type = CommonService.get_jump_type(event_type)
  125. sound = 'call_phone.mp3' if event_type in DATA_PUSH_EVENT_TYPE_LIST else 'default'
  126. # 构造 body —— 把 image_url 等字段放顶层,便于 Notification Service Extension 直接读取
  127. body = {
  128. "alert": msg_text,
  129. "aps": {
  130. "alert": {
  131. "title": msg_title,
  132. "body": msg_text
  133. },
  134. "sound": sound,
  135. "category": "myCategory",
  136. "mutable-content": 1
  137. },
  138. 'jump_type': jump_type,
  139. 'image_url': launch_image,
  140. "channel": channel,
  141. "event_time": n_time,
  142. "event_type": event_type,
  143. "msg": "",
  144. "received_at": n_time,
  145. "sound": "",
  146. "uid": uid,
  147. "zpush": "1"
  148. }
  149. headers = {
  150. "apns-topic": app_bundle_id,
  151. "apns-push-type": "alert"
  152. }
  153. with httpx.Client(http2=True,timeout=10, cert=str(pem_path),) as client:
  154. res = client.post(url, headers=headers, json=body)
  155. if res.status_code == 200:
  156. LOGGER.info(f"{uid} iOS 推送成功, token:{token_val}")
  157. return True
  158. elif res.status_code == 400:
  159. LOGGER.error(
  160. f"{uid} iOS 推送失败, 状态码: {res.status_code}, 原因: {res.text}, 包名: {app_bundle_id}, iOS token无效"
  161. )
  162. # 删除无效token数据
  163. UidPushModel.objects.filter(uid_set__uid=uid, token_val=token_val).delete()
  164. return False
  165. elif res.status_code == 410:
  166. LOGGER.error(
  167. f"{uid} iOS 推送失败, 状态码: {res.status_code}, 原因: {res.text}, 包名: {app_bundle_id}, iOS token过期"
  168. )
  169. # 删除失效token数据
  170. UidPushModel.objects.filter(uid_set__uid=uid, token_val=token_val).delete()
  171. return False
  172. else:
  173. LOGGER.error(f"{uid} iOS 推送失败,状态码: {res.status_code}, 原因: {res.text}")
  174. return False
  175. except Exception as e:
  176. LOGGER.error(f"{uid} iOS 推送异常: {repr(e)}, 行数: {e.__traceback__.tb_lineno}")
  177. return False
  178. @staticmethod
  179. def ios_p8_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  180. uid='', channel='1', launch_image=None):
  181. """
  182. iOS推送 (P8证书模式)
  183. @param nickname: 设备昵称
  184. @param app_bundle_id: app包id
  185. @param token_val: 推送 token
  186. @param n_time: 当前时间
  187. @param event_type: 事件类型
  188. @param msg_title: 推送标题
  189. @param msg_text: 推送内容
  190. @param uid:uid
  191. @param channel: 通道
  192. @param launch_image: 推送图片链接
  193. @return: bool
  194. """
  195. if app_bundle_id not in ConstantEnum.IOS_P8_CONFIG.value:
  196. return PushObject.ios_apns_push(
  197. nickname=nickname,
  198. app_bundle_id=app_bundle_id,
  199. token_val=token_val,
  200. n_time=n_time,
  201. event_type=event_type,
  202. msg_title=msg_title,
  203. msg_text=msg_text,
  204. uid=uid,
  205. channel=channel,
  206. launch_image=launch_image
  207. )
  208. else:
  209. LOGGER.info("进入 ios_p8_push 方法,准备开始推送")
  210. try:
  211. team_id = ConstantEnum.IOS_P8_CONFIG.value[app_bundle_id]['team_id']
  212. key_id = ConstantEnum.IOS_P8_CONFIG.value[app_bundle_id]['key_id']
  213. p8_path = os.path.join(BASE_DIR, ConstantEnum.IOS_P8_CONFIG.value[app_bundle_id]['pem_path'])
  214. with open(p8_path, "r") as f:
  215. private_key = f.read()
  216. now = int(time.time())
  217. token = jwt.encode(
  218. {"iss": team_id, "iat": now},
  219. private_key,
  220. algorithm="ES256",
  221. headers={"kid": key_id}
  222. )
  223. if isinstance(token, bytes):
  224. token = token.decode("utf-8")
  225. apns_url = "https://api.sandbox.push.apple.com" if CONFIG_INFO == CONFIG_TEST else "https://api.push.apple.com"
  226. url = f"{apns_url}/3/device/{token_val}"
  227. sound = 'call_phone.mp3' if event_type in DATA_PUSH_EVENT_TYPE_LIST else 'default'
  228. body = {
  229. "aps": {
  230. "alert": {
  231. "title": msg_title,
  232. "body": msg_text
  233. },
  234. "sound": sound,
  235. "category": "myCategory",
  236. "mutable-content": 1
  237. },
  238. # ---- 把原来的 push_data 展平到顶层(尤其是 image_url) ----
  239. "jump_type": CommonService.get_jump_type(event_type),
  240. "image_url": launch_image,
  241. "channel": channel,
  242. "event_time": n_time,
  243. "event_type": event_type,
  244. "msg":"",
  245. "received_at": n_time,
  246. "sound":"",
  247. "uid": uid,
  248. "zpush": "1",
  249. "nickname": nickname,
  250. }
  251. headers = {
  252. "authorization": f"bearer {token}",
  253. "apns-topic": app_bundle_id,
  254. "apns-push-type": "alert"
  255. }
  256. with httpx.Client(http2=True, timeout=10,) as client:
  257. res = client.post(url, headers=headers, json=body)
  258. if res.status_code == 200:
  259. LOGGER.info(f"{uid} iOS p8 推送成功, token:{token_val}")
  260. return True
  261. elif res.status_code == 400:
  262. LOGGER.error(
  263. f"{uid} iOS p8 推送失败, 状态码: {res.status_code}, "
  264. f"原因: {res.text}, 包名: {app_bundle_id}, iOS token无效")
  265. # 删除无效token数据
  266. UidPushModel.objects.filter(uid_set__uid=uid, token_val=token_val).delete()
  267. return False
  268. elif res.status_code == 410:
  269. LOGGER.error(
  270. f"{uid} iOS p8 推送失败, 状态码: {res.status_code}, "
  271. f"原因: {res.text}, 包名: {app_bundle_id}, iOS token过期")
  272. # 删除失效token数据
  273. UidPushModel.objects.filter(uid_set__uid=uid, token_val=token_val).delete()
  274. return False
  275. else:
  276. LOGGER.error(f"{uid} iOS p8 推送失败,状态码: {res.status_code}, 原因: {res.text}")
  277. return False
  278. except Exception as e:
  279. LOGGER.error(f"{uid} iOS p8 推送异常: {repr(e)}, 行数: {e.__traceback__.tb_lineno}")
  280. return False
  281. @staticmethod
  282. def android_fcm_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  283. uid='', channel='1', image=''):
  284. """
  285. android fcm 推送
  286. @param nickname: 设备昵称
  287. @param app_bundle_id: app包id
  288. @param token_val: 推送token
  289. @param n_time: 当前时间
  290. @param event_type: 事件类型
  291. @param msg_title: 推送标题
  292. @param msg_text: 推送内容
  293. @param uid: uid
  294. @param channel: 通道
  295. @param image: 推送图片链接
  296. @return: bool
  297. """
  298. try:
  299. serverKey = FCM_CONFIG[app_bundle_id]
  300. push_service = FCMNotification(api_key=serverKey)
  301. push_data = {'alert': msg_text, 'msg': '', 'zpush': '1', 'image': image,
  302. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  303. 'uid': uid, 'channel': channel
  304. }
  305. if event_type in DATA_PUSH_EVENT_TYPE_LIST:
  306. push_data['priority'] = 'high'
  307. push_data['content_available'] = True
  308. push_data['direct_boot_ok'] = True
  309. sound = 'android.resource://com.ansjer.zccloud_a/raw/phone_call'
  310. result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
  311. message_body=msg_text, data_message=push_data, sound=sound,
  312. android_channel_id='video',
  313. click_action='android.intent.action.VIEW',
  314. extra_kwargs={'default_sound': False,
  315. 'default_vibrate_timings': True,
  316. 'default_light_settings': True,
  317. },
  318. )
  319. else:
  320. result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
  321. message_body=msg_text, data_message=push_data,
  322. click_action='android.intent.action.VIEW',
  323. extra_kwargs={'default_sound': True,
  324. 'default_vibrate_timings': True,
  325. 'default_light_settings': True,
  326. },
  327. )
  328. TIME_LOGGER.info('uid:{}fcm推送结果:{}'.format(uid, result))
  329. return True
  330. except Exception as e:
  331. TIME_LOGGER.error('uid:{}fcm推送异常:{}'.format(uid, repr(e)))
  332. return False
  333. @staticmethod
  334. def android_fcm_push_v1(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  335. uid='', channel='1', image=''):
  336. """
  337. android fcm 推送
  338. @param nickname: 设备昵称
  339. @param app_bundle_id: app包id
  340. @param token_val: 推送token
  341. @param n_time: 当前时间
  342. @param event_type: 事件类型
  343. @param msg_title: 推送标题
  344. @param msg_text: 推送内容
  345. @param uid: uid
  346. @param channel: 通道
  347. @param image: 推送图片链接
  348. @return: bool
  349. """
  350. try:
  351. event_type = str(event_type)
  352. n_time = str(n_time)
  353. # 跳转类型
  354. jump_type = str(CommonService.get_jump_type(event_type))
  355. # 推送数据类型必须为字符串,否则抛ValueError('Message.data must not contain non-string values.')异常
  356. push_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  357. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  358. 'uid': uid, 'channel': channel, 'jump_type': jump_type
  359. }
  360. if event_type in DATA_PUSH_EVENT_TYPE_LIST:
  361. push_data['priority'] = 'high'
  362. push_data['content_available'] = True
  363. push_data['direct_boot_ok'] = True
  364. message = messaging.Message(
  365. notification=messaging.Notification(
  366. title=msg_title,
  367. body=msg_text,
  368. image=image
  369. ),
  370. data=push_data,
  371. token=token_val,
  372. )
  373. # Send a message to the device corresponding to the provided
  374. # registration token.
  375. result = messaging.send(message)
  376. LOGGER.info('uid:{} fcm推送结果:{}'.format(uid, result))
  377. return True
  378. except UnregisteredError as e:
  379. LOGGER.info('uid:{},token:{},fcm推送异常UnregisteredError:{}'.format(uid, token_val, repr(e)))
  380. # 删除失效token数据
  381. UidPushModel.objects.filter(uid_set__uid=uid, token_val=token_val).delete()
  382. except Exception as e:
  383. LOGGER.info('uid:{} fcm推送异常:{}'.format(uid, repr(e)))
  384. return False
  385. @staticmethod
  386. def android_jpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text, channel=1):
  387. """
  388. android 极光 推送
  389. @param nickname: 设备昵称
  390. @param app_bundle_id: app包id
  391. @param token_val: 推送token
  392. @param n_time: 当前时间
  393. @param event_type: 事件类型
  394. @param msg_title: 推送标题
  395. @param msg_text: 推送内容
  396. @param channel: 设备通道
  397. @return: bool
  398. """
  399. try:
  400. # app_key = JPUSH_CONFIG[app_bundle_id]['Key']
  401. # master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
  402. # # 换成各自的app_key和master_secret
  403. # _jpush = jpush.JPush(app_key, master_secret)
  404. # push = _jpush.create_push()
  405. # push.audience = jpush.registration_id(token_val)
  406. # if event_type in DATA_PUSH_EVENT_TYPE_LIST:
  407. # channel_id = '111934'
  408. # else:
  409. # channel_id = '1'
  410. # push_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'uid': nickname,
  411. # 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  412. # 'channel': channel
  413. # }
  414. # android = jpush.android(title=msg_title, big_text=msg_text, alert=msg_text, extras=push_data,
  415. # priority=1, style=1, alert_type=7, channel_id=channel_id
  416. # )
  417. # push.notification = jpush.notification(android=android)
  418. # push.platform = jpush.all_
  419. # res = push.send()
  420. # assert res.status_code == 200
  421. return True
  422. except Exception as e:
  423. LOGGER.info('uid:{},time:{},极光推送异常:{}'.format(nickname, n_time, repr(e)))
  424. return False
  425. @staticmethod
  426. def jpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text, channel=1):
  427. """
  428. android 极光 推送
  429. @param nickname: 设备昵称
  430. @param app_bundle_id: app包id
  431. @param token_val: 推送token
  432. @param n_time: 当前时间
  433. @param event_type: 事件类型
  434. @param msg_title: 推送标题
  435. @param msg_text: 推送内容
  436. @param channel: 设备通道
  437. @return: bool
  438. """
  439. try:
  440. app_key = JPUSH_CONFIG[app_bundle_id]['Key']
  441. master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
  442. # 换成各自的app_key和master_secret
  443. _jpush = jpush.JPush(app_key, master_secret)
  444. push = _jpush.create_push()
  445. push.audience = jpush.registration_id(token_val)
  446. if event_type in DATA_PUSH_EVENT_TYPE_LIST:
  447. channel_id = '111934'
  448. else:
  449. channel_id = '1'
  450. # 跳转类型
  451. jump_type = CommonService.get_jump_type(event_type)
  452. push_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'uid': nickname,
  453. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  454. 'channel': channel, 'jump_type': jump_type
  455. }
  456. android = jpush.android(title=msg_title, big_text=msg_text, alert=msg_text, extras=push_data,
  457. priority=1, style=1, alert_type=7, channel_id=channel_id
  458. )
  459. push.notification = jpush.notification(android=android)
  460. push.platform = jpush.all_
  461. res = push.send()
  462. assert res.status_code == 200
  463. LOGGER.info('极光推送响应:{}, 参数:{}, 令牌:{}'.format(res, push_data, token_val))
  464. return True
  465. except Exception as e:
  466. LOGGER.info('极光推送异常:{}'.format(repr(e)))
  467. return False
  468. @staticmethod
  469. def android_xmpush(channel_id, nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  470. uid='', channel='1', image=''):
  471. """
  472. android 小米 推送
  473. @param channel_id: 通知通道
  474. @param nickname: 设备昵称
  475. @param app_bundle_id: app包id
  476. @param token_val: 推送token
  477. @param n_time: 当前时间
  478. @param event_type: 事件类型
  479. @param msg_title: 推送标题
  480. @param msg_text: 推送内容
  481. @param uid: uid
  482. @param channel: 通道
  483. @param image: 推送图片链接
  484. @return: bool
  485. """
  486. try:
  487. url = 'https://api.xmpush.xiaomi.com/v3/message/regid'
  488. app_secret = XMPUSH_CONFIG[app_bundle_id]
  489. # 跳转类型
  490. jump_type = CommonService.get_jump_type(event_type)
  491. data = {
  492. 'title': msg_title,
  493. 'description': msg_text,
  494. 'payload': 'payload',
  495. 'restricted_package_name': app_bundle_id,
  496. 'registration_id': token_val,
  497. 'extra.channel_id': channel_id,
  498. 'extra.alert': msg_text,
  499. 'extra.msg': '',
  500. 'extra.sound': 'sound.aif',
  501. 'extra.zpush': '1',
  502. 'extra.received_at': n_time,
  503. 'extra.event_time': n_time,
  504. 'extra.event_type': event_type,
  505. 'extra.nickname': nickname,
  506. 'extra.uid': uid,
  507. 'extra.channel': channel,
  508. 'extra.jump_type': jump_type
  509. }
  510. # if image:
  511. # data['extra.notification_style_type'] = 2
  512. # data['extra.notification_bigPic_uri'] = image
  513. headers = {
  514. 'Authorization': 'key={}'.format(app_secret)
  515. }
  516. response = requests.post(url, data=data, headers=headers)
  517. LOGGER.info("小米推送返回值:{}".format(response.json()))
  518. assert response.status_code == 200
  519. return True
  520. except Exception as e:
  521. LOGGER.info("小米推送异常:{}".format(repr(e)))
  522. return False
  523. @staticmethod
  524. def android_vivopush(token_val, n_time, event_type, msg_title, msg_text, app_bundle_id='', uid='', channel='1',
  525. image='', nickname='', appBundleId='', jg_token_val=''):
  526. """
  527. vivo 推送(不支持图片)
  528. @param app_bundle_id: app包名
  529. @param appBundleId: app包名
  530. @param token_val: 推送token
  531. @param jg_token_val: 极光推送token
  532. @param event_type: 事件类型
  533. @param msg_title: 推送标题
  534. @param msg_text: 推送内容
  535. @param n_time: 当前时间
  536. @param nickname: 设备昵称
  537. @param uid: uid
  538. @param image: 推送图片链接
  539. @param channel: 通道
  540. @return: bool
  541. """
  542. try:
  543. app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
  544. # 获取redis里面的authToken
  545. if msg_title == '':
  546. msg_title = APP_BUNDLE_DICT[app_bundle_id]
  547. app_id = VIVOPUSH_CONFIG[app_bundle_id]['ID']
  548. app_key = VIVOPUSH_CONFIG[app_bundle_id]['Key']
  549. app_secret = VIVOPUSH_CONFIG[app_bundle_id]['Secret']
  550. sender = APISender(app_secret)
  551. rec = sender.get_token(app_id, app_key)
  552. # 鉴权接口调用获得authToken
  553. sender_send = APISender(app_secret)
  554. sender_send.set_token(rec['authToken'])
  555. # 跳转类型
  556. jump_type = CommonService.get_jump_type(event_type)
  557. push_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
  558. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  559. 'uid': uid, 'channel': channel, 'jump_type': jump_type
  560. }
  561. # 获取唯一标识符
  562. uid_push_qs = UidPushModel.objects.filter(token_val=token_val).values('m_code')
  563. m_code = uid_push_qs[0]['m_code'] if uid_push_qs[0]['m_code'] else ''
  564. # 推送 push_mode: 推送模式 (0:正式推送;1:测试推送,默认为0)
  565. # 推送 event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
  566. # 推送 skip_type: 跳转类型(1:打开 APP 首页 2:打开链接 3:自定义 4:打开 app 内指定页面)
  567. activity = 'vpushscheme://com.vivo.pushvideo/detail'
  568. message = PushMessage() \
  569. .reg_id(token_val) \
  570. .title(msg_title) \
  571. .content(msg_text) \
  572. .push_mode(0) \
  573. .notify_type(3) \
  574. .skip_type(4) \
  575. .skip_content(activity) \
  576. .request_id(m_code) \
  577. .classification(1) \
  578. .client_custom_map(**push_data) \
  579. .message_dict()
  580. rec = sender_send.send(message)
  581. LOGGER.info('vivo推送结果:{}, 设备uid:{}'.format(rec, uid))
  582. if rec['result'] == 0 and event_type in DATA_PUSH_EVENT_TYPE_LIST:
  583. PushObject.jpush_transparent_transmission(msg_title, msg_text, app_bundle_id, jg_token_val, push_data)
  584. return True
  585. except Exception as e:
  586. LOGGER.error('vivo推送异常,uid:{},error_line:{},error_msg:{}'.
  587. format(uid, e.__traceback__.tb_lineno, repr(e)))
  588. return False
  589. @staticmethod
  590. def android_oppopush(channel_id, nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
  591. uid='', channel='1', image='', jg_token_val=''):
  592. """
  593. android oppo 推送
  594. @param channel_id: 通知通道id
  595. @param nickname: 设备昵称
  596. @param app_bundle_id: app包id
  597. @param token_val: 推送token
  598. @param jg_token_val: 推送token
  599. @param n_time: 当前时间
  600. @param event_type: 事件类型
  601. @param msg_title: 推送标题
  602. @param msg_text: 推送内容
  603. @param uid: uid
  604. @param channel: 通道
  605. @param image: 推送图片链接
  606. @return: bool
  607. """
  608. try:
  609. """
  610. android 国内oppo APP消息提醒推送
  611. """
  612. app_key = OPPOPUSH_CONFIG[app_bundle_id]['Key']
  613. master_secret = OPPOPUSH_CONFIG[app_bundle_id]['Secret']
  614. url = 'https://api.push.oppomobile.com/'
  615. now_time = str(round(time.time() * 1000))
  616. # 1、实例化一个sha256对象
  617. sha256 = hashlib.sha256()
  618. # 2、调用update方法进行加密
  619. sha256.update((app_key + now_time + master_secret).encode('utf-8'))
  620. # 3、调用hexdigest方法,获取加密结果
  621. sign = sha256.hexdigest()
  622. # 获取auth_token
  623. get_token_url = url + 'server/v1/auth'
  624. post_data = {
  625. 'app_key': app_key,
  626. 'sign': sign,
  627. 'timestamp': now_time
  628. }
  629. headers = {'Content-Type': 'application/x-www-form-urlencoded'}
  630. response = requests.post(get_token_url, data=post_data, headers=headers)
  631. result = response.json()
  632. # 发送推送
  633. push_url = url + 'server/v1/message/notification/unicast'
  634. extra_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  635. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  636. 'uid': uid, 'channel': channel
  637. }
  638. message = {
  639. "target_type": 2,
  640. "target_value": token_val,
  641. "notification": {
  642. "title": msg_title,
  643. "content": msg_text,
  644. 'channel_id': channel_id,
  645. 'action_parameters': extra_data,
  646. 'click_action_type': 1,
  647. 'click_action_activity': OPPOPUSH_CONFIG[app_bundle_id]['click_action_activity']
  648. }
  649. }
  650. push_data = {
  651. 'auth_token': result['data']['auth_token'],
  652. 'message': json.dumps(message)
  653. }
  654. response = requests.post(push_url, data=push_data, headers=headers)
  655. LOGGER.info("oppo推送返回值:{}".format(response.json()))
  656. if response.status_code == 200 and event_type in DATA_PUSH_EVENT_TYPE_LIST:
  657. PushObject.jpush_transparent_transmission(msg_title, msg_text, app_bundle_id, jg_token_val, extra_data)
  658. return True
  659. except Exception as e:
  660. LOGGER.info("oppo推送异常:{}".format(repr(e)))
  661. return False
  662. @staticmethod
  663. def android_meizupush(token_val, n_time, event_type, msg_title, msg_text, uid='', channel='1',
  664. app_bundle_id='', appBundleId='', nickname='', image=''):
  665. """
  666. android 魅族推送(不支持图片)
  667. @param app_bundle_id: app包名
  668. @param appBundleId: app包名
  669. @param token_val: 推送token
  670. @param event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
  671. @param msg_title: 推送标题
  672. @param msg_text: 推送内容
  673. @param n_time: 当前时间
  674. @param nickname: 设备昵称
  675. @param uid: uid
  676. @param image: 推送图片链接
  677. @param channel: 通道
  678. @return: bool
  679. """
  680. try:
  681. # 获取包和AppSecret
  682. app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
  683. appId = MEIZUPUSH_CONFIG[app_bundle_id]['ID']
  684. appSecret = MEIZUPUSH_CONFIG[app_bundle_id]['AppSecret']
  685. url = 'https://server-api-push.meizu.com/garcia/api/server/push/varnished/pushByPushId'
  686. # 跳转类型
  687. jump_type = CommonService.get_jump_type(event_type)
  688. extra_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  689. 'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
  690. 'uid': uid, 'channel': channel, 'jump_type': jump_type
  691. }
  692. # 转换为json格式
  693. extra_data = json.dumps(extra_data)
  694. if msg_title == '':
  695. msg_title = APP_BUNDLE_DICT[app_bundle_id]
  696. # 拼接发送内容
  697. activity = MEIZUPUSH_CONFIG[app_bundle_id]['click_action_activity']
  698. # clickType点击动作, 0打开应用, 1打开应用页面, 2打开url页面, 3应用客户端自定义
  699. messageJson = '{"clickTypeInfo": {"activity": "%s",' \
  700. '"clickType": 1, "parameters": %s },"extra": {},' % (activity, extra_data)
  701. noticeBarInfo = ('"noticeBarInfo": {"title": "%s", "content": "%s"},' % (msg_title, msg_text))
  702. noticeExpandInfo = '"noticeExpandInfo": {"noticeExpandType": 0}, "pushTimeInfo": {"validTime": 24}}'
  703. messageJson += noticeBarInfo
  704. messageJson += noticeExpandInfo
  705. data_meizu = {
  706. 'appId': appId,
  707. 'pushIds': token_val,
  708. 'messageJson': messageJson
  709. }
  710. # 魅族MD5加密,生成密钥
  711. sign = CommonService.getMD5Sign(data=data_meizu, key=appSecret)
  712. data = {
  713. 'appId': appId,
  714. 'messageJson': messageJson,
  715. 'sign': sign,
  716. 'pushIds': token_val,
  717. }
  718. # 进行推送
  719. response = requests.post(url, data=data)
  720. LOGGER.info("uid:{},time:{},魅族推送结果:{}".format(uid, n_time, response.json()))
  721. return True
  722. except Exception as e:
  723. LOGGER.info("uid:{},time:{},魅族推送异常:{}".format(uid, n_time, repr(e)))
  724. return False
  725. @staticmethod
  726. def android_honorpush(token_val, n_time, event_type, msg_title, msg_text,
  727. uid='', channel='1', image='', app_bundle_id='', appBundleId='', channel_id='', nickname=''):
  728. """
  729. android honor 推送
  730. @param channel_id: 通知通道id
  731. @param nickname: 设备昵称
  732. @param app_bundle_id: app包id
  733. @param appBundleId: app包id
  734. @param token_val: 推送token
  735. @param n_time: 当前时间
  736. @param event_type: 事件类型
  737. @param msg_title: 推送标题
  738. @param msg_text: 推送内容
  739. @param uid: uid
  740. @param channel: 通道
  741. @param image: 推送图片链接
  742. @return: bool
  743. """
  744. app_bundle_id = appBundleId if appBundleId else app_bundle_id
  745. try:
  746. client_id = HONORPUSH_CONFIG[app_bundle_id]['client_id']
  747. client_secret = HONORPUSH_CONFIG[app_bundle_id]['client_secret']
  748. app_id = HONORPUSH_CONFIG[app_bundle_id]['app_id']
  749. get_access_token_url = 'https://iam.developer.hihonor.com/auth/token'
  750. post_data = {
  751. 'grant_type': 'client_credentials',
  752. 'client_id': client_id,
  753. 'client_secret': client_secret
  754. }
  755. headers = {'Content-Type': 'application/x-www-form-urlencoded'}
  756. access_token_response = requests.post(get_access_token_url, data=post_data, headers=headers)
  757. access_result = access_token_response.json()
  758. authorization_token = 'Bearer ' + access_result['access_token']
  759. # 发送推送
  760. push_url = 'https://push-api.cloud.hihonor.com/api/v1/{}/sendMessage'.format(app_id)
  761. headers = {'Content-Type': 'application/json', 'Authorization': authorization_token,
  762. 'timestamp': str(int(time.time()) * 1000)}
  763. # 跳转类型
  764. jump_type = CommonService.get_jump_type(event_type)
  765. extra_data = {'alert': msg_text, 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
  766. 'received_at': n_time, 'event_time': n_time, 'event_type': str(event_type),
  767. 'nickname': nickname, 'uid': uid, 'channel': channel, 'title': msg_title, 'body': msg_text,
  768. 'jump_type': jump_type
  769. }
  770. # 通知推送
  771. push_data = {
  772. "android": {
  773. "notification": {
  774. "body": msg_text,
  775. "title": msg_title,
  776. "importance": "NORMAL",
  777. "clickAction": {
  778. "type": 3
  779. }
  780. },
  781. "targetUserType": 0,
  782. "data": json.dumps(extra_data)
  783. },
  784. "token": [token_val]
  785. }
  786. response = requests.post(push_url, json=push_data, headers=headers)
  787. LOGGER.info("uid:{},时间:{},荣耀推送通知返回值:{}".format(uid, n_time, response.json()))
  788. # 一键通话透传推送
  789. if int(event_type) in DATA_PUSH_EVENT_TYPE_LIST:
  790. push_data = {
  791. "data": json.dumps(extra_data),
  792. "token": [token_val]
  793. }
  794. response = requests.post(push_url, json=push_data, headers=headers)
  795. LOGGER.info("uid:{},时间:{},荣耀透传推送返回值:{}".format(uid, n_time, response.json()))
  796. return True
  797. except Exception as e:
  798. LOGGER.info("荣耀推送异常:error_line:{},error_msg:{}".format(e.__traceback__.tb_lineno, repr(e)))
  799. return False
  800. @staticmethod
  801. def jpush_transparent_transmission(msg_title, msg_text, app_bundle_id, token_val, extra_data):
  802. """
  803. android 极光透传
  804. @param msg_title: 推送标题
  805. @param msg_text: 推送内容
  806. @param token_val: 推送token
  807. @param app_bundle_id: app包id
  808. @param extra_data: 额外数据
  809. @return: None
  810. """
  811. try:
  812. app_key = JPUSH_CONFIG[app_bundle_id]['Key']
  813. master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
  814. # 换成各自的app_key和master_secret
  815. _jpush = jpush.JPush(app_key, master_secret)
  816. push = _jpush.create_push()
  817. push.audience = jpush.registration_id(token_val)
  818. push.message = jpush.message(msg_content=msg_text, title=msg_title, extras=extra_data)
  819. push.platform = jpush.all_
  820. res = push.send()
  821. LOGGER.info('极光透传,结果:{},参数:{},令牌:{}'.format(res, extra_data, token_val))
  822. except Exception as e:
  823. LOGGER.info('jpush_transparent_transmission极光透传异常:errLine:{}, errMsg:{}, 参数:{}'.format(
  824. e.__traceback__.tb_lineno, repr(e), extra_data))