sparker.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #
  2. # Copyright (c) 2017-2018 Joe Clarke <jclarke@cisco.com>
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. #
  14. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. # SUCH DAMAGE.
  25. import requests
  26. from requests_toolbelt import MultipartEncoder
  27. from io import BytesIO
  28. import logging
  29. import time
  30. class Sparker():
  31. SPARK_API = "https://api.ciscospark.com/v1/"
  32. RETRIES = 3
  33. _headers = {
  34. 'authorization': None,
  35. 'content-type': 'application/json'
  36. }
  37. _logit = False
  38. def __init__(self, **kwargs):
  39. if 'logit' in kwargs:
  40. self._logit = kwargs['logit']
  41. if 'token' in kwargs:
  42. self._headers['authorization'] = 'Bearer ' + kwargs['token']
  43. self._team_cache = {}
  44. self._room_cache = {}
  45. @staticmethod
  46. def _request_with_retry(*args, **kwargs):
  47. backoff = 1
  48. i = 0
  49. while True:
  50. try:
  51. response = requests.request(*args, **kwargs)
  52. response.raise_for_status()
  53. return response
  54. except Exception as e:
  55. if (response.status_code != 429 and response.status_code != 503 and response.status_code != 400) or i == Sparker.RETRIES:
  56. return response
  57. time.sleep(backoff)
  58. backoff *= 2
  59. i += 1
  60. def set_token(self, token):
  61. self._headers['authorization'] = 'Bearer ' + token
  62. def check_token(self):
  63. if self._headers['authorization'] is None:
  64. if self._logit:
  65. logging.error('Spark token is not set!')
  66. else:
  67. print('Spark token is not set!')
  68. return False
  69. return True
  70. def get_message(self, mid):
  71. if not self.check_token():
  72. return None
  73. url = self.SPARK_API + 'messages' + '/' + mid
  74. try:
  75. response = Sparker._request_with_retry(
  76. 'GET', url, headers=self._headers)
  77. response.raise_for_status()
  78. except Exception as e:
  79. msg = 'Error getting message with ID {}: {}'.format(mid, e)
  80. if self._logit:
  81. logging.error(msg)
  82. else:
  83. print(msg)
  84. return None
  85. return response.json()
  86. def get_team_id(self, team):
  87. if not self.check_token():
  88. return None
  89. if team in self._team_cache:
  90. return self._team_cache[team]
  91. url = self.SPARK_API + 'teams'
  92. try:
  93. response = Sparker._request_with_retry(
  94. 'GET', url, headers=self._headers)
  95. response.raise_for_status()
  96. except Exception as e:
  97. msg = 'Error retrieving teams: {}'.format(e)
  98. if self._logit:
  99. logging.error(msg)
  100. else:
  101. print(msg)
  102. return None
  103. team_id = None
  104. for t in response.json()['items']:
  105. if t['name'] == team:
  106. self._team_cache[team] = t['id']
  107. return t['id']
  108. if team_id is None:
  109. msg = 'Error finding team ID for {}'.format(team)
  110. if self._logit:
  111. logging.error(msg)
  112. else:
  113. print(msg)
  114. return None
  115. def get_room_id(self, team_id, room):
  116. if not self.check_token():
  117. return None
  118. if team_id is None:
  119. team_id = ''
  120. if '{}:{}'.format(team_id, room) in self._room_cache:
  121. return self._room_cache['{}:{}'.format(team_id, room)]
  122. url = self.SPARK_API + 'rooms'
  123. params = {}
  124. if team_id != '':
  125. params['teamId'] = team_id
  126. try:
  127. response = Sparker._request_with_retry(
  128. 'GET', url, headers=self._headers, params=params)
  129. response.raise_for_status()
  130. except Exception as e:
  131. msg = 'Error retrieving room {}: {}'.format(room, e)
  132. if self._logit:
  133. logging.error(msg)
  134. else:
  135. print(msg)
  136. return None
  137. room_id = None
  138. for r in response.json()['items']:
  139. if r['title'] == room:
  140. self._room_cache['{}:{}'.format(team_id, room)] = r['id']
  141. return r['id']
  142. if room_id is None:
  143. msg = 'Failed to find room ID for {}'.format(room)
  144. if self._logit:
  145. logging.error(msg)
  146. else:
  147. print(msg)
  148. return None
  149. def get_members(self, team):
  150. if not self.check_token():
  151. return None
  152. team_id = None
  153. team_id = self.get_team_id(team)
  154. if team_id is None:
  155. return None
  156. url = self.SPARK_API + 'team/memberships'
  157. payload = {'teamId': team_id}
  158. try:
  159. response = Sparker._request_with_retry(
  160. 'GET', url, params=payload, headers=self._headers)
  161. response.raise_for_status()
  162. except Exception as e:
  163. msg = 'Error getting team membership: {}'.format(e)
  164. if self._logit:
  165. logging.error(msg)
  166. else:
  167. print(msg)
  168. return None
  169. return response.json()
  170. def post_to_spark(self, team, room, msg):
  171. if not self.check_token():
  172. return None
  173. team_id = None
  174. if team is not None:
  175. team_id = self.get_team_id(team)
  176. if team_id is None:
  177. return False
  178. room_id = self.get_room_id(team_id, room)
  179. if room_id is None:
  180. return False
  181. url = self.SPARK_API + 'messages'
  182. payload = {'roomId': room_id,
  183. 'markdown': msg}
  184. try:
  185. response = Sparker._request_with_retry(
  186. 'POST', url, json=payload, headers=self._headers)
  187. response.raise_for_status()
  188. except Exception as e:
  189. msg = 'Error posting message: {}'.format(e)
  190. if self._logit:
  191. logging.error(msg)
  192. else:
  193. print(msg)
  194. return False
  195. return True
  196. def post_to_spark_with_attach(self, team, room, msg, attach, fname, ftype):
  197. if not self.check_token():
  198. return None
  199. team_id = None
  200. if team is not None:
  201. team_id = self.get_team_id(team)
  202. if team_id is None:
  203. return False
  204. room_id = self.get_room_id(team_id, room)
  205. if room_id is None:
  206. return False
  207. url = self.SPARK_API + 'messages'
  208. bio = BytesIO(attach)
  209. payload = {'roomId': room_id,
  210. 'markdown': msg,
  211. 'files': (fname, bio, ftype)}
  212. m = MultipartEncoder(fields=payload)
  213. headers = self._headers
  214. headers['content-type'] = m.content_type
  215. try:
  216. response = Sparker._request_with_retry(
  217. 'POST', url, data=m, headers=headers)
  218. response.raise_for_status()
  219. except Exception as e:
  220. msg = 'Error posting message: {}'.format(e)
  221. if self._logit:
  222. logging.error(msg)
  223. else:
  224. print(msg)
  225. return False
  226. return True