webex-bot 1.0.4__py2.py3-none-any.whl → 1.0.6__py2.py3-none-any.whl

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.
webex_bot/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Top-level package for Webex Bot."""
2
2
 
3
3
  __author__ = """Finbarr Brady"""
4
- __version__ = '1.0.4'
4
+ __version__ = '1.0.6'
webex_bot/webex_bot.py CHANGED
@@ -30,6 +30,7 @@ class WebexBot(WebexWebsocketClient):
30
30
  bot_name="Webex Bot",
31
31
  bot_help_subtitle="Here are my available commands. Click one to begin.",
32
32
  threads=True,
33
+ allow_bot_to_bot=False,
33
34
  help_command=None,
34
35
  log_level="INFO",
35
36
  proxies=None):
@@ -41,10 +42,11 @@ class WebexBot(WebexWebsocketClient):
41
42
  @param approved_domains: List of domains which are allowed to chat to this bot.
42
43
  @param approved_rooms: List of rooms whose members are allowed to chat to this bot.
43
44
  @param device_url: WDM Url
44
- @param include_demo_commands: If True, any demo commands will be included.
45
+ @param include_demo_commands: If True, any demo commands will be included. (default False)
45
46
  @param bot_name: Your custom name for the bot.
46
47
  @param bot_help_subtitle: Text to show in the help card.
47
- @param threads: If True, respond to msg by creating a thread.
48
+ @param threads: If True, respond to msg by creating a thread. (default True)
49
+ @param allow_bot_to_bot: If True, incoming messages from other bots will be processed. Use with caution to avoid message loops. (default False)
48
50
  @param help_command: If None, use internal HelpCommand, otherwise override.
49
51
  @param log_level: Set loggin level.
50
52
  @param proxies: Dictionary of proxies for connections.
@@ -57,6 +59,7 @@ class WebexBot(WebexWebsocketClient):
57
59
  log.info("Registering bot with Webex cloud")
58
60
  WebexWebsocketClient.__init__(self,
59
61
  teams_bot_token,
62
+ bot_name,
60
63
  on_message=self.process_incoming_message,
61
64
  on_card_action=self.process_incoming_card_action,
62
65
  proxies=proxies)
@@ -90,6 +93,7 @@ class WebexBot(WebexWebsocketClient):
90
93
  self.approval_parameters_check()
91
94
  self.bot_display_name = ""
92
95
  self.threads = threads
96
+ self.allow_bot_to_bot = allow_bot_to_bot
93
97
 
94
98
  @backoff.on_exception(backoff.expo, requests.exceptions.ConnectionError)
95
99
  def get_me_info(self):
@@ -202,9 +206,12 @@ class WebexBot(WebexWebsocketClient):
202
206
  raw_message = teams_message.text
203
207
  is_one_on_one_space = 'ONE_ON_ONE' in activity['target']['tags']
204
208
 
205
- if activity['actor']['type'] != 'PERSON':
206
- log.debug('message is from a bot, ignoring')
209
+ if activity['actor']['type'] != 'PERSON' and not self.allow_bot_to_bot:
210
+ log.warning('Message is from a bot, ignoring')
207
211
  return
212
+ else:
213
+ log.warning(
214
+ f"Message is from a bot and allow_bot_to_bot is {self.allow_bot_to_bot}. Be careful not to create a message loop!")
208
215
 
209
216
  # Log details on message
210
217
  log.info(f"Message from {user_email}: {teams_message}")
@@ -43,6 +43,7 @@ MAX_BACKOFF_TIME = 240
43
43
  class WebexWebsocketClient(object):
44
44
  def __init__(self,
45
45
  access_token,
46
+ bot_name,
46
47
  on_message=None,
47
48
  on_card_action=None,
48
49
  proxies=None):
@@ -50,9 +51,10 @@ class WebexWebsocketClient(object):
50
51
  self.teams = WebexAPI(access_token=access_token, proxies=proxies)
51
52
  self.tracking_id = f"webex-bot_{uuid.uuid4()}"
52
53
  self.session = requests.Session()
53
- self.session.headers = self._get_headers()
54
54
  sdk_ua = self.teams._session.headers["User-Agent"]
55
- self.teams._session.update_headers(self._get_headers(add_to_ua=f" ({sdk_ua})"))
55
+ self.add_to_ua = f" '{bot_name}' ({sdk_ua})"
56
+ self.session.headers = self._get_headers()
57
+ self.teams._session.update_headers(self._get_headers())
56
58
  # log the tracking ID
57
59
  logger.info(f"Tracking ID: {self.tracking_id}")
58
60
  self.device_info = None
@@ -69,11 +71,11 @@ class WebexWebsocketClient(object):
69
71
  if proxy_connect is None:
70
72
  raise ImportError("Failed to load libraries for proxy, maybe forgot [proxy] option during installation.")
71
73
 
72
- def _get_headers(self, add_to_ua=''):
74
+ def _get_headers(self):
73
75
  return {
74
76
  "Authorization": f"Bearer {self.access_token}",
75
77
  "Content-type": "application/json;charset=utf-8",
76
- "User-Agent": f"webex_bot/{__version__}{add_to_ua}",
78
+ "User-Agent": f"webex_bot/{__version__}{self.add_to_ua}",
77
79
  "trackingid": self.tracking_id
78
80
  }
79
81
 
@@ -279,7 +281,7 @@ class WebexWebsocketClient(object):
279
281
  # Track the number of consecutive 404 errors to prevent infinite loops
280
282
  max_404_retries = 3
281
283
  current_404_retries = 0
282
-
284
+
283
285
  while True:
284
286
  try:
285
287
  asyncio.get_event_loop().run_until_complete(_connect_and_listen())
@@ -287,19 +289,19 @@ class WebexWebsocketClient(object):
287
289
  break
288
290
  except InvalidStatusCode as e:
289
291
  logger.error(f"WebSocket handshake to {ws_url} failed with status {e.status_code}")
290
-
292
+
291
293
  if e.status_code == 404:
292
294
  current_404_retries += 1
293
295
  if current_404_retries >= max_404_retries:
294
296
  logger.error(f"Reached maximum retries ({max_404_retries}) for 404 errors. Giving up.")
295
297
  raise Exception(f"Unable to connect to WebSocket after {max_404_retries} attempts. Device registration may be invalid.")
296
-
298
+
297
299
  logger.info(f"Refreshing WDM device info and retrying... (Attempt {current_404_retries} of {max_404_retries})")
298
300
  # Force a new device registration
299
301
  self._get_device_info(check_existing=False)
300
302
  # Update ws_url with the new device info
301
303
  ws_url = self.device_info.get('webSocketUrl')
302
-
304
+
303
305
  # Add a delay before retrying to avoid hammering the server
304
306
  logger.info(f"Waiting 5 seconds before retry attempt {current_404_retries}...")
305
307
  asyncio.get_event_loop().run_until_complete(asyncio.sleep(5))
@@ -308,15 +310,15 @@ class WebexWebsocketClient(object):
308
310
  raise
309
311
  except Exception as runException:
310
312
  logger.error(f"runException: {runException}")
311
-
313
+
312
314
  # Check if we can get device info
313
315
  if self._get_device_info(check_existing=False) is None:
314
316
  logger.error('could not create device info')
315
317
  raise Exception("No WDM device info")
316
-
318
+
317
319
  # Update the URL in case it changed
318
320
  ws_url = self.device_info.get('webSocketUrl')
319
-
321
+
320
322
  # Wait a bit before reconnecting
321
323
  logger.info("Waiting 5 seconds before attempting to reconnect...")
322
324
  asyncio.get_event_loop().run_until_complete(asyncio.sleep(5))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webex_bot
3
- Version: 1.0.4
3
+ Version: 1.0.6
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
@@ -455,7 +455,14 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
455
455
  ### 1.0.4 (2025-Jul-01)
456
456
 
457
457
  * Add retry mechanism with backoff for websocket 404 errors
458
- *
458
+
459
+ ### 1.0.5 (2025-Sept-11)
460
+
461
+ * Update UA
462
+
463
+ ### 1.0.6 (2025-Sept-17)
464
+
465
+ * Allow flag to disable bot to bot check
459
466
 
460
467
  [1]: https://github.com/aaugustin/websockets
461
468
 
@@ -1,7 +1,7 @@
1
- webex_bot/__init__.py,sha256=Tzm9k-JrR1ZSrXQY5OyBg6ljQWMbUK_6TZJCUgCGfBU,95
1
+ webex_bot/__init__.py,sha256=qc-1SbTkooTynnF9zQGCoDfMfosolKxMvBBa8Mw_qFk,95
2
2
  webex_bot/exceptions.py,sha256=qs9yVitfJtvxwBMC8uCvTDOxUQ_oZjWFf1dU8Oaue14,740
3
3
  webex_bot/formatting.py,sha256=jvPKym-z8CIJygpPVTVbt6vFXQo9_HQHpRDJB-nh-SI,382
4
- webex_bot/webex_bot.py,sha256=QRia8QBXnWNt6iwj1kfpcWgPi7RT_DoQfqtdUUbsUTo,21278
4
+ webex_bot/webex_bot.py,sha256=tlogg1mu30et-O38B8eNMbHIHhs0g2bwL6uZEOSMDeg,21802
5
5
  webex_bot/cards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  webex_bot/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  webex_bot/commands/echo.py,sha256=STY-MikBRjUteDK-g8G4GPB0V-Yi7siPN1DRMXrT-QU,3399
@@ -10,9 +10,9 @@ webex_bot/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
10
10
  webex_bot/models/command.py,sha256=MyThlDaEkGlj1fDE_i_wr79O3QboakimRme8yI744yo,5327
11
11
  webex_bot/models/response.py,sha256=d4k2ohR5SUVzvuQzcnm7jQQVTMB0gH9Kz9y09vkoAaU,2545
12
12
  webex_bot/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- webex_bot/websockets/webex_websocket_client.py,sha256=qVbYquh_FJ-rDZeWspcDydBJOw96U3UcYZSX2XfYYoY,13964
14
- webex_bot-1.0.4.dist-info/licenses/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
15
- webex_bot-1.0.4.dist-info/METADATA,sha256=Ni8h1xfMT6ijtlWsucOGK9yqTM2S0GbN3Jw1CAbZFjw,14782
16
- webex_bot-1.0.4.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
17
- webex_bot-1.0.4.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
18
- webex_bot-1.0.4.dist-info/RECORD,,
13
+ webex_bot/websockets/webex_websocket_client.py,sha256=ZlyTyNZuWr7cbf0mHBPu7JB432gVwBKT2Gvq9K1Pv5k,13895
14
+ webex_bot-1.0.6.dist-info/licenses/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
15
+ webex_bot-1.0.6.dist-info/METADATA,sha256=bxwRlGcbQlHJ5nA4qAbuxJ2eyo4NY0WroHetB0rlhbg,14887
16
+ webex_bot-1.0.6.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
17
+ webex_bot-1.0.6.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
18
+ webex_bot-1.0.6.dist-info/RECORD,,