_messages.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. # -*-coding:utf-8-*-
  2. #
  3. # Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import numbers
  17. import re
  18. import six
  19. class Message(object):
  20. """A message that can be sent Huawei Cloud Messaging.
  21. Args:
  22. data: A string value.
  23. notification: An instance of ``messaging.Notification`` (optional).
  24. android: An instance of ``messaging.Android`` (optional).
  25. apns: APSN related message definition
  26. web_push: Web Push related message definition
  27. token: token list, must be tuple (optional).
  28. topic: message topic, must be string (optional).
  29. condition: message condition, must be string (optional).
  30. """
  31. def __init__(self, data=None, notification=None, android=None, apns=None, web_push=None, token=None,
  32. topic=None, condition=None):
  33. MessageValidator.check_message(data, notification, android, apns, web_push, token, topic, condition)
  34. self.data = data
  35. self.notification = notification
  36. self.android = android
  37. self.apns = apns
  38. self.web_push = web_push
  39. self.token = token
  40. self.topic = topic
  41. self.condition = condition
  42. class Notification(object):
  43. """A notification that can be included in a message.
  44. Args:
  45. title: Title of the notification (optional).
  46. body: Body of the notification (optional).
  47. """
  48. def __init__(self, title=None, body=None, image=None):
  49. MessageValidator.check_notification(title, body, image)
  50. self.title = title
  51. self.body = body
  52. self.image = image
  53. # ----------------------------------------------------------------------------------------------------------------------
  54. class APNsConfig(object):
  55. """
  56. Please refer to the Apple APNS API reference:
  57. https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/\
  58. CommunicatingwithAPNs.html
  59. """
  60. def __init__(self, headers=None, payload=None, apns_hms_options=None):
  61. MessageValidator.check_apns_config(headers=headers, payload=payload, apns_hms_options=apns_hms_options)
  62. self.headers = headers
  63. self.payload = payload
  64. self.apns_hms_options = apns_hms_options
  65. class APNsHeader(object):
  66. """
  67. authorization
  68. apns-id
  69. apns-expiration
  70. apns-priority
  71. apns-topic
  72. apns-collapse-id
  73. """
  74. HEAD_AUTHORIZATION = "authorization"
  75. HEAD_APNs_ID = "apns-id"
  76. HEAD_APNs_EXPIRATION = "apns-expiration"
  77. HEAD_APNs_PRIORITY = "apns-priority"
  78. HEAD_APNs_TOPIC = "pns-topic"
  79. HEAD_APNs_COLLAPSE_ID = "apns-collapse-id"
  80. class APNsPayload(object):
  81. """
  82. APNs payload definition
  83. """
  84. def __init__(self, aps, **kwargs):
  85. MessageValidator.check_apns_payload(aps=aps)
  86. self.aps = aps
  87. self.custom_data = kwargs
  88. class APNsAps(object):
  89. """
  90. APNs aps definition: https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual\
  91. /RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1
  92. one sample is as follows:
  93. {
  94. "aps" : {
  95. "alert" : {
  96. "title" : "Game Request",
  97. "body" : "Bob wants to play poker",
  98. "action-loc-key" : "PLAY"
  99. "loc-key" : "GAME_PLAY_REQUEST_FORMAT",
  100. "loc-args" : [ "Jenna", "Frank"],
  101. "content-available" : 1
  102. },
  103. "badge" : 5,
  104. "sound" : "bingbong.aiff",
  105. },
  106. "acme1" : "bar",
  107. "acme2" : [ "bang", "whiz" ]
  108. }
  109. """
  110. def __init__(self, alert=None, badge=None, sound=None, content_available=None, category=None,
  111. thread_id=None, mutable_content=None, custom_data=None):
  112. MessageValidator.check_apns_payload_aps(alert=alert, badge=badge, sound=sound,
  113. content_available=content_available, category=category,
  114. thread_id=thread_id, mutable_content=mutable_content,
  115. custom_data=custom_data)
  116. self.alert = alert
  117. self.badge = badge
  118. self.sound = sound
  119. self.content_available = content_available
  120. self.category = category
  121. self.thread_id = thread_id
  122. self.mutable_content = mutable_content
  123. self.custom_data = custom_data
  124. class APNsAlert(object):
  125. """An alert that can be included in ``messaging.Aps``.
  126. Args:
  127. """
  128. def __init__(self, title=None, body=None, loc_key=None, loc_args=None,
  129. title_loc_key=None, title_loc_args=None, action_loc_key=None, launch_image=None,
  130. custom_data=None):
  131. MessageValidator.check_apns_payload_aps_alert(title=title, body=body, loc_key=loc_key, loc_args=loc_args,
  132. title_loc_key=title_loc_key, title_loc_args=title_loc_args,
  133. action_loc_key=action_loc_key, launch_image=launch_image,
  134. custom_data=custom_data)
  135. self.title = title
  136. self.body = body
  137. self.loc_key = loc_key
  138. self.loc_args = loc_args
  139. self.title_loc_key = title_loc_key
  140. self.title_loc_args = title_loc_args
  141. self.action_loc_key = action_loc_key
  142. self.launch_image = launch_image
  143. self.custom_data = custom_data
  144. class APNsHMSOptions(object):
  145. """Options for features provided by the FCM SDK for iOS.
  146. Args:
  147. target_user_type: Developer or Commercial enviroment
  148. """
  149. def __init__(self, target_user_type=None):
  150. MessageValidator.check_apns_hms_options(target_user_type=target_user_type)
  151. self.target_user_type = target_user_type
  152. # ----------------------------------------------------------------------------------------------------------------------
  153. class WebPushConfig(object):
  154. """
  155. Web push-specific options that can be included in a message.
  156. For Web Push Specification Reference: https://tools.ietf.org/html/rfc8030#section-5
  157. For mozilla implementation: https://developer.mozilla.org/en-US/docs/Web/API/notification
  158. """
  159. TTL_HEADER = "ttl"
  160. URGENCY_HEADER = "urgency"
  161. TOPIC_HEADER = "topic"
  162. def __init__(self, headers=None, data=None, notification=None, hms_options=None):
  163. """
  164. :param headers: A dictionary of headers (optional). Refer `Web push Specification`_ for supported headers.
  165. :param notification: A ``messaging.WebPushNotification`` to be included in the message (optional).
  166. :param hms_options: A ``WebPushHMSOptions`` instance to be included in the message(optional).
  167. """
  168. MessageValidator.check_webpush_config(headers, data, notification, hms_options)
  169. """ Refer to https://tools.ietf.org/html/rfc7240 """
  170. self.headers = headers
  171. """ message deliver to the end application directly """
  172. self.data = data
  173. """ Refer to WebPushNotification """
  174. self.notification = notification
  175. """ Refer to WebPushHMSOptions"""
  176. self.hms_options = hms_options
  177. class WebPushHeader(object):
  178. """
  179. Web Push Header, refer to: https://tools.ietf.org/html/rfc7240
  180. """
  181. def __init__(self, ttl=None, urgency=None, topic=None):
  182. MessageValidator.check_webpush_header(ttl, urgency, topic)
  183. self.ttl = ttl
  184. self.urgency = urgency
  185. self.topic = topic
  186. class WebPushNotification(object):
  187. """
  188. Web Push Notification
  189. """
  190. def __init__(self, title=None, body=None, icon=None, actions=None, badge=None, data=None, dir=None,
  191. image=None, lang=None, renotify=None, require_interaction=None, silent=None, tag=None,
  192. timestamp=None, vibrate=None):
  193. MessageValidator.check_webpush_notification(title=title, body=body, icon=icon, actions=actions, badge=badge,
  194. data=data, dir=dir, image=image, lang=lang,
  195. renotify=renotify, require_interaction=require_interaction,
  196. silent=silent, tag=tag, timestamp=timestamp, vibrate=vibrate)
  197. self.title = title
  198. self.body = body
  199. """ Refer to WebPushNotificationAction """
  200. self.actions = actions
  201. self.badge = badge
  202. self.data = data
  203. self.dir = dir
  204. self.icon = icon
  205. self.image = image
  206. self.lang = lang
  207. self.renotify = renotify
  208. self.require_interaction = require_interaction
  209. self.silent = silent
  210. self.tag = tag
  211. self.timestamp = timestamp
  212. self.vibrate = vibrate
  213. class WebPushNotificationAction(object):
  214. """
  215. The action for web push notification
  216. """
  217. def __init__(self, action=None, title=None, icon=None):
  218. """
  219. :param action:
  220. :param title:
  221. :param icon:
  222. """
  223. MessageValidator.check_webpush_notification_action(action=action, title=title, icon=icon)
  224. self.action = action
  225. self.icon = icon
  226. self.title = title
  227. class WebPushHMSOptions(object):
  228. """
  229. optional link option
  230. """
  231. def __init__(self, link=None):
  232. MessageValidator.check_webpush_hms_options(link)
  233. self.link = link
  234. # ----------------------------------------------------------------------------------------------------------------------
  235. class AndroidConfig(object):
  236. HIGH_PRIORITY = "HIGH"
  237. NORMAL_PRIORITY = "NORMAL"
  238. """
  239. Android-specific options that can be included in a message.
  240. """
  241. def __init__(self, collapse_key=None, urgency='NORMAL', ttl=None, bi_tag=None
  242. , fast_app_target=None, notification=None, data=None, category=None):
  243. MessageValidator.check_android_config(collapse_key, urgency, ttl, bi_tag, fast_app_target
  244. , notification, data)
  245. self.collapse_key = collapse_key
  246. self.urgency = urgency
  247. self.ttl = ttl
  248. self.bi_tag = bi_tag
  249. self.fast_app_target = fast_app_target
  250. self.notification = notification
  251. self.data = data
  252. self.category = category
  253. class AndroidNotification(object):
  254. PRIORITY_LOW = "LOW"
  255. PRIORITY_DEFAULT = "NORMAL"
  256. PRIORITY_HIGH = "HIGH"
  257. VISIBILITY_UNSPECIFIED = "VISIBILITY_UNSPECIFIED"
  258. PRIVATE = "PRIVATE"
  259. PUBLIC = "PUBLIC"
  260. SECRET = "SECRET"
  261. """
  262. Android-specific notification parameters.
  263. """
  264. def __init__(self, title=None, body=None, icon=None, color=None, sound=None, default_sound=None, tag=None,
  265. click_action=None, body_loc_key=None, body_loc_args=None, title_loc_key=None,
  266. title_loc_args=None, multi_lang_key=None, channel_id=None, notify_summary=None, image=None,
  267. style=None, big_title=None, big_body=None, auto_clear=None, notify_id=None, group=None, badge=None,
  268. ticker=None, auto_cancel=None, when=None, importance=None, use_default_vibrate=True,
  269. use_default_light=True, vibrate_config=None, visibility=None, light_settings=None, foreground_show=False,
  270. buttons=None):
  271. MessageValidator.check_android(title=title, body=body, icon=icon, color=color, sound=sound,
  272. default_sound=default_sound, tag=tag, click_action=click_action,
  273. body_loc_key=body_loc_key, body_loc_args=body_loc_args,
  274. title_loc_key=title_loc_key, title_loc_args=title_loc_args,
  275. multi_lang_key=multi_lang_key, channel_id=channel_id,
  276. notify_summary=notify_summary,
  277. image=image, style=style, big_title=big_title, big_body=big_body,
  278. auto_clear=auto_clear, notify_id=notify_id,
  279. group=group, badge=badge, ticker=ticker, auto_cancel=auto_cancel, when=when,
  280. importance=importance,
  281. use_default_vibrate=use_default_vibrate,
  282. use_default_light=use_default_light, vibrate_config=vibrate_config,
  283. visibility=visibility, light_settings=light_settings,
  284. foreground_show=foreground_show)
  285. self.title = title
  286. self.body = body
  287. self.icon = icon
  288. self.color = color
  289. self.sound = sound
  290. self.default_sound = default_sound
  291. self.tag = tag
  292. self.click_action = click_action
  293. self.body_loc_key = body_loc_key
  294. self.body_loc_args = body_loc_args
  295. self.title_loc_key = title_loc_key
  296. self.title_loc_args = title_loc_args
  297. self.multi_lang_key = multi_lang_key
  298. self.channel_id = channel_id
  299. self.notify_summary = notify_summary
  300. self.image = image
  301. self.style = style
  302. self.big_title = big_title
  303. self.big_body = big_body
  304. self.auto_clear = auto_clear
  305. self.notify_id = notify_id
  306. self.group = group
  307. self.badge = badge
  308. self.ticker = ticker
  309. self.auto_cancel = auto_cancel
  310. self.when = when
  311. self.importance = importance
  312. self.use_default_vibrate = use_default_vibrate
  313. self.use_default_light = use_default_light
  314. self.vibrate_config = vibrate_config
  315. self.visibility = visibility
  316. self.light_settings = light_settings
  317. self.foreground_show = foreground_show
  318. self.buttons = buttons
  319. class AndroidClickAction(object):
  320. """A ClickAction that can be included in a message.android.notification.
  321. Args:
  322. action_type: type of the android.notification (optional).
  323. intent: intent of the android.notification (optional).
  324. url: url of the android.notification (optional).
  325. action: action definition for push message
  326. 1: to specific activity of application
  327. 2: specific URL
  328. 3: to specific application
  329. """
  330. def __init__(self, action_type=None, intent=None, action=None, url=None):
  331. MessageValidator.check_click_action(action_type=action_type, intent=intent, action=action, url=url)
  332. self.action_type = action_type
  333. self.intent = intent
  334. self.action = action
  335. self.url = url
  336. class AndroidBadgeNotification(object):
  337. """A BadgeNotification that can be included in a message.android.notification.
  338. Args:
  339. add_num: message number of badge notification in the android.notification (optional).
  340. set_num: set the specific number of badge notification (optional).
  341. clazz: message class of badge notification in the android.notification (optional).
  342. """
  343. def __init__(self, add_num=None, set_num=None, clazz=None):
  344. MessageValidator.check_badge_notification(add_num=add_num, set_num=set_num, clazz=clazz)
  345. self.add_num = add_num
  346. self.set_num = set_num
  347. self.clazz = clazz
  348. class AndroidLightSettings(object):
  349. """
  350. light_settings":{
  351. "color":{
  352. "alpha":0,
  353. "red":0,
  354. "green":1,
  355. "blue":1
  356. },
  357. "light_on_duration":"3.5",
  358. "light_off_duration":"5S"
  359. }
  360. """
  361. def __init__(self, color=None, light_on_duration=None, light_off_duration=None):
  362. MessageValidator.check_light_settings(color=color, light_on_duration=light_on_duration, light_off_duration=light_off_duration)
  363. self.color = color
  364. self.light_on_duration = light_on_duration
  365. self.light_off_duration = light_off_duration
  366. class AndroidLightSettingsColor(object):
  367. """
  368. "color":{
  369. "alpha":0,
  370. "red":0,
  371. "green":1,
  372. "blue":1
  373. }
  374. """
  375. def __init__(self, alpha=None, red=None, green=None, blue=None):
  376. MessageValidator.check_light_settings_color(alpha=alpha, red=red, green=green, blue=blue)
  377. self.alpha = alpha
  378. self.red = red
  379. self.green = green
  380. self.blue = blue
  381. # --------------------------------------------------------------------------------------------------------------------
  382. class MessageValidator(object):
  383. """
  384. message validation utilities.
  385. Methods provided in this class raise ValueErrors if any validations fail.
  386. """
  387. @classmethod
  388. def check_https_url(cls, hint, value):
  389. cls.check_string(hint, value)
  390. if value is not None and not re.match(r"^https:/{2}\w.+$", value):
  391. raise ValueError('{0} must be a valid https url.'.format(hint))
  392. @classmethod
  393. def check_string(cls, hint, value, non_empty=False):
  394. """Checks if the given value is a string."""
  395. if value is None:
  396. return None
  397. if not isinstance(value, six.string_types):
  398. if non_empty:
  399. raise ValueError('{0} must be a non-empty string.'.format(hint))
  400. else:
  401. raise ValueError('{0} must be a string.'.format(hint))
  402. if non_empty and not value:
  403. raise ValueError('{0} must be a non-empty string.'.format(hint))
  404. return value
  405. @classmethod
  406. def assert_string_values(cls, hint, value, *args):
  407. """
  408. Check the class value should be an instance of string, and related values should be within *args
  409. :param hint: prompt message
  410. :param value: the real value
  411. :param args: the specific value list
  412. :return:
  413. """
  414. if value is None:
  415. return None
  416. if not isinstance(value, six.string_types):
  417. raise ValueError('{0} must be a string.'.format(hint))
  418. for v in args:
  419. if value.__eq__(v):
  420. return value
  421. raise ValueError('{} must be a value within{}.'.format(hint, args))
  422. @classmethod
  423. def check_string_list(cls, label, value):
  424. """Checks if the given value is a list comprised only of strings."""
  425. if value is None or value == []:
  426. return None
  427. if not isinstance(value, list):
  428. raise ValueError('{0} must be a list of strings.'.format(label))
  429. non_str = [k for k in value if not isinstance(k, six.string_types)]
  430. if non_str:
  431. raise ValueError('{0} must not contain non-string values.'.format(label))
  432. return value
  433. @classmethod
  434. def check_boolean(cls, hint, value):
  435. """Checks if the given value is a string."""
  436. if value is None:
  437. return None
  438. if not isinstance(value, bool):
  439. raise ValueError('{0} must be a boolean.'.format(hint))
  440. return value
  441. @classmethod
  442. def count_boolean(cls, *args):
  443. count = 0
  444. for v in args:
  445. if v:
  446. count += 1
  447. return count
  448. @classmethod
  449. def check_not_all_none(cls, hint, *args):
  450. total_size = len(args)
  451. count = 0
  452. for data in args:
  453. if data is None:
  454. count += 1
  455. if total_size == count:
  456. raise ValueError(hint)
  457. @classmethod
  458. def check_type(cls, class_obj, class_type, hint):
  459. if (class_obj is not None) and (not isinstance(class_obj, class_type)):
  460. raise ValueError(hint)
  461. @classmethod
  462. def check_type_list(cls, label, value, cls_type):
  463. """Checks if the given value is a list comprised only of numbers."""
  464. if value is None or value == []:
  465. return None
  466. if not isinstance(value, list):
  467. raise ValueError('{0} must be a list of {1}.'.format(label, cls_type))
  468. non_number = [k for k in value if not isinstance(k, cls_type)]
  469. if non_number:
  470. raise ValueError('{0} must not contain non-{1} values.'.format(label, cls_type))
  471. return value
  472. @classmethod
  473. def check_number(cls, label, value):
  474. if value is None:
  475. return None
  476. if not isinstance(value, numbers.Number):
  477. raise ValueError('{0} must be a number.'.format(label))
  478. return value
  479. @classmethod
  480. def check_number_span(cls, label, value, min, max):
  481. if value is None:
  482. return None
  483. if not isinstance(value, numbers.Number):
  484. raise ValueError('{0} must be a number.'.format(label))
  485. if value < min or value > max:
  486. raise ValueError('{0} must be within {1} to {2}.'.format(label, min, max))
  487. return value
  488. @classmethod
  489. def assert_integer_values(cls, hint, value, *args):
  490. """
  491. Check the class value should be an instance of string, and related values should be within *args
  492. :param hint: prompt message
  493. :param value: the real value
  494. :param args: the specific value list
  495. :return:
  496. """
  497. if value is None:
  498. return None
  499. if not isinstance(value, six.integer_types):
  500. raise ValueError('{0} must be a integer.'.format(hint))
  501. for v in args:
  502. if value == v:
  503. return value
  504. raise ValueError('{} must be a value within{}.'.format(hint, args))
  505. @classmethod
  506. def check_number_list(cls, label, value):
  507. if value is None or value == []:
  508. return None
  509. if not isinstance(value, list):
  510. raise ValueError('{0} must be a list of numbers.'.format(label))
  511. non_number = [k for k in value if not isinstance(k, numbers.Number)]
  512. if non_number:
  513. raise ValueError('{0} must not contain non-number values.'.format(label))
  514. return value
  515. @classmethod
  516. def check_string_dict(cls, label, value):
  517. if value is None or value == {}:
  518. return None
  519. if not isinstance(value, dict):
  520. raise ValueError('{0} must be a dictionary.'.format(label))
  521. non_str = [k for k in value if not isinstance(k, six.string_types)]
  522. if non_str:
  523. raise ValueError('{0} must not contain non-string keys.'.format(label))
  524. return value
  525. # ------------------------------------------------------------------------------------------------------------------
  526. @classmethod
  527. def check_message(cls, data, notification, android, apns, web_push, token, topic, condition):
  528. """
  529. Check whether the message parameter is valid or not
  530. :param data:
  531. :param notification:
  532. :param android:
  533. :param apns:
  534. :param web_push:
  535. :param token:
  536. :param topic:
  537. :param condition:
  538. :return:
  539. """
  540. # data must be string
  541. cls.check_string(hint="Message.data", value=data)
  542. # notification
  543. if (notification is not None) and (not isinstance(notification, Notification)):
  544. raise ValueError('notification must be an instance of Notification class')
  545. # android / APNs / Web Push
  546. # if notification message(data is None), one of android / APNs / Web Push must be present
  547. if data is None:
  548. cls.check_not_all_none('Message.data is None, one of Message.android/Message.apns/Message.webpush \
  549. must be present', android, apns, web_push)
  550. cls.check_type(android, AndroidConfig, 'android must be an instance of AndroidConfig class')
  551. cls.check_type(apns, APNsConfig, 'apns must be an instance of APNsConfig class')
  552. cls.check_type(web_push, WebPushConfig, 'web_push must be an instance of WebPushConfig class')
  553. """token, topic, condition"""
  554. # [token, topic, condition] only one not None
  555. target_count = cls.count_boolean(token is not None, topic is not None, condition is not None)
  556. if target_count != 1:
  557. raise ValueError('Exactly one of token, topic or condition must be specified.')
  558. # token must be tuple or list
  559. if token is not None:
  560. if not isinstance(token, tuple) and not isinstance(token, list):
  561. raise ValueError('token must be a tuple or a list')
  562. if len(token) > 1000:
  563. raise ValueError('token must not contain more than 1000 tokens')
  564. cls.check_string(hint="Message.topic", value=topic)
  565. cls.check_string(hint="Message.condition", value=condition)
  566. @classmethod
  567. def check_notification(cls, title, body, image):
  568. cls.check_string(hint="Notification.title", value=title)
  569. cls.check_string(hint="Notification.body", value=body)
  570. cls.check_https_url(hint="Notification.image", value=image)
  571. @classmethod
  572. def check_android_config(cls, collapse_key, urgency, ttl, bi_tag, fast_app_target, notification, data):
  573. # collapse_key
  574. cls.check_number('AndroidConfig.collapse_key', collapse_key)
  575. # urgency
  576. cls.assert_string_values("AndroidConfig.urgency", urgency, AndroidConfig.HIGH_PRIORITY,
  577. AndroidConfig.NORMAL_PRIORITY)
  578. # ttl
  579. cls.check_string(hint="AndroidConfig.ttl", value=ttl)
  580. # bi_tag
  581. cls.check_string(hint="AndroidConfig.bi_tag", value=bi_tag)
  582. # fast_app_target
  583. cls.check_number_span("AndroidConfig.fast_app_target", fast_app_target, 1, 2)
  584. # notification
  585. cls.check_type(notification, AndroidNotification,
  586. hint='notification must be an instance of AndroidNotification')
  587. # data
  588. cls.check_string(hint="AndroidConfig.data", value=data)
  589. @classmethod
  590. def check_android(cls, title, body, icon, color, sound, default_sound, tag, click_action, body_loc_key, body_loc_args,
  591. title_loc_key, title_loc_args, multi_lang_key, channel_id, notify_summary, image,
  592. style, big_title, big_body, auto_clear, notify_id, group, badge,
  593. ticker, auto_cancel, when, importance,
  594. use_default_vibrate, use_default_light, vibrate_config, visibility, light_settings,
  595. foreground_show):
  596. # title
  597. cls.check_string(hint="AndroidNotification.title", value=title)
  598. # body
  599. cls.check_string(hint="AndroidNotification.body", value=body)
  600. # icon
  601. cls.check_string(hint="AndroidNotification.icon", value=icon)
  602. # color
  603. cls.check_string(hint="AndroidNotification.color", value=color)
  604. # sound
  605. cls.check_string(hint="AndroidNotification.sound", value=sound)
  606. # default_sound
  607. cls.check_boolean(hint="AndroidNotification.default_sound", value=default_sound)
  608. # tag
  609. cls.check_string(hint="AndroidNotification.tag", value=tag)
  610. # click_action
  611. cls.check_type(click_action, AndroidClickAction,
  612. hint='click_action must be an instance of AndroidClickAction')
  613. # body_loc_key
  614. cls.check_string(hint="AndroidNotification.body_loc_key", value=body_loc_key)
  615. # body_loc_args
  616. if (body_loc_args is not None) and (not isinstance(body_loc_args, tuple)) and (not isinstance(body_loc_args, list)):
  617. raise ValueError('AndroidNotification.body_loc_args must be an instance of tuple or list')
  618. # title_loc_key
  619. cls.check_string(hint="AndroidNotification.title_loc_key", value=title_loc_key)
  620. # title_loc_args
  621. if (title_loc_args is not None) and (not isinstance(title_loc_args, tuple) and not isinstance(title_loc_args, list)):
  622. raise ValueError('AndroidNotification.title_loc_args must be an instance of tuple or list')
  623. # multi_lang_key
  624. if multi_lang_key is not None:
  625. if not isinstance(multi_lang_key, dict):
  626. raise ValueError('AndroidNotification.multi_lang_key must be a dict.')
  627. # channel_id
  628. cls.check_string(hint="AndroidNotification.channel_id", value=channel_id)
  629. # notify_summary
  630. cls.check_string(hint="AndroidNotification.notify_summary", value=notify_summary)
  631. #
  632. # image
  633. cls.check_https_url(hint="AndroidNotification.image", value=image)
  634. # style
  635. if style is not None:
  636. if style not in [0, 1, 2]:
  637. raise ValueError('AndroidNotification.style must in [0, 1, 2]')
  638. # big_title, big_body
  639. if style == 1:
  640. if (big_title is None) or (not isinstance(big_title, str)):
  641. raise ValueError('AndroidNotification.big_title must be valid string when style is 1')
  642. if (big_body is None) and (not isinstance(big_body, str)):
  643. raise ValueError('AndroidNotification.big_body must be valid string when style is 1')
  644. # auto_clear
  645. cls.check_number(label='AndroidNotification.auto_clear ', value=auto_clear)
  646. # notify_id
  647. cls.check_number(label='AndroidNotification.notify_id ', value=notify_id)
  648. # group
  649. cls.check_string(hint="AndroidNotification.group", value=group)
  650. # badge
  651. cls.check_type(badge, AndroidBadgeNotification, "badge should be an instance of AndroidBadgeNotification")
  652. # ticker
  653. cls.check_string(hint="AndroidNotification.ticker", value=ticker)
  654. # auto_cancel
  655. cls.check_boolean(hint="AndroidNotification.auto_cancel", value=auto_cancel)
  656. # when
  657. cls.check_string(hint="AndroidNotification.when", value=when)
  658. # importance
  659. cls.assert_string_values("AndroidNotification.importance", importance,
  660. AndroidNotification.PRIORITY_DEFAULT,
  661. AndroidNotification.PRIORITY_HIGH, AndroidNotification.PRIORITY_LOW)
  662. # use_default_vibrate
  663. cls.check_boolean(hint="AndroidNotification.use_default_vibrate", value=use_default_vibrate)
  664. # use_default_light
  665. cls.check_boolean(hint="AndroidNotification.use_default_light", value=use_default_light)
  666. # vibrate_config
  667. cls.check_string_list(label="AndroidNotification.vibrate_config", value=vibrate_config)
  668. # visibility
  669. cls.assert_string_values("AndroidNotification.visibility", visibility,
  670. AndroidNotification.PRIVATE,
  671. AndroidNotification.PUBLIC, AndroidNotification.SECRET,
  672. AndroidNotification.VISIBILITY_UNSPECIFIED)
  673. # light_settings
  674. cls.check_type(light_settings, AndroidLightSettings, "light_settings should be an instance of AndroidLightSettings")
  675. # foreground_show
  676. cls.check_boolean(hint="AndroidNotification.foreground_show", value=foreground_show)
  677. @classmethod
  678. def check_badge_notification(cls, add_num, set_num, clazz):
  679. # add_num must be int
  680. cls.check_number_span(label="AndroidBadgeNotification.add_num", value=add_num, min=0, max=100)
  681. # set_num must be int
  682. cls.check_number_span(label="AndroidBadgeNotification.set_num", value=set_num, min=0, max=100)
  683. # clazz
  684. cls.check_string(hint="AndroidBadgeNotification.clazz", value=clazz)
  685. @classmethod
  686. def check_click_action(cls, action_type, intent, action, url):
  687. # type must be in [1, 4]
  688. if (action_type is None) or (action_type not in [1, 2, 3, 4]):
  689. raise ValueError('ClickAction.type must be in [1, 2, 3, 4]')
  690. # intent, if type is 1, intent or action must be present or both
  691. if action_type == 1:
  692. count = cls.count_boolean(isinstance(intent, str), isinstance(action, str))
  693. if count <= 0:
  694. raise ValueError('ClickAction.intent or ClickAction.action must be present or both when click_type is 1')
  695. # url, if type is 2, url must
  696. if action_type == 2:
  697. if not isinstance(url, str):
  698. raise ValueError('ClickAction.url must when ClickAction.type is 2')
  699. if not url.upper().startswith('HTTPS'):
  700. raise ValueError('ClickAction.url must be https prefix when ClickAction.type is 2')
  701. @classmethod
  702. def check_light_settings(cls, color, light_on_duration, light_off_duration):
  703. cls.check_type(color, AndroidLightSettingsColor, "color must be an instance of AndroidLightSettingsColor")
  704. cls.check_string(hint="AndroidLightSettings.light_on_duration", value=light_on_duration)
  705. cls.check_string(hint="AndroidLightSettings.light_off_duration", value=light_off_duration)
  706. @classmethod
  707. def check_light_settings_color(cls, alpha, red, green, blue):
  708. cls.check_number("AndroidLightSettingsColor.alpha", alpha)
  709. cls.check_number("AndroidLightSettingsColor.red", red)
  710. cls.check_number("AndroidLightSettingsColor.green", green)
  711. cls.check_number("AndroidLightSettingsColor.blue", blue)
  712. @classmethod
  713. def check_webpush_config(cls, headers, data, notification, hms_options):
  714. # headers
  715. cls.check_type(headers, WebPushHeader, "headers must be an instance of WebPushHeader")
  716. cls.check_string(hint="WebPushConfig.headers", value=data)
  717. cls.check_type(notification, WebPushNotification, "notification must be an instance of WebPushNotification")
  718. cls.check_type(hms_options, WebPushHMSOptions, "hms_options must be an instance of WebPushHMSOptions")
  719. @classmethod
  720. def check_webpush_header(cls, ttl=None, urgency=None, topic=None):
  721. cls.check_string(hint="WebPushHeader.ttl", value=ttl)
  722. cls.check_string(hint="WebPushHeader.urgency", value=urgency)
  723. cls.check_string(hint="WebPushHeader.topic", value=topic)
  724. @classmethod
  725. def check_webpush_notification(cls, title=None, body=None, icon=None, actions=None, badge=None,
  726. data=None, dir=None, image=None, lang=None, renotify=None,
  727. require_interaction=None, silent=None, tag=None, timestamp=None, vibrate=None):
  728. cls.check_string(hint="WebPushNotification.title", value=title)
  729. cls.check_string(hint="WebPushNotification.body", value=body)
  730. cls.check_string(hint="WebPushNotification.icon", value=icon)
  731. cls.check_string(hint="WebPushNotification.data", value=data)
  732. cls.check_type_list("WebPushNotificationAction.actions", actions, WebPushNotificationAction)
  733. cls.check_string(hint="WebPushNotification.image", value=image)
  734. cls.check_string(hint="WebPushNotification.lang", value=lang)
  735. cls.check_string(hint="WebPushNotification.tag", value=tag)
  736. cls.check_string(hint="WebPushNotification.badge", value=badge)
  737. cls.assert_string_values("WebPushNotification.dir", dir, "auto", "ltr", "rtl")
  738. cls.check_number_list("WebPushNotification.vibrate", vibrate)
  739. cls.check_boolean("WebPushNotification.renotify", renotify)
  740. cls.check_boolean("WebPushNotification.require_interaction", require_interaction)
  741. cls.check_boolean("WebPushNotification.silent", silent)
  742. cls.check_number("WebPushNotification.timestamp", timestamp)
  743. @classmethod
  744. def check_webpush_notification_action(cls, action=None, title=None, icon=None):
  745. cls.check_string(hint="WebPushNotificationAction.action", value=action)
  746. cls.check_string(hint="WebPushNotificationAction.title", value=title)
  747. cls.check_string(hint="WebPushNotificationAction.icon", value=icon)
  748. @classmethod
  749. def check_webpush_hms_options(cls, link):
  750. cls.check_string(hint="WebPushHMSOptions.link", value=link)
  751. @classmethod
  752. def check_apns_config(cls, headers=None, payload=None, apns_hms_options=None):
  753. cls.check_string_dict("APNsConfig.headers", headers)
  754. cls.check_type(payload, APNsPayload, "payload must be an instance of APNsPayload")
  755. cls.check_type(apns_hms_options, APNsHMSOptions, "apns_hms_options must be an instance of APNsHMSOptions")
  756. @classmethod
  757. def check_apns_payload(cls, aps=None):
  758. cls.check_type(aps, APNsAps, "aps must be an instance of APNsAps")
  759. pass
  760. @classmethod
  761. def check_apns_payload_aps(cls, alert, badge, sound, content_available, category, thread_id, mutable_content,
  762. custom_data):
  763. # alert: Dictionary or String
  764. if alert is not None:
  765. if not isinstance(alert, six.string_types):
  766. cls.check_type(alert, APNsAlert, "alert must be an instance of String or APNsAlert class")
  767. # badge: Number
  768. cls.check_number("APNsAps.badge", badge)
  769. # sound: String
  770. cls.check_string("APNsAps.sound", sound)
  771. # content_available: number
  772. cls.check_number("APNsAps.content_available", content_available)
  773. # category: String
  774. cls.check_string("APNsAps.category", category)
  775. # thread_id: String
  776. cls.check_string("APNsAps.thread_id", thread_id)
  777. # mutable_content
  778. cls.check_boolean("APNsAps.mutable_content", mutable_content)
  779. # custom_data
  780. if custom_data is not None:
  781. if not isinstance(custom_data, dict):
  782. raise ValueError('APNsAps.custom_data must be a dict.')
  783. @classmethod
  784. def check_apns_payload_aps_alert(cls, title, body, loc_key, loc_args, title_loc_key, title_loc_args, action_loc_key,
  785. launch_image, custom_data):
  786. # title: String
  787. cls.check_string("APNsAlert.title", title)
  788. # body: String
  789. cls.check_string("APNsAlert.body", body)
  790. # loc_key: String
  791. cls.check_string("APNsAlert.loc_key", loc_key)
  792. # loc_args: Array of strings
  793. cls.check_string_list("APNsAlert.loc_args", loc_args)
  794. # title_loc_key: String or null
  795. cls.check_string("APNsAlert.title_loc_key", title_loc_key)
  796. # title_loc_args: Array of strings or null
  797. cls.check_string_list("APNsAlert.title_loc_args", title_loc_args)
  798. # action_loc_key: String or null
  799. cls.check_string("APNsAlert.action_loc_key", action_loc_key)
  800. # launch_image: String
  801. cls.check_string("APNsAlert.launch_image", launch_image)
  802. # custom_data
  803. if custom_data is not None:
  804. if not isinstance(custom_data, dict):
  805. raise ValueError('APNsAlert.custom_data must be a dict.')
  806. @classmethod
  807. def check_apns_hms_options(cls, target_user_type):
  808. cls.assert_integer_values("APNsHMSOptions.target_user_type", target_user_type, 1, 2, 3)