webex-bot 0.6.0__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {webex_bot-0.6.0/webex_bot.egg-info → webex_bot-0.6.2}/PKG-INFO +12 -1
  2. {webex_bot-0.6.0 → webex_bot-0.6.2}/README.md +11 -0
  3. {webex_bot-0.6.0 → webex_bot-0.6.2}/setup.cfg +1 -1
  4. {webex_bot-0.6.0 → webex_bot-0.6.2}/setup.py +1 -1
  5. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/__init__.py +1 -1
  6. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/webex_bot.py +2 -4
  7. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/websockets/webex_websocket_client.py +47 -8
  8. {webex_bot-0.6.0 → webex_bot-0.6.2/webex_bot.egg-info}/PKG-INFO +12 -1
  9. {webex_bot-0.6.0 → webex_bot-0.6.2}/CONTRIBUTING.rst +0 -0
  10. {webex_bot-0.6.0 → webex_bot-0.6.2}/LICENSE +0 -0
  11. {webex_bot-0.6.0 → webex_bot-0.6.2}/MANIFEST.in +0 -0
  12. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/Makefile +0 -0
  13. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/conf.py +0 -0
  14. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/contributing.rst +0 -0
  15. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/index.rst +0 -0
  16. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/installation.rst +0 -0
  17. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/make.bat +0 -0
  18. {webex_bot-0.6.0 → webex_bot-0.6.2}/docs/usage.rst +0 -0
  19. {webex_bot-0.6.0 → webex_bot-0.6.2}/tests/__init__.py +0 -0
  20. {webex_bot-0.6.0 → webex_bot-0.6.2}/tests/test_webex_bot.py +0 -0
  21. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/cards/__init__.py +0 -0
  22. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/commands/__init__.py +0 -0
  23. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/commands/echo.py +0 -0
  24. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/commands/help.py +0 -0
  25. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/exceptions.py +0 -0
  26. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/formatting.py +0 -0
  27. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/models/__init__.py +0 -0
  28. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/models/command.py +0 -0
  29. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/models/response.py +0 -0
  30. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot/websockets/__init__.py +0 -0
  31. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot.egg-info/SOURCES.txt +0 -0
  32. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot.egg-info/dependency_links.txt +0 -0
  33. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot.egg-info/not-zip-safe +0 -0
  34. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot.egg-info/requires.txt +0 -0
  35. {webex_bot-0.6.0 → webex_bot-0.6.2}/webex_bot.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webex_bot
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Python package for a Webex Bot based on websockets.
5
5
  Home-page: https://github.com/fbradyirl/webex_bot
6
6
  Author: Finbarr Brady
@@ -438,6 +438,15 @@ to
438
438
  bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
439
439
  ```
440
440
 
441
+
442
+ ### 0.6.1 (2025-May-19)
443
+
444
+ * Handle and retry on InvalidStatusCode in Websocket loop
445
+
446
+ ### 0.6.2 (2025-May-23)
447
+
448
+ * Fix for [issue #48][i48] - Fix for `Commands not being received` issue.
449
+
441
450
  [1]: https://github.com/aaugustin/websockets
442
451
 
443
452
  [2]: https://github.com/WebexCommunity/WebexPythonSDK
@@ -483,3 +492,5 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
483
492
  [i13]: https://github.com/fbradyirl/webex_bot/issues/13
484
493
 
485
494
  [i20]: https://github.com/fbradyirl/webex_bot/issues/20
495
+
496
+ [i48]: https://github.com/fbradyirl/webex_bot/issues/48
@@ -398,6 +398,15 @@ to
398
398
  bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
399
399
  ```
400
400
 
401
+
402
+ ### 0.6.1 (2025-May-19)
403
+
404
+ * Handle and retry on InvalidStatusCode in Websocket loop
405
+
406
+ ### 0.6.2 (2025-May-23)
407
+
408
+ * Fix for [issue #48][i48] - Fix for `Commands not being received` issue.
409
+
401
410
  [1]: https://github.com/aaugustin/websockets
402
411
 
403
412
  [2]: https://github.com/WebexCommunity/WebexPythonSDK
@@ -443,3 +452,5 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
443
452
  [i13]: https://github.com/fbradyirl/webex_bot/issues/13
444
453
 
445
454
  [i20]: https://github.com/fbradyirl/webex_bot/issues/20
455
+
456
+ [i48]: https://github.com/fbradyirl/webex_bot/issues/48
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.6.0
2
+ current_version = 0.6.2
3
3
  commit = True
4
4
  tag = True
5
5
 
@@ -45,6 +45,6 @@ setup(
45
45
  test_suite='tests',
46
46
  tests_require=test_requirements,
47
47
  url='https://github.com/fbradyirl/webex_bot',
48
- version='0.6.0',
48
+ version='0.6.2',
49
49
  zip_safe=False,
50
50
  )
@@ -1,4 +1,4 @@
1
1
  """Top-level package for Webex Bot."""
2
2
 
3
3
  __author__ = """Finbarr Brady"""
4
- __version__ = '0.6.0'
4
+ __version__ = '0.6.2'
@@ -1,8 +1,8 @@
1
1
  """Main module."""
2
2
  import logging
3
3
  import os
4
-
5
4
  import types
5
+
6
6
  import backoff
7
7
  import coloredlogs
8
8
  import requests
@@ -14,7 +14,7 @@ from webex_bot.exceptions import BotException
14
14
  from webex_bot.formatting import quote_info
15
15
  from webex_bot.models.command import CALLBACK_KEYWORD_KEY, Command, COMMAND_KEYWORD_KEY
16
16
  from webex_bot.models.response import Response
17
- from webex_bot.websockets.webex_websocket_client import WebexWebsocketClient, DEFAULT_DEVICE_URL
17
+ from webex_bot.websockets.webex_websocket_client import WebexWebsocketClient
18
18
 
19
19
  log = logging.getLogger(__name__)
20
20
 
@@ -26,7 +26,6 @@ class WebexBot(WebexWebsocketClient):
26
26
  approved_users=[],
27
27
  approved_domains=[],
28
28
  approved_rooms=[],
29
- device_url=DEFAULT_DEVICE_URL,
30
29
  include_demo_commands=False,
31
30
  bot_name="Webex Bot",
32
31
  bot_help_subtitle="Here are my available commands. Click one to begin.",
@@ -60,7 +59,6 @@ class WebexBot(WebexWebsocketClient):
60
59
  teams_bot_token,
61
60
  on_message=self.process_incoming_message,
62
61
  on_card_action=self.process_incoming_card_action,
63
- device_url=device_url,
64
62
  proxies=proxies)
65
63
 
66
64
  if help_command is None:
@@ -10,6 +10,7 @@ import certifi
10
10
  import requests
11
11
  import websockets
12
12
  from webexpythonsdk import WebexAPI
13
+ from websockets.exceptions import InvalidStatusCode
13
14
 
14
15
  try:
15
16
  from websockets_proxy import Proxy, proxy_connect
@@ -19,7 +20,7 @@ except ImportError:
19
20
 
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
- DEFAULT_DEVICE_URL = "https://wdm-a.wbx2.com/wdm/api/v1"
23
+ DEFAULT_U2C_URL = "https://u2c.wbx2.com/u2c/api/v1/catalog"
23
24
 
24
25
  DEVICE_DATA = {
25
26
  "deviceName": "pywebsocket-client",
@@ -40,14 +41,13 @@ MAX_BACKOFF_TIME = 240
40
41
  class WebexWebsocketClient(object):
41
42
  def __init__(self,
42
43
  access_token,
43
- device_url=DEFAULT_DEVICE_URL,
44
44
  on_message=None,
45
45
  on_card_action=None,
46
46
  proxies=None):
47
47
  self.access_token = access_token
48
48
  self.teams = WebexAPI(access_token=access_token, proxies=proxies)
49
- self.device_url = device_url
50
49
  self.device_info = None
50
+ self.device_url = self._get_device_url()
51
51
  self.on_message = on_message
52
52
  self.on_card_action = on_card_action
53
53
  self.proxies = proxies
@@ -154,6 +154,25 @@ class WebexWebsocketClient(object):
154
154
  asyncio.run(self.websocket.send(json.dumps(ack_message)))
155
155
  logger.info(f"WebSocket ack message with id={message_id}. Complete.")
156
156
 
157
+ def _get_device_url(self):
158
+ headers = {}
159
+ headers["Authorization"] = 'Bearer ' + self.access_token
160
+
161
+ params = {"format": "hostmap"}
162
+ response = requests.get(DEFAULT_U2C_URL, headers=headers, params=params)
163
+
164
+ # check for 401 Unauthorized
165
+ if response.status_code == 401:
166
+ logger.error("Unauthorized access. Please check your access token.")
167
+ raise Exception("Unauthorized access. Please check your access token.")
168
+
169
+ data = response.json()
170
+
171
+ wdm_url = data["serviceLinks"].get("wdm") # or whatever key your hostmap uses
172
+ logging.info(f"wdm url: {wdm_url}")
173
+ return wdm_url
174
+
175
+
157
176
  def _get_device_info(self, check_existing=True):
158
177
  """
159
178
  Get device info from Webex Cloud.
@@ -193,6 +212,9 @@ class WebexWebsocketClient(object):
193
212
  logger.error('could not get/create device info')
194
213
  raise Exception("No WDM device info")
195
214
 
215
+ # Pull out URL now so we can log it on failure
216
+ ws_url = self.device_info.get('webSocketUrl')
217
+
196
218
  async def _websocket_recv():
197
219
  message = await self.websocket.recv()
198
220
  logger.debug("WebSocket Received Message(raw): %s\n" % message)
@@ -204,10 +226,17 @@ class WebexWebsocketClient(object):
204
226
  logger.warning(
205
227
  f"An exception occurred while processing message. Ignoring. {messageProcessingException}")
206
228
 
207
- @backoff.on_exception(backoff.expo, websockets.ConnectionClosedError, max_time=MAX_BACKOFF_TIME)
208
- @backoff.on_exception(backoff.expo, websockets.ConnectionClosedOK, max_time=MAX_BACKOFF_TIME)
209
- @backoff.on_exception(backoff.expo, websockets.ConnectionClosed, max_time=MAX_BACKOFF_TIME)
210
- @backoff.on_exception(backoff.expo, socket.gaierror, max_time=MAX_BACKOFF_TIME)
229
+ @backoff.on_exception(
230
+ backoff.expo,
231
+ (
232
+ websockets.ConnectionClosedError,
233
+ websockets.ConnectionClosedOK,
234
+ websockets.ConnectionClosed,
235
+ socket.gaierror,
236
+ InvalidStatusCode,
237
+ ),
238
+ max_time=MAX_BACKOFF_TIME
239
+ )
211
240
  async def _connect_and_listen():
212
241
  ws_url = self.device_info['webSocketUrl']
213
242
  logger.info(f"Opening websocket connection to {ws_url}")
@@ -237,10 +266,20 @@ class WebexWebsocketClient(object):
237
266
 
238
267
  try:
239
268
  asyncio.get_event_loop().run_until_complete(_connect_and_listen())
269
+ except InvalidStatusCode as e:
270
+ logger.error(f"WebSocket handshake to {ws_url} failed with status {e.status_code}")
271
+ if e.status_code == 404:
272
+ logger.info("Refreshing WDM device info and retrying...")
273
+ self._get_device_info(check_existing=False)
274
+ # update ws_url before retry
275
+ ws_url = self.device_info.get('webSocketUrl')
276
+ asyncio.get_event_loop().run_until_complete(_connect_and_listen())
277
+ else:
278
+ raise
240
279
  except Exception as runException:
241
280
  logger.error(f"runException: {runException}")
242
281
  if self._get_device_info(check_existing=False) is None:
243
282
  logger.error('could not create device info')
244
283
  raise Exception("No WDM device info")
245
284
  # trigger re-connect
246
- asyncio.get_event_loop().run_until_complete(_connect_and_listen())
285
+ asyncio.get_event_loop().run_until_complete(_connect_and_listen())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webex_bot
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Python package for a Webex Bot based on websockets.
5
5
  Home-page: https://github.com/fbradyirl/webex_bot
6
6
  Author: Finbarr Brady
@@ -438,6 +438,15 @@ to
438
438
  bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
439
439
  ```
440
440
 
441
+
442
+ ### 0.6.1 (2025-May-19)
443
+
444
+ * Handle and retry on InvalidStatusCode in Websocket loop
445
+
446
+ ### 0.6.2 (2025-May-23)
447
+
448
+ * Fix for [issue #48][i48] - Fix for `Commands not being received` issue.
449
+
441
450
  [1]: https://github.com/aaugustin/websockets
442
451
 
443
452
  [2]: https://github.com/WebexCommunity/WebexPythonSDK
@@ -483,3 +492,5 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
483
492
  [i13]: https://github.com/fbradyirl/webex_bot/issues/13
484
493
 
485
494
  [i20]: https://github.com/fbradyirl/webex_bot/issues/20
495
+
496
+ [i48]: https://github.com/fbradyirl/webex_bot/issues/48
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes