webex-bot 1.0.8__py2.py3-none-any.whl → 1.1.12__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.8'
4
+ __version__ = '1.1.12'
@@ -17,6 +17,7 @@ class EchoCommand(Command):
17
17
  super().__init__(
18
18
  command_keyword="echo",
19
19
  help_message="Echo Words Back to You!",
20
+ delete_previous_message=True,
20
21
  chained_commands=[EchoCallback()])
21
22
 
22
23
  def pre_execute(self, message, attachment_actions, activity):
webex_bot/webex_bot.py CHANGED
@@ -174,7 +174,7 @@ class WebexBot(WebexWebsocketClient):
174
174
  if member.personEmail == user_email:
175
175
  is_user_member = True
176
176
  except webexpythonsdk.exceptions.ApiError as apie:
177
- log.warn(f"API error: {apie}")
177
+ log.warning(f"API error: {apie}")
178
178
  return is_user_member
179
179
 
180
180
  def process_incoming_card_action(self, attachment_actions, activity):
@@ -308,6 +308,8 @@ class WebexBot(WebexWebsocketClient):
308
308
  log.info(f"delete_previous_message is True. Deleting message with ID: {previous_message_id}")
309
309
  self.teams.messages.delete(previous_message_id)
310
310
 
311
+ pre_reply_message_id = None
312
+
311
313
  if not is_card_callback_command and command.card is not None:
312
314
  response = Response()
313
315
  response.text = "This bot requires a client which can render cards."
@@ -320,7 +322,7 @@ class WebexBot(WebexWebsocketClient):
320
322
  message=message_without_command,
321
323
  teams_message=teams_message,
322
324
  activity=activity)
323
- self.do_reply(pre_card_load_reply, room_id, user_email, pre_card_load_reply_one_to_one, is_one_on_one_space, thread_parent_id)
325
+ pre_reply_message_id = self.do_reply(pre_card_load_reply, room_id, user_email, pre_card_load_reply_one_to_one, is_one_on_one_space, thread_parent_id)
324
326
  reply = response
325
327
  else:
326
328
  log.debug(f"Going to run command: '{command}' with input: '{message_without_command}'")
@@ -328,25 +330,36 @@ class WebexBot(WebexWebsocketClient):
328
330
  message=message_without_command,
329
331
  teams_message=teams_message,
330
332
  activity=activity)
331
- self.do_reply(pre_execute_reply, room_id, user_email, pre_execute_reply_one_to_one, is_one_on_one_space, thread_parent_id)
333
+ pre_reply_message_id = self.do_reply(pre_execute_reply, room_id, user_email, pre_execute_reply_one_to_one, is_one_on_one_space, thread_parent_id)
332
334
  reply, reply_one_to_one = self.run_command_and_handle_bot_exceptions(command=command,
333
335
  message=message_without_command,
334
336
  teams_message=teams_message,
335
337
  activity=activity)
336
338
  log.info(f"Using thread id={thread_parent_id}")
337
- return self.do_reply(reply, room_id, user_email, reply_one_to_one, is_one_on_one_space, thread_parent_id)
339
+ final_message_id = self.do_reply(reply, room_id, user_email, reply_one_to_one, is_one_on_one_space, thread_parent_id)
340
+
341
+ # If requested, delete the pre-execute (or pre-card-load) message once the final reply has been sent
342
+ if command.delete_previous_message and pre_reply_message_id:
343
+ try:
344
+ log.info(f"Deleting pre-execute message with ID: {pre_reply_message_id}")
345
+ self.teams.messages.delete(pre_reply_message_id)
346
+ except Exception as e:
347
+ log.warning(f"Failed to delete pre-execute message {pre_reply_message_id}: {e}")
348
+
349
+ return final_message_id
338
350
 
339
351
  def do_reply(self, reply, room_id, user_email, reply_one_to_one, is_one_on_one_space, conv_target_id):
340
352
  # allow command handlers to craft their own Teams message
353
+ created_message_id = None
341
354
  if reply and isinstance(reply, Response):
342
355
  # If the Response lacks a roomId, set it to the incoming room
343
356
  if not reply.roomId:
344
357
  reply.roomId = room_id
345
358
  if not reply.parentId and conv_target_id and self.threads:
346
359
  reply.parentId = conv_target_id
347
- reply = reply.as_dict()
348
- self.teams.messages.create(**reply)
349
- reply = "ok"
360
+ reply_dict = reply.as_dict()
361
+ created = self.teams.messages.create(**reply_dict)
362
+ created_message_id = getattr(created, 'id', None)
350
363
  # Support returning a list of Responses
351
364
  elif reply and (isinstance(reply, list) or isinstance(reply, types.GeneratorType)):
352
365
  for response in reply:
@@ -356,24 +369,24 @@ class WebexBot(WebexWebsocketClient):
356
369
  response.roomId = room_id
357
370
  if not response.parentId and conv_target_id:
358
371
  response.parentId = conv_target_id
359
- self.teams.messages.create(**response.as_dict())
372
+ created = self.teams.messages.create(**response.as_dict())
373
+ created_message_id = getattr(created, 'id', None)
360
374
  else:
361
375
  # Just a plain message
362
- self.send_message_to_room_or_person(user_email,
363
- room_id,
364
- reply_one_to_one,
365
- is_one_on_one_space,
366
- response,
367
- conv_target_id)
368
- reply = "ok"
376
+ created_message_id = self.send_message_to_room_or_person(user_email,
377
+ room_id,
378
+ reply_one_to_one,
379
+ is_one_on_one_space,
380
+ response,
381
+ conv_target_id)
369
382
  elif reply:
370
- self.send_message_to_room_or_person(user_email,
371
- room_id,
372
- reply_one_to_one,
373
- is_one_on_one_space,
374
- reply,
375
- conv_target_id)
376
- return reply
383
+ created_message_id = self.send_message_to_room_or_person(user_email,
384
+ room_id,
385
+ reply_one_to_one,
386
+ is_one_on_one_space,
387
+ reply,
388
+ conv_target_id)
389
+ return created_message_id
377
390
 
378
391
  def send_message_to_room_or_person(self,
379
392
  user_email,
@@ -384,27 +397,29 @@ class WebexBot(WebexWebsocketClient):
384
397
  conv_target_id):
385
398
  default_move_to_one_to_one_heads_up = \
386
399
  quote_info(f"{user_email} I've messaged you 1-1. Please reply to me there.")
400
+ last_created = None
387
401
  if reply_one_to_one:
388
402
  if not is_one_on_one_space:
389
403
  if self.threads:
390
- self.teams.messages.create(roomId=room_id,
391
- markdown=default_move_to_one_to_one_heads_up,
392
- parentId=conv_target_id)
404
+ last_created = self.teams.messages.create(roomId=room_id,
405
+ markdown=default_move_to_one_to_one_heads_up,
406
+ parentId=conv_target_id)
393
407
  else:
394
- self.teams.messages.create(roomId=room_id,
395
- markdown=default_move_to_one_to_one_heads_up)
408
+ last_created = self.teams.messages.create(roomId=room_id,
409
+ markdown=default_move_to_one_to_one_heads_up)
396
410
  if self.threads:
397
- self.teams.messages.create(toPersonEmail=user_email,
398
- markdown=reply,
399
- parentId=conv_target_id)
411
+ last_created = self.teams.messages.create(toPersonEmail=user_email,
412
+ markdown=reply,
413
+ parentId=conv_target_id)
400
414
  else:
401
- self.teams.messages.create(toPersonEmail=user_email,
402
- markdown=reply)
415
+ last_created = self.teams.messages.create(toPersonEmail=user_email,
416
+ markdown=reply)
403
417
  else:
404
418
  if self.threads:
405
- self.teams.messages.create(roomId=room_id, markdown=reply, parentId=conv_target_id)
419
+ last_created = self.teams.messages.create(roomId=room_id, markdown=reply, parentId=conv_target_id)
406
420
  else:
407
- self.teams.messages.create(roomId=room_id, markdown=reply)
421
+ last_created = self.teams.messages.create(roomId=room_id, markdown=reply)
422
+ return getattr(last_created, 'id', None)
408
423
 
409
424
  def run_pre_card_load_reply(self, command, message, teams_message, activity):
410
425
  """
@@ -413,7 +428,7 @@ class WebexBot(WebexWebsocketClient):
413
428
  try:
414
429
  return command.pre_card_load_reply(message, teams_message, activity), False
415
430
  except BotException as e:
416
- log.warn(f"BotException: {e.debug_message}")
431
+ log.warning(f"BotException: {e.debug_message}")
417
432
  return e.reply_message, e.reply_one_to_one
418
433
 
419
434
  def run_pre_execute(self, command, message, teams_message, activity):
@@ -423,14 +438,14 @@ class WebexBot(WebexWebsocketClient):
423
438
  try:
424
439
  return command.pre_execute(message, teams_message, activity), False
425
440
  except BotException as e:
426
- log.warn(f"BotException: {e.debug_message}")
441
+ log.warning(f"BotException: {e.debug_message}")
427
442
  return e.reply_message, e.reply_one_to_one
428
443
 
429
444
  def run_command_and_handle_bot_exceptions(self, command, message, teams_message, activity):
430
445
  try:
431
446
  return command.card_callback(message, teams_message, activity), False
432
447
  except BotException as e:
433
- log.warn(f"BotException: {e.debug_message}")
448
+ log.warning(f"BotException: {e.debug_message}")
434
449
  return e.reply_message, e.reply_one_to_one
435
450
 
436
451
  @staticmethod
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import json
3
+ import inspect
3
4
  import logging
4
5
  import socket
5
6
  import ssl
@@ -10,7 +11,10 @@ import certifi
10
11
  import requests
11
12
  import websockets
12
13
  from webexpythonsdk import WebexAPI
13
- from websockets.exceptions import InvalidStatusCode
14
+ try:
15
+ from websockets import InvalidStatus
16
+ except ImportError: # pragma: no cover - fallback for older websockets versions
17
+ from websockets.exceptions import InvalidStatus
14
18
 
15
19
  from webex_bot import __version__
16
20
 
@@ -79,6 +83,20 @@ class WebexWebsocketClient(object):
79
83
  "trackingid": self.tracking_id
80
84
  }
81
85
 
86
+ def _get_websocket_connect_kwargs(self, connect_func):
87
+ headers = self._get_headers()
88
+ try:
89
+ params = inspect.signature(connect_func).parameters
90
+ except (TypeError, ValueError):
91
+ return {"extra_headers": headers}
92
+
93
+ if "extra_headers" in params:
94
+ return {"extra_headers": headers}
95
+ if "additional_headers" in params:
96
+ return {"additional_headers": headers}
97
+
98
+ return {"extra_headers": headers}
99
+
82
100
  def _process_incoming_websocket_message(self, msg):
83
101
  """
84
102
  Handle websocket data.
@@ -247,7 +265,7 @@ class WebexWebsocketClient(object):
247
265
  websockets.ConnectionClosedOK,
248
266
  websockets.ConnectionClosed,
249
267
  socket.gaierror,
250
- InvalidStatusCode,
268
+ InvalidStatus,
251
269
  ),
252
270
  max_time=MAX_BACKOFF_TIME
253
271
  )
@@ -258,14 +276,28 @@ class WebexWebsocketClient(object):
258
276
  if self.proxies and "wss" in self.proxies:
259
277
  logger.info(f"Using proxy for websocket connection: {self.proxies['wss']}")
260
278
  proxy = Proxy.from_url(self.proxies["wss"])
261
- connect = proxy_connect(ws_url, ssl=ssl_context, proxy=proxy, extra_headers=self._get_headers())
279
+ connect = proxy_connect(
280
+ ws_url,
281
+ ssl=ssl_context,
282
+ proxy=proxy,
283
+ **self._get_websocket_connect_kwargs(proxy_connect),
284
+ )
262
285
  elif self.proxies and "https" in self.proxies:
263
286
  logger.info(f"Using proxy for websocket connection: {self.proxies['https']}")
264
287
  proxy = Proxy.from_url(self.proxies["https"])
265
- connect = proxy_connect(ws_url, ssl=ssl_context, proxy=proxy, extra_headers=self._get_headers())
288
+ connect = proxy_connect(
289
+ ws_url,
290
+ ssl=ssl_context,
291
+ proxy=proxy,
292
+ **self._get_websocket_connect_kwargs(proxy_connect),
293
+ )
266
294
  else:
267
295
  logger.debug(f"Not using proxy for websocket connection.")
268
- connect = websockets.connect(ws_url, ssl=ssl_context, extra_headers=self._get_headers())
296
+ connect = websockets.connect(
297
+ ws_url,
298
+ ssl=ssl_context,
299
+ **self._get_websocket_connect_kwargs(websockets.connect),
300
+ )
269
301
 
270
302
  async with connect as _websocket:
271
303
  self.websocket = _websocket
@@ -287,10 +319,11 @@ class WebexWebsocketClient(object):
287
319
  asyncio.get_event_loop().run_until_complete(_connect_and_listen())
288
320
  # If we get here, the connection was successful, so break out of the loop
289
321
  break
290
- except InvalidStatusCode as e:
291
- logger.error(f"WebSocket handshake to {ws_url} failed with status {e.status_code}")
322
+ except InvalidStatus as e:
323
+ status_code = getattr(e.response, "status_code", None)
324
+ logger.error(f"WebSocket handshake to {ws_url} failed with status {status_code}")
292
325
 
293
- if e.status_code == 404:
326
+ if status_code == 404:
294
327
  current_404_retries += 1
295
328
  if current_404_retries >= max_404_retries:
296
329
  logger.error(f"Reached maximum retries ({max_404_retries}) for 404 errors. Giving up.")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: webex_bot
3
- Version: 1.0.8
3
+ Version: 1.1.12
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
@@ -18,29 +18,16 @@ Classifier: Programming Language :: Python :: 3.13
18
18
  Requires-Python: >=3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
- Requires-Dist: webexpythonsdk==2.0.4
21
+ Requires-Dist: webexpythonsdk==2.0.5
22
22
  Requires-Dist: coloredlogs
23
- Requires-Dist: websockets==11.0.3
23
+ Requires-Dist: websockets==16
24
24
  Requires-Dist: backoff
25
25
  Provides-Extra: proxy
26
- Requires-Dist: websockets_proxy>=0.1.1; extra == "proxy"
27
- Dynamic: author
28
- Dynamic: author-email
29
- Dynamic: classifier
30
- Dynamic: description
31
- Dynamic: description-content-type
32
- Dynamic: home-page
33
- Dynamic: keywords
34
- Dynamic: license
35
- Dynamic: license-file
36
- Dynamic: provides-extra
37
- Dynamic: requires-dist
38
- Dynamic: requires-python
39
- Dynamic: summary
26
+ Requires-Dist: websockets-proxy>=0.1.3; extra == "proxy"
40
27
 
41
28
  # Introduction
42
29
 
43
- [![Pypi](https://img.shields.io/pypi/v/webex_bot.svg)](https://pypi.python.org/pypi/webex_bot) [![Build Status](https://github.com/fbradyirl/webex_bot/workflows/Python%20package/badge.svg)](https://github.com/fbradyirl/webex_bot/actions)
30
+ [![Pypi](https://img.shields.io/pypi/v/webex_bot.svg)](https://pypi.python.org/pypi/webex_bot) [![Build Status](https://github.com/fbradyirl/webex_bot/workflows/Python%20package/badge.svg)](https://github.com/fbradyirl/webex_bot/actions) [![CodeQL](https://github.com/fbradyirl/webex_bot/actions/workflows/codeql.yml/badge.svg)](https://github.com/fbradyirl/webex_bot/actions/workflows/codeql.yml) [![Coverage](https://codecov.io/gh/fbradyirl/webex_bot/branch/main/graph/badge.svg)](https://codecov.io/gh/fbradyirl/webex_bot) [![Release](https://img.shields.io/github/v/release/fbradyirl/webex_bot?sort=semver)](https://github.com/fbradyirl/webex_bot/releases) [![Downloads](https://img.shields.io/pypi/dm/webex_bot)](https://pypi.python.org/pypi/webex_bot)
44
31
 
45
32
  > [!IMPORTANT]
46
33
  > This repository is only sporadically maintained. Breaking API changes will be maintained on a best efforts basis.
@@ -50,7 +37,7 @@ Dynamic: summary
50
37
  > Bug reports unrelated to API changes may not get the attention you want.
51
38
 
52
39
 
53
- By using this module, you can create a [Webex Teams][5] messaging bot quickly in just a couple of lines of code.
40
+ By using this module, you can create a [Webex][5] messaging bot quickly in just a couple of lines of code.
54
41
 
55
42
  This module does not require you to set up an ngrok tunnel to receive incoming messages when behind a firewall or
56
43
  inside a LAN. This package instead uses a websocket to receive messages from the Webex cloud.
@@ -75,7 +62,7 @@ You can find a sample project, using OpenAI/ChatGPT with this library here: http
75
62
 
76
63
  ----
77
64
 
78
- **Only Python 3.13 is tested at this time.**
65
+ **Python 3.10, 3.11, and 3.12 are tested at this time.**
79
66
 
80
67
  1. Install this module from pypi:
81
68
 
@@ -113,7 +100,7 @@ proxies = {
113
100
  # Create a Bot Object
114
101
  bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN"),
115
102
  approved_rooms=['06586d8d-6aad-4201-9a69-0bf9eeb5766e'],
116
- bot_name="My Teams Ops Bot",
103
+ bot_name="My Webex Ops Bot",
117
104
  include_demo_commands=True,
118
105
  proxies=proxies)
119
106
 
@@ -464,6 +451,14 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
464
451
 
465
452
  * Allow flag to disable bot to bot check
466
453
 
454
+ ### 1.1.6 (2026-Feb-02)
455
+
456
+ * Update dependancies.
457
+ * Expand test coverage and add CI matrix for Python 3.10–3.12
458
+ * Add coverage reporting and update contributor docs
459
+ * Refresh websocket status handling to avoid deprecations
460
+
461
+
467
462
  [1]: https://github.com/aaugustin/websockets
468
463
 
469
464
  [2]: https://github.com/WebexCommunity/WebexPythonSDK
@@ -0,0 +1,18 @@
1
+ webex_bot/__init__.py,sha256=be-WC6WXfNEs7SDFXNjf9a9eBf9j3BJeRd6KAFiPOLI,96
2
+ webex_bot/exceptions.py,sha256=qs9yVitfJtvxwBMC8uCvTDOxUQ_oZjWFf1dU8Oaue14,740
3
+ webex_bot/formatting.py,sha256=jvPKym-z8CIJygpPVTVbt6vFXQo9_HQHpRDJB-nh-SI,382
4
+ webex_bot/webex_bot.py,sha256=IDCxIbVWyZX0SkDIcvb_iquEcrElXvYQdElDunvxTmA,23350
5
+ webex_bot/cards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ webex_bot/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ webex_bot/commands/echo.py,sha256=DwKAFHVEP0LrSj0l35uDQQhoeh9w62wUvP5C-IankeM,3441
8
+ webex_bot/commands/help.py,sha256=n7sbnJHepNSqUgZ0-8PW37kbGsaqvGIk2-jYy6I7ch4,3470
9
+ webex_bot/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ webex_bot/models/command.py,sha256=MyThlDaEkGlj1fDE_i_wr79O3QboakimRme8yI744yo,5327
11
+ webex_bot/models/response.py,sha256=d4k2ohR5SUVzvuQzcnm7jQQVTMB0gH9Kz9y09vkoAaU,2545
12
+ webex_bot/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ webex_bot/websockets/webex_websocket_client.py,sha256=ZIK4GFF0i_EqdDbQj5Sdc6Q0u-nWVWjMbq4Ex4_wToM,14911
14
+ webex_bot-1.1.12.dist-info/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
15
+ webex_bot-1.1.12.dist-info/METADATA,sha256=D2tHNMEEQLaSnn7wzEo6lCFwxYZU2VaQ85WxpRl4TiY,15358
16
+ webex_bot-1.1.12.dist-info/WHEEL,sha256=YqkG3aKUxtNWfdKBz3DzkIakbboD_OAzmgL1ryH-ru4,110
17
+ webex_bot-1.1.12.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
18
+ webex_bot-1.1.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: bdist_wheel (0.46.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,18 +0,0 @@
1
- webex_bot/__init__.py,sha256=qoZvkulvaJ2p1Nz8u_sfKjX7O-TAr7RW7zA3ej0n2O0,95
2
- webex_bot/exceptions.py,sha256=qs9yVitfJtvxwBMC8uCvTDOxUQ_oZjWFf1dU8Oaue14,740
3
- webex_bot/formatting.py,sha256=jvPKym-z8CIJygpPVTVbt6vFXQo9_HQHpRDJB-nh-SI,382
4
- webex_bot/webex_bot.py,sha256=eQSsUX0lYQDVjh7DXX1Or5E8I-8gIKh4vQ3FzGoXAq4,22069
5
- webex_bot/cards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- webex_bot/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- webex_bot/commands/echo.py,sha256=STY-MikBRjUteDK-g8G4GPB0V-Yi7siPN1DRMXrT-QU,3399
8
- webex_bot/commands/help.py,sha256=n7sbnJHepNSqUgZ0-8PW37kbGsaqvGIk2-jYy6I7ch4,3470
9
- webex_bot/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- webex_bot/models/command.py,sha256=MyThlDaEkGlj1fDE_i_wr79O3QboakimRme8yI744yo,5327
11
- webex_bot/models/response.py,sha256=d4k2ohR5SUVzvuQzcnm7jQQVTMB0gH9Kz9y09vkoAaU,2545
12
- webex_bot/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- webex_bot/websockets/webex_websocket_client.py,sha256=ZlyTyNZuWr7cbf0mHBPu7JB432gVwBKT2Gvq9K1Pv5k,13895
14
- webex_bot-1.0.8.dist-info/licenses/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
15
- webex_bot-1.0.8.dist-info/METADATA,sha256=xwpWPEPi6fOBO6-64MK80YOAc-F80k2-Iq5Hkx0XzSw,14887
16
- webex_bot-1.0.8.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
17
- webex_bot-1.0.8.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
18
- webex_bot-1.0.8.dist-info/RECORD,,