webex-bot 1.0.7__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.7'
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
@@ -102,7 +102,8 @@ class WebexBot(WebexWebsocketClient):
102
102
  """
103
103
  me = self.teams.people.me()
104
104
  self.bot_display_name = me.displayName
105
- log.info(f"Running as {me.type} '{me.displayName}' with email {me.emails}")
105
+ self.bot_email = me.emails[0]
106
+ log.info(f"Running as {me.type} '{me.displayName}' with email {self.bot_email}")
106
107
  log.debug(f"Running as bot '{me}'")
107
108
  return me
108
109
 
@@ -173,7 +174,7 @@ class WebexBot(WebexWebsocketClient):
173
174
  if member.personEmail == user_email:
174
175
  is_user_member = True
175
176
  except webexpythonsdk.exceptions.ApiError as apie:
176
- log.warn(f"API error: {apie}")
177
+ log.warning(f"API error: {apie}")
177
178
  return is_user_member
178
179
 
179
180
  def process_incoming_card_action(self, attachment_actions, activity):
@@ -207,12 +208,15 @@ class WebexBot(WebexWebsocketClient):
207
208
  is_one_on_one_space = 'ONE_ON_ONE' in activity['target']['tags']
208
209
 
209
210
  if activity['actor']['type'] != 'PERSON':
211
+ if self.bot_email == user_email:
212
+ log.warning(f"Message is from myself ({self.bot_email}), ignoring.")
213
+ return
210
214
  if not self.allow_bot_to_bot:
211
- log.warning(f"Message is from a bot ({user_email}), ignoring")
215
+ log.warning(f"Message is from a bot ({user_email}), ignoring.")
212
216
  return
213
217
  else:
214
218
  log.warning(
215
- f"Message is from a bot ({user_email}), and allow_bot_to_bot is {self.allow_bot_to_bot}. Be careful not to create a message loop!")
219
+ f"Message is from another bot ({user_email}), and allow_bot_to_bot is {self.allow_bot_to_bot}. Be careful not to create a message loop!")
216
220
 
217
221
  # Log details on message
218
222
  log.info(f"Message from {user_email}: {teams_message}")
@@ -304,6 +308,8 @@ class WebexBot(WebexWebsocketClient):
304
308
  log.info(f"delete_previous_message is True. Deleting message with ID: {previous_message_id}")
305
309
  self.teams.messages.delete(previous_message_id)
306
310
 
311
+ pre_reply_message_id = None
312
+
307
313
  if not is_card_callback_command and command.card is not None:
308
314
  response = Response()
309
315
  response.text = "This bot requires a client which can render cards."
@@ -316,7 +322,7 @@ class WebexBot(WebexWebsocketClient):
316
322
  message=message_without_command,
317
323
  teams_message=teams_message,
318
324
  activity=activity)
319
- 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)
320
326
  reply = response
321
327
  else:
322
328
  log.debug(f"Going to run command: '{command}' with input: '{message_without_command}'")
@@ -324,25 +330,36 @@ class WebexBot(WebexWebsocketClient):
324
330
  message=message_without_command,
325
331
  teams_message=teams_message,
326
332
  activity=activity)
327
- 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)
328
334
  reply, reply_one_to_one = self.run_command_and_handle_bot_exceptions(command=command,
329
335
  message=message_without_command,
330
336
  teams_message=teams_message,
331
337
  activity=activity)
332
338
  log.info(f"Using thread id={thread_parent_id}")
333
- 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
334
350
 
335
351
  def do_reply(self, reply, room_id, user_email, reply_one_to_one, is_one_on_one_space, conv_target_id):
336
352
  # allow command handlers to craft their own Teams message
353
+ created_message_id = None
337
354
  if reply and isinstance(reply, Response):
338
355
  # If the Response lacks a roomId, set it to the incoming room
339
356
  if not reply.roomId:
340
357
  reply.roomId = room_id
341
358
  if not reply.parentId and conv_target_id and self.threads:
342
359
  reply.parentId = conv_target_id
343
- reply = reply.as_dict()
344
- self.teams.messages.create(**reply)
345
- reply = "ok"
360
+ reply_dict = reply.as_dict()
361
+ created = self.teams.messages.create(**reply_dict)
362
+ created_message_id = getattr(created, 'id', None)
346
363
  # Support returning a list of Responses
347
364
  elif reply and (isinstance(reply, list) or isinstance(reply, types.GeneratorType)):
348
365
  for response in reply:
@@ -352,24 +369,24 @@ class WebexBot(WebexWebsocketClient):
352
369
  response.roomId = room_id
353
370
  if not response.parentId and conv_target_id:
354
371
  response.parentId = conv_target_id
355
- self.teams.messages.create(**response.as_dict())
372
+ created = self.teams.messages.create(**response.as_dict())
373
+ created_message_id = getattr(created, 'id', None)
356
374
  else:
357
375
  # Just a plain message
358
- self.send_message_to_room_or_person(user_email,
359
- room_id,
360
- reply_one_to_one,
361
- is_one_on_one_space,
362
- response,
363
- conv_target_id)
364
- 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)
365
382
  elif reply:
366
- self.send_message_to_room_or_person(user_email,
367
- room_id,
368
- reply_one_to_one,
369
- is_one_on_one_space,
370
- reply,
371
- conv_target_id)
372
- 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
373
390
 
374
391
  def send_message_to_room_or_person(self,
375
392
  user_email,
@@ -380,27 +397,29 @@ class WebexBot(WebexWebsocketClient):
380
397
  conv_target_id):
381
398
  default_move_to_one_to_one_heads_up = \
382
399
  quote_info(f"{user_email} I've messaged you 1-1. Please reply to me there.")
400
+ last_created = None
383
401
  if reply_one_to_one:
384
402
  if not is_one_on_one_space:
385
403
  if self.threads:
386
- self.teams.messages.create(roomId=room_id,
387
- markdown=default_move_to_one_to_one_heads_up,
388
- 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)
389
407
  else:
390
- self.teams.messages.create(roomId=room_id,
391
- 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)
392
410
  if self.threads:
393
- self.teams.messages.create(toPersonEmail=user_email,
394
- markdown=reply,
395
- parentId=conv_target_id)
411
+ last_created = self.teams.messages.create(toPersonEmail=user_email,
412
+ markdown=reply,
413
+ parentId=conv_target_id)
396
414
  else:
397
- self.teams.messages.create(toPersonEmail=user_email,
398
- markdown=reply)
415
+ last_created = self.teams.messages.create(toPersonEmail=user_email,
416
+ markdown=reply)
399
417
  else:
400
418
  if self.threads:
401
- 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)
402
420
  else:
403
- 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)
404
423
 
405
424
  def run_pre_card_load_reply(self, command, message, teams_message, activity):
406
425
  """
@@ -409,7 +428,7 @@ class WebexBot(WebexWebsocketClient):
409
428
  try:
410
429
  return command.pre_card_load_reply(message, teams_message, activity), False
411
430
  except BotException as e:
412
- log.warn(f"BotException: {e.debug_message}")
431
+ log.warning(f"BotException: {e.debug_message}")
413
432
  return e.reply_message, e.reply_one_to_one
414
433
 
415
434
  def run_pre_execute(self, command, message, teams_message, activity):
@@ -419,14 +438,14 @@ class WebexBot(WebexWebsocketClient):
419
438
  try:
420
439
  return command.pre_execute(message, teams_message, activity), False
421
440
  except BotException as e:
422
- log.warn(f"BotException: {e.debug_message}")
441
+ log.warning(f"BotException: {e.debug_message}")
423
442
  return e.reply_message, e.reply_one_to_one
424
443
 
425
444
  def run_command_and_handle_bot_exceptions(self, command, message, teams_message, activity):
426
445
  try:
427
446
  return command.card_callback(message, teams_message, activity), False
428
447
  except BotException as e:
429
- log.warn(f"BotException: {e.debug_message}")
448
+ log.warning(f"BotException: {e.debug_message}")
430
449
  return e.reply_message, e.reply_one_to_one
431
450
 
432
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.7
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
 
@@ -460,10 +447,18 @@ bot = WebexBot(teams_bot_token=os.getenv("WEBEX_ACCESS_TOKEN")
460
447
 
461
448
  * Update UA
462
449
 
463
- ### 1.0.7 (2025-Sept-17)
450
+ ### 1.0.8 (2025-Sept-18)
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=khfK-5S8GGJi-F6Y8AYhEOeZtF7W7B2IBROMjDG_t6U,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=fuqucqKCZ-jFo4qW3XAYo7n5g_LB3GQnuAuQHEZRw_Y,21866
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.7.dist-info/licenses/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
15
- webex_bot-1.0.7.dist-info/METADATA,sha256=ZtQy9gjLJT6gFgrXLk94wBqn9ZGXZViQ2ATs2VX28_I,14887
16
- webex_bot-1.0.7.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
17
- webex_bot-1.0.7.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
18
- webex_bot-1.0.7.dist-info/RECORD,,