webex-bot 0.5.0__py2.py3-none-any.whl → 0.5.2__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 +1 -1
- webex_bot/models/command.py +5 -2
- webex_bot/webex_bot.py +20 -7
- webex_bot/websockets/webex_websocket_client.py +32 -5
- {webex_bot-0.5.0.dist-info → webex_bot-0.5.2.dist-info}/METADATA +52 -9
- {webex_bot-0.5.0.dist-info → webex_bot-0.5.2.dist-info}/RECORD +9 -9
- {webex_bot-0.5.0.dist-info → webex_bot-0.5.2.dist-info}/WHEEL +1 -1
- {webex_bot-0.5.0.dist-info → webex_bot-0.5.2.dist-info}/LICENSE +0 -0
- {webex_bot-0.5.0.dist-info → webex_bot-0.5.2.dist-info}/top_level.txt +0 -0
webex_bot/__init__.py
CHANGED
webex_bot/models/command.py
CHANGED
|
@@ -9,8 +9,9 @@ COMMAND_KEYWORD_KEY = "command_keyword"
|
|
|
9
9
|
|
|
10
10
|
class Command(ABC):
|
|
11
11
|
|
|
12
|
-
def __init__(self, command_keyword=None,
|
|
13
|
-
|
|
12
|
+
def __init__(self, command_keyword=None, exact_command_keyword_match=False,
|
|
13
|
+
chained_commands=[], card=None,
|
|
14
|
+
help_message=None, delete_previous_message=False,
|
|
14
15
|
card_callback_keyword=None, approved_rooms=None):
|
|
15
16
|
"""
|
|
16
17
|
Create a new bot command.
|
|
@@ -25,6 +26,7 @@ class Command(ABC):
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
@param command_keyword: (optional) Text indicating a phrase to invoke this card.
|
|
29
|
+
@param exact_command_keyword_match: If True, there will be an exact command_keyword match performed. If False, then a sub-string match will be performed. Default: False.
|
|
28
30
|
@param chained_commands: (optional) List of other commands related
|
|
29
31
|
to this command. This allows multiple related cards to be added at once.
|
|
30
32
|
@param card: (deprecated) A dict representation of the JSON card.
|
|
@@ -37,6 +39,7 @@ class Command(ABC):
|
|
|
37
39
|
@param approved_rooms: If defined, only members of these spaces will be allowed to run this command. Default: None (everyone)
|
|
38
40
|
"""
|
|
39
41
|
self.command_keyword = command_keyword
|
|
42
|
+
self.exact_command_keyword_match = exact_command_keyword_match
|
|
40
43
|
self.help_message = help_message
|
|
41
44
|
self.card = card
|
|
42
45
|
self.pre_card_callback = self.execute
|
webex_bot/webex_bot.py
CHANGED
|
@@ -31,7 +31,8 @@ class WebexBot(WebexWebsocketClient):
|
|
|
31
31
|
bot_help_subtitle="Here are my available commands. Click one to begin.",
|
|
32
32
|
threads=True,
|
|
33
33
|
help_command=None,
|
|
34
|
-
log_level="INFO"
|
|
34
|
+
log_level="INFO",
|
|
35
|
+
proxies=None):
|
|
35
36
|
"""
|
|
36
37
|
Initialise WebexBot.
|
|
37
38
|
|
|
@@ -46,7 +47,7 @@ class WebexBot(WebexWebsocketClient):
|
|
|
46
47
|
@param threads: If True, respond to msg by creating a thread.
|
|
47
48
|
@param help_command: If None, use internal HelpCommand, otherwise override.
|
|
48
49
|
@param log_level: Set loggin level.
|
|
49
|
-
|
|
50
|
+
@param proxies: Dictionary of proxies for connections.
|
|
50
51
|
"""
|
|
51
52
|
|
|
52
53
|
coloredlogs.install(level=os.getenv("LOG_LEVEL", log_level),
|
|
@@ -58,7 +59,8 @@ class WebexBot(WebexWebsocketClient):
|
|
|
58
59
|
teams_bot_token,
|
|
59
60
|
on_message=self.process_incoming_message,
|
|
60
61
|
on_card_action=self.process_incoming_card_action,
|
|
61
|
-
device_url=device_url
|
|
62
|
+
device_url=device_url,
|
|
63
|
+
proxies=proxies)
|
|
62
64
|
|
|
63
65
|
if help_command is None:
|
|
64
66
|
self.help_command = HelpCommand(
|
|
@@ -232,10 +234,21 @@ class WebexBot(WebexWebsocketClient):
|
|
|
232
234
|
|
|
233
235
|
if not is_card_callback_command and c.command_keyword:
|
|
234
236
|
log.debug(f"c.command_keyword: {c.command_keyword}")
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
log.info(f"exact_command_keyword_match: {c.exact_command_keyword_match}")
|
|
238
|
+
log.info(f"user_command: {user_command}")
|
|
239
|
+
log.info(f"command_keyword: {c.command_keyword}")
|
|
240
|
+
if c.exact_command_keyword_match: # Check if the "exact_command_keyword_match" flag is set to True
|
|
241
|
+
if user_command == c.command_keyword:
|
|
242
|
+
log.info("Exact match found!")
|
|
243
|
+
command=c
|
|
244
|
+
# If a command was found, stop looking for others
|
|
245
|
+
break
|
|
246
|
+
else: # Enter here if the "exact_command_keyword_match" flag is set to False
|
|
247
|
+
if user_command.find(c.command_keyword) != -1:
|
|
248
|
+
log.info("Sub-string match found!")
|
|
249
|
+
command = c
|
|
250
|
+
# If a command was found, stop looking for others
|
|
251
|
+
break
|
|
239
252
|
else:
|
|
240
253
|
log.debug(f"card_callback_keyword: {c.card_callback_keyword}")
|
|
241
254
|
if user_command == c.command_keyword or user_command == c.card_callback_keyword:
|
|
@@ -11,6 +11,12 @@ import requests
|
|
|
11
11
|
import websockets
|
|
12
12
|
from webexteamssdk import WebexTeamsAPI
|
|
13
13
|
|
|
14
|
+
try:
|
|
15
|
+
from websockets_proxy import Proxy, proxy_connect
|
|
16
|
+
except ImportError:
|
|
17
|
+
Proxy = None
|
|
18
|
+
proxy_connect = None
|
|
19
|
+
|
|
14
20
|
logger = logging.getLogger(__name__)
|
|
15
21
|
|
|
16
22
|
DEFAULT_DEVICE_URL = "https://wdm-a.wbx2.com/wdm/api/v1"
|
|
@@ -36,16 +42,23 @@ class WebexWebsocketClient(object):
|
|
|
36
42
|
access_token,
|
|
37
43
|
device_url=DEFAULT_DEVICE_URL,
|
|
38
44
|
on_message=None,
|
|
39
|
-
on_card_action=None
|
|
45
|
+
on_card_action=None,
|
|
46
|
+
proxies=None):
|
|
40
47
|
self.access_token = access_token
|
|
41
|
-
self.teams = WebexTeamsAPI(access_token=access_token)
|
|
48
|
+
self.teams = WebexTeamsAPI(access_token=access_token, proxies=proxies)
|
|
42
49
|
self.device_url = device_url
|
|
43
50
|
self.device_info = None
|
|
44
51
|
self.on_message = on_message
|
|
45
52
|
self.on_card_action = on_card_action
|
|
53
|
+
self.proxies = proxies
|
|
46
54
|
self.websocket = None
|
|
47
55
|
self.share_id = None
|
|
48
56
|
|
|
57
|
+
if self.proxies:
|
|
58
|
+
# Connecting through a proxy
|
|
59
|
+
if proxy_connect is None:
|
|
60
|
+
raise ImportError("Failed to load libraries for proxy, maybe forgot [proxy] option during installation.")
|
|
61
|
+
|
|
49
62
|
def _process_incoming_websocket_message(self, msg):
|
|
50
63
|
"""
|
|
51
64
|
Handle websocket data.
|
|
@@ -124,7 +137,8 @@ class WebexWebsocketClient(object):
|
|
|
124
137
|
f"{verb}/{activity_id}")
|
|
125
138
|
headers = {"Authorization": f"Bearer {self.access_token}"}
|
|
126
139
|
conversation_message = requests.get(conversation_message_url,
|
|
127
|
-
headers=headers
|
|
140
|
+
headers=headers,
|
|
141
|
+
proxies=self.proxies).json()
|
|
128
142
|
logger.debug(f"conversation_message={conversation_message}")
|
|
129
143
|
return conversation_message['id']
|
|
130
144
|
|
|
@@ -197,7 +211,20 @@ class WebexWebsocketClient(object):
|
|
|
197
211
|
async def _connect_and_listen():
|
|
198
212
|
ws_url = self.device_info['webSocketUrl']
|
|
199
213
|
logger.info(f"Opening websocket connection to {ws_url}")
|
|
200
|
-
|
|
214
|
+
|
|
215
|
+
if self.proxies and "wss" in self.proxies:
|
|
216
|
+
logger.info(f"Using proxy for websocket connection: {self.proxies['wss']}")
|
|
217
|
+
proxy = Proxy.from_url(self.proxies["wss"])
|
|
218
|
+
connect = proxy_connect(ws_url, ssl=ssl_context, proxy=proxy)
|
|
219
|
+
elif self.proxies and "https" in self.proxies:
|
|
220
|
+
logger.info(f"Using proxy for websocket connection: {self.proxies['https']}")
|
|
221
|
+
proxy = Proxy.from_url(self.proxies["https"])
|
|
222
|
+
connect = proxy_connect(ws_url, ssl=ssl_context, proxy=proxy)
|
|
223
|
+
else:
|
|
224
|
+
logger.debug(f"Not using proxy for websocket connection.")
|
|
225
|
+
connect = websockets.connect(ws_url, ssl=ssl_context)
|
|
226
|
+
|
|
227
|
+
async with connect as _websocket:
|
|
201
228
|
self.websocket = _websocket
|
|
202
229
|
logger.info("WebSocket Opened.")
|
|
203
230
|
msg = {'id': str(uuid.uuid4()),
|
|
@@ -216,4 +243,4 @@ class WebexWebsocketClient(object):
|
|
|
216
243
|
logger.error('could not create device info')
|
|
217
244
|
raise Exception("No WDM device info")
|
|
218
245
|
# trigger re-connect
|
|
219
|
-
asyncio.get_event_loop().run_until_complete(_connect_and_listen())
|
|
246
|
+
asyncio.get_event_loop().run_until_complete(_connect_and_listen())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: webex-bot
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.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
|
|
@@ -17,15 +17,25 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
17
17
|
Requires-Python: >=3.8
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
-
Requires-Dist: webexteamssdk
|
|
20
|
+
Requires-Dist: webexteamssdk==1.6.1
|
|
21
21
|
Requires-Dist: coloredlogs
|
|
22
|
-
Requires-Dist: websockets
|
|
22
|
+
Requires-Dist: websockets==11.0.3
|
|
23
23
|
Requires-Dist: backoff
|
|
24
|
+
Provides-Extra: proxy
|
|
25
|
+
Requires-Dist: websockets-proxy>=0.1.1; extra == "proxy"
|
|
24
26
|
|
|
25
27
|
# Introduction
|
|
26
28
|
|
|
27
29
|
[](https://pypi.python.org/pypi/webex_bot) [](https://github.com/fbradyirl/webex_bot/actions)
|
|
28
30
|
|
|
31
|
+
> [!IMPORTANT]
|
|
32
|
+
> This repository is only sporadically maintained. Breaking API changes will be maintained on a best efforts basis.
|
|
33
|
+
>
|
|
34
|
+
> Collaborators are welcome, as are PRs for enhancements.
|
|
35
|
+
>
|
|
36
|
+
> Bug reports unrelated to API changes may not get the attention you want.
|
|
37
|
+
|
|
38
|
+
|
|
29
39
|
By using this module, you can create a [Webex Teams][5] messaging bot quickly in just a couple of lines of code.
|
|
30
40
|
|
|
31
41
|
This module does not require you to set up an ngrok tunnel to receive incoming messages when behind a firewall or
|
|
@@ -57,6 +67,10 @@ You can find a sample project, using OpenAI/ChatGPT with this library here: http
|
|
|
57
67
|
|
|
58
68
|
`pip install webex_bot`
|
|
59
69
|
|
|
70
|
+
If you need optional proxy support, use this command instead:
|
|
71
|
+
|
|
72
|
+
`pip install webex_bot[proxy]`
|
|
73
|
+
|
|
60
74
|
2. On the Webex Developer portal, create a new [bot token][3] and expose it as an environment variable.
|
|
61
75
|
|
|
62
76
|
```sh
|
|
@@ -75,11 +89,19 @@ import os
|
|
|
75
89
|
from webex_bot.commands.echo import EchoCommand
|
|
76
90
|
from webex_bot.webex_bot import WebexBot
|
|
77
91
|
|
|
92
|
+
# (Optional) Proxy configuration
|
|
93
|
+
# Supports https or wss proxy, wss prioritized.
|
|
94
|
+
proxies = {
|
|
95
|
+
'https': 'http://proxy.esl.example.com:80',
|
|
96
|
+
'wss': 'socks5://proxy.esl.example.com:1080'
|
|
97
|
+
}
|
|
98
|
+
|
|
78
99
|
# Create a Bot Object
|
|
79
100
|
bot = WebexBot(teams_bot_token=os.getenv("WEBEX_TEAMS_ACCESS_TOKEN"),
|
|
80
101
|
approved_rooms=['06586d8d-6aad-4201-9a69-0bf9eeb5766e'],
|
|
81
102
|
bot_name="My Teams Ops Bot",
|
|
82
|
-
include_demo_commands=True
|
|
103
|
+
include_demo_commands=True,
|
|
104
|
+
proxies=proxies)
|
|
83
105
|
|
|
84
106
|
# Add new commands for the bot to listen out for.
|
|
85
107
|
bot.add_command(EchoCommand())
|
|
@@ -357,13 +379,20 @@ and off you go!
|
|
|
357
379
|
|
|
358
380
|
### 0.5.0 (2024-Apr-25)
|
|
359
381
|
|
|
360
|
-
* Add max backoff time (#55)
|
|
361
|
-
* Attached files. Help, threading and log level overrides. (#54)
|
|
362
|
-
* add stop() call to gracefully exit the bot (#42)
|
|
363
|
-
* feat(20231212): add help image size parameter (#46)
|
|
364
|
-
* update websockets to 11.0.3 (#43)
|
|
382
|
+
* Add max backoff time ([#55][pr55])
|
|
383
|
+
* Attached files. Help, threading and log level overrides. ([#54][pr54])
|
|
384
|
+
* add stop() call to gracefully exit the bot ([#42][pr42])
|
|
385
|
+
* feat(20231212): add help image size parameter ([#46][pr46])
|
|
386
|
+
* update websockets to 11.0.3 ([#43][pr43])
|
|
365
387
|
* Fix for help_command syntax issue
|
|
366
388
|
|
|
389
|
+
### 0.5.1 (2024-Apr-25)
|
|
390
|
+
|
|
391
|
+
* Add Proxy Support. ([#56][pr56])
|
|
392
|
+
|
|
393
|
+
### 0.5.2 (2024-Aug-21)
|
|
394
|
+
|
|
395
|
+
* Introduce exact_command_keyword_match feature ([#59][pr59])
|
|
367
396
|
|
|
368
397
|
[1]: https://github.com/aaugustin/websockets
|
|
369
398
|
|
|
@@ -379,6 +408,20 @@ and off you go!
|
|
|
379
408
|
|
|
380
409
|
[7]: https://eurl.io/#TeBLqZjLs
|
|
381
410
|
|
|
411
|
+
[pr43]: https://github.com/fbradyirl/webex_bot/pull/43
|
|
412
|
+
|
|
413
|
+
[pr46]: https://github.com/fbradyirl/webex_bot/pull/46
|
|
414
|
+
|
|
415
|
+
[pr42]: https://github.com/fbradyirl/webex_bot/pull/42
|
|
416
|
+
|
|
417
|
+
[pr54]: https://github.com/fbradyirl/webex_bot/pull/54
|
|
418
|
+
|
|
419
|
+
[pr55]: https://github.com/fbradyirl/webex_bot/pull/55
|
|
420
|
+
|
|
421
|
+
[pr56]: https://github.com/fbradyirl/webex_bot/pull/56
|
|
422
|
+
|
|
423
|
+
[pr59]: https://github.com/fbradyirl/webex_bot/pull/59
|
|
424
|
+
|
|
382
425
|
[i1]: https://github.com/fbradyirl/webex_bot/issues/1
|
|
383
426
|
|
|
384
427
|
[i2]: https://github.com/fbradyirl/webex_bot/issues/2
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
webex_bot/__init__.py,sha256=
|
|
1
|
+
webex_bot/__init__.py,sha256=JfFcRVsDoAtttzwsMUmplNDZyvQ4gsjzQH9P1fx6XRo,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=
|
|
4
|
+
webex_bot/webex_bot.py,sha256=fUGMMIJVcwoP4fIaIyvvpSxoaMAl34dXSk0_QX44VGo,21222
|
|
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=YmeRVh56pzsj0_lRHDvgyrv6YqQQdRiusG65dHTh9qU,3397
|
|
8
8
|
webex_bot/commands/help.py,sha256=s4buQxnmHijkqWiYs8RZ_H-yzi3EhAffSozPCGNndD0,3468
|
|
9
9
|
webex_bot/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
webex_bot/models/command.py,sha256=
|
|
10
|
+
webex_bot/models/command.py,sha256=HyZqS3oIEiWpHIEq8UvXPDHwd-opB-VWZxf2M3HKgvM,5325
|
|
11
11
|
webex_bot/models/response.py,sha256=1U0EQv8O5R3-demaumxMExn278DG4AeQHhXP_g4xndk,2544
|
|
12
12
|
webex_bot/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
webex_bot/websockets/webex_websocket_client.py,sha256=
|
|
14
|
-
webex_bot-0.5.
|
|
15
|
-
webex_bot-0.5.
|
|
16
|
-
webex_bot-0.5.
|
|
17
|
-
webex_bot-0.5.
|
|
18
|
-
webex_bot-0.5.
|
|
13
|
+
webex_bot/websockets/webex_websocket_client.py,sha256=QUTQUWgnBYg3bSZ857m_vM49Ns9GWHWT69TYP4U6R9I,10627
|
|
14
|
+
webex_bot-0.5.2.dist-info/LICENSE,sha256=93eGb10xmgkBP2Fh_n0E9YDXe0c0oz-FsnAimXG0S4Y,1072
|
|
15
|
+
webex_bot-0.5.2.dist-info/METADATA,sha256=-GLfltxFyjZyDkIUgN7BDZAyjSSzqMhmF4yWxiwxn2o,12401
|
|
16
|
+
webex_bot-0.5.2.dist-info/WHEEL,sha256=fS9sRbCBHs7VFcwJLnLXN1MZRR0_TVTxvXKzOnaSFs8,110
|
|
17
|
+
webex_bot-0.5.2.dist-info/top_level.txt,sha256=q1Y0RtYYinR7oXSwL93cK59c2KN_CbMVca8MLWeF63M,10
|
|
18
|
+
webex_bot-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|