Skip to content

Commit 454ef41

Browse files
author
Irving Popovetsky
committed
phase 9
1 parent 202e8ba commit 454ef41

56 files changed

Lines changed: 294 additions & 486 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/UPGRADE_PLAN.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,10 @@ Same change: replace `asyncio.coroutine()` with type check.
13351335

13361336
---
13371337

1338-
## Phase 9: Testing (Day 6-7)
1338+
## Phase 9: Testing (Day 6-7) ✅ COMPLETE
1339+
1340+
> **Status**: Completed January 4, 2026
1341+
> **Result**: All linting passed, 57/57 tests passing, bot starts successfully
13391342
13401343
### 9.1 Install Dependencies
13411344

pybot/__main__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import sentry_sdk
55
import yaml
66
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
7+
78
from pybot._vendor.sirbot import SirBot
89
from pybot._vendor.sirbot.plugins.slack import SlackPlugin
9-
1010
from pybot.endpoints import handle_health_check
1111
from pybot.endpoints.slack.utils import HOST, PORT, slack_configs
1212

@@ -20,9 +20,7 @@
2020
with open(
2121
os.path.join(os.path.dirname(os.path.realpath(__file__)), "../logging.yml")
2222
) as log_configfile:
23-
logging.config.dictConfig(
24-
yaml.load(log_configfile.read(), Loader=yaml.SafeLoader)
25-
)
23+
logging.config.dictConfig(yaml.load(log_configfile.read(), Loader=yaml.SafeLoader))
2624
except Exception as e:
2725
logging.basicConfig(level=logging.DEBUG)
2826
logger.exception(e)

pybot/_vendor/sirbot/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Vendored sirbot library for Python 3.12+
33
Original: https://github.com/pyslackers/sir-bot-a-lot-2
44
"""
5+
56
from pybot._vendor.sirbot.bot import SirBot
67

78
__all__ = ["SirBot"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Slack plugin for sirbot."""
2+
23
from pybot._vendor.sirbot.plugins.slack.plugin import SlackPlugin
34

45
__all__ = ["SlackPlugin"]

pybot/_vendor/sirbot/plugins/slack/endpoints.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import aiohttp.web
55
from aiohttp.web import Response
66

7-
from pybot._vendor.slack.events import Event
8-
from pybot._vendor.slack.sansio import validate_request_signature
97
from pybot._vendor.slack.actions import Action
108
from pybot._vendor.slack.commands import Command
11-
from pybot._vendor.slack.exceptions import InvalidTimestamp, FailedVerification, InvalidSlackSignature
9+
from pybot._vendor.slack.events import Event
10+
from pybot._vendor.slack.exceptions import (
11+
FailedVerification,
12+
InvalidSlackSignature,
13+
InvalidTimestamp,
14+
)
15+
from pybot._vendor.slack.sansio import validate_request_signature
1216

1317
LOG = logging.getLogger(__name__)
1418

pybot/_vendor/sirbot/plugins/slack/plugin.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import asyncio
66
import logging
77
import os
8-
from typing import Any, Callable, Coroutine
8+
from collections.abc import Callable, Coroutine
9+
from typing import Any
910

1011
from pybot._vendor.slack import methods
11-
from pybot._vendor.slack.events import EventRouter, MessageRouter
1212
from pybot._vendor.slack.actions import Router as ActionRouter
1313
from pybot._vendor.slack.commands import Router as CommandRouter
14+
from pybot._vendor.slack.events import EventRouter, MessageRouter
1415
from pybot._vendor.slack.io.aiohttp import SlackAPI
1516

1617
from . import endpoints
@@ -124,9 +125,7 @@ def on_message(
124125
handler = _ensure_async(handler)
125126

126127
if admin and not self.admins:
127-
LOG.warning(
128-
"Slack admin IDs are not set. Admin-limited endpoints will not work."
129-
)
128+
LOG.warning("Slack admin IDs are not set. Admin-limited endpoints will not work.")
130129

131130
configuration = {"mention": mention, "admin": admin, "wait": wait}
132131
self.routers["message"].register(
@@ -155,9 +154,7 @@ def on_block(
155154
"""Register handler for a block_actions type action."""
156155
handler = _ensure_async(handler)
157156
configuration = {"wait": wait}
158-
self.routers["action"].register_block_action(
159-
block_id, (handler, configuration), action_id
160-
)
157+
self.routers["action"].register_block_action(block_id, (handler, configuration), action_id)
161158

162159
def on_dialog_submission(
163160
self,
@@ -168,14 +165,10 @@ def on_dialog_submission(
168165
"""Register handler for a dialog_submission type action."""
169166
handler = _ensure_async(handler)
170167
configuration = {"wait": wait}
171-
self.routers["action"].register_dialog_submission(
172-
callback_id, (handler, configuration)
173-
)
168+
self.routers["action"].register_dialog_submission(callback_id, (handler, configuration))
174169

175170
async def find_bot_id(self, app: Any) -> None:
176-
rep = await self.api.query(
177-
url=methods.USERS_INFO, data={"user": self.bot_user_id}
178-
)
171+
rep = await self.api.query(url=methods.USERS_INFO, data={"user": self.bot_user_id})
179172
self.bot_id = rep["user"]["profile"]["bot_id"]
180173
LOG.warning(
181174
'`SLACK_BOT_ID` not set. For a faster start time set it to: "%s"',

pybot/_vendor/slack/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Original: https://github.com/pyslackers/slack-sansio
44
"""
55

6-
from pybot._vendor.slack.methods import Methods as methods, HOOK_URL, ROOT_URL
6+
from pybot._vendor.slack.methods import HOOK_URL, ROOT_URL
7+
from pybot._vendor.slack.methods import Methods as methods
78

89
__all__ = ["methods", "HOOK_URL", "ROOT_URL"]

pybot/_vendor/slack/actions.py

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import json
2-
import typing
32
import logging
4-
from typing import Any, Dict, Iterator, Optional
3+
import typing
54
from collections import defaultdict
6-
from collections.abc import MutableMapping
5+
from collections.abc import Iterator, MutableMapping
6+
from typing import Any
77

88
from . import exceptions
99

@@ -26,20 +26,16 @@ class Action(MutableMapping):
2626
def __init__(
2727
self,
2828
raw_action: typing.MutableMapping,
29-
verification_token: Optional[str] = None,
30-
team_id: Optional[str] = None,
29+
verification_token: str | None = None,
30+
team_id: str | None = None,
3131
) -> None:
3232
self.action = raw_action
3333

3434
if verification_token and self.action["token"] != verification_token:
35-
raise exceptions.FailedVerification(
36-
self.action["token"], self.action["team"]["id"]
37-
)
35+
raise exceptions.FailedVerification(self.action["token"], self.action["team"]["id"])
3836

3937
if team_id and self.action["team"]["id"] != team_id:
40-
raise exceptions.FailedVerification(
41-
self.action["token"], self.action["team"]["id"]
42-
)
38+
raise exceptions.FailedVerification(self.action["token"], self.action["team"]["id"])
4339

4440
def __getitem__(self, item):
4541
return self.action[item]
@@ -63,8 +59,8 @@ def __repr__(self):
6359
def from_http(
6460
cls,
6561
payload: typing.MutableMapping,
66-
verification_token: Optional[str] = None,
67-
team_id: Optional[str] = None,
62+
verification_token: str | None = None,
63+
team_id: str | None = None,
6864
) -> "Action":
6965
action = json.loads(payload["payload"])
7066
return cls(action, verification_token=verification_token, team_id=team_id)
@@ -77,7 +73,7 @@ class Router:
7773
"""
7874

7975
def __init__(self):
80-
self._routes: Dict[str, Dict] = defaultdict(dict)
76+
self._routes: dict[str, dict] = defaultdict(dict)
8177

8278
def register(self, callback_id: str, handler: Any, name: str = "*") -> None:
8379
"""
@@ -98,9 +94,7 @@ def register(self, callback_id: str, handler: Any, name: str = "*") -> None:
9894

9995
self._routes[callback_id][name].append(handler)
10096

101-
def register_interactive_message(
102-
self, callback_id: str, handler: Any, name: str = "*"
103-
) -> None:
97+
def register_interactive_message(self, callback_id: str, handler: Any, name: str = "*") -> None:
10498
"""
10599
Register a new handler for a specific :class:`slack.actions.Action` `callback_id`.
106100
Optional routing based on the action name too.
@@ -118,9 +112,7 @@ def register_interactive_message(
118112
"""
119113
self.register(callback_id, handler, name)
120114

121-
def register_block_action(
122-
self, block_id: str, handler: Any, action_id: str = "*"
123-
) -> None:
115+
def register_block_action(self, block_id: str, handler: Any, action_id: str = "*") -> None:
124116
"""
125117
Register a new handler for a block-based :class:`slack.actions.Action`.
126118
Internally uses the base `register` method for actual registration.
@@ -158,9 +150,7 @@ def dispatch(self, action: Action) -> Any:
158150
handler
159151
"""
160152
if "callback_id" in action:
161-
LOG.debug(
162-
"Dispatching action %s, %s", action["type"], action["callback_id"]
163-
)
153+
LOG.debug("Dispatching action %s, %s", action["type"], action["callback_id"])
164154
else:
165155
LOG.debug(
166156
"Dispatching action %s, %s",

pybot/_vendor/slack/commands.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import typing
21
import logging
3-
from typing import Any, Dict, Iterator, Optional
2+
import typing
43
from collections import defaultdict
5-
from collections.abc import MutableMapping
4+
from collections.abc import Iterator, MutableMapping
5+
from typing import Any
66

77
from . import exceptions
88

@@ -24,20 +24,16 @@ class Command(MutableMapping):
2424
def __init__(
2525
self,
2626
raw_command: typing.MutableMapping,
27-
verification_token: Optional[str] = None,
28-
team_id: Optional[str] = None,
27+
verification_token: str | None = None,
28+
team_id: str | None = None,
2929
) -> None:
3030
self.command = raw_command
3131

3232
if verification_token and self.command["token"] != verification_token:
33-
raise exceptions.FailedVerification(
34-
self.command["token"], self.command["team_id"]
35-
)
33+
raise exceptions.FailedVerification(self.command["token"], self.command["team_id"])
3634

3735
if team_id and self.command["team_id"] != team_id:
38-
raise exceptions.FailedVerification(
39-
self.command["token"], self.command["team_id"]
40-
)
36+
raise exceptions.FailedVerification(self.command["token"], self.command["team_id"])
4137

4238
def __getitem__(self, item):
4339
return self.command[item]
@@ -66,7 +62,7 @@ class Router:
6662
"""
6763

6864
def __init__(self):
69-
self._routes: Dict[str, list] = defaultdict(list)
65+
self._routes: dict[str, list] = defaultdict(list)
7066

7167
def register(self, command: str, handler: Any):
7268
"""

pybot/_vendor/slack/events.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import re
21
import copy
2+
import itertools
33
import json
44
import logging
5-
import itertools
6-
from typing import Any, Dict, Iterator, Optional
5+
import re
76
from collections import defaultdict
8-
from collections.abc import MutableMapping
7+
from collections.abc import Iterator, MutableMapping
8+
from typing import Any
99

1010
from . import exceptions
1111

@@ -21,9 +21,7 @@ class Event(MutableMapping):
2121
(see `slack event API documentation <https://api.slack.com/events-api#receiving_events>`_)
2222
"""
2323

24-
def __init__(
25-
self, raw_event: MutableMapping, metadata: Optional[MutableMapping] = None
26-
) -> None:
24+
def __init__(self, raw_event: MutableMapping, metadata: MutableMapping | None = None) -> None:
2725
self.event = raw_event
2826
self.metadata = metadata
2927

@@ -77,8 +75,8 @@ def from_rtm(cls, raw_event: MutableMapping) -> "Event":
7775
def from_http(
7876
cls,
7977
raw_body: MutableMapping,
80-
verification_token: Optional[str] = None,
81-
team_id: Optional[str] = None,
78+
verification_token: str | None = None,
79+
team_id: str | None = None,
8280
) -> "Event":
8381
"""
8482
Create an event with data coming from the HTTP Event API.
@@ -116,8 +114,8 @@ class Message(Event):
116114

117115
def __init__(
118116
self,
119-
msg: Optional[MutableMapping] = None,
120-
metadata: Optional[MutableMapping] = None,
117+
msg: MutableMapping | None = None,
118+
metadata: MutableMapping | None = None,
121119
) -> None:
122120
if not msg:
123121
msg = {}
@@ -126,7 +124,7 @@ def __init__(
126124
def __repr__(self) -> str:
127125
return "Slack Message: " + str(self.event)
128126

129-
def response(self, in_thread: Optional[bool] = None) -> "Message":
127+
def response(self, in_thread: bool | None = None) -> "Message":
130128
"""
131129
Create a response message.
132130
@@ -143,9 +141,7 @@ def response(self, in_thread: Optional[bool] = None) -> "Message":
143141

144142
if in_thread:
145143
if "message" in self:
146-
data["thread_ts"] = (
147-
self["message"].get("thread_ts") or self["message"]["ts"]
148-
)
144+
data["thread_ts"] = self["message"].get("thread_ts") or self["message"]["ts"]
149145
else:
150146
data["thread_ts"] = self.get("thread_ts") or self["ts"]
151147
elif in_thread is None:
@@ -180,7 +176,7 @@ class EventRouter:
180176
"""
181177

182178
def __init__(self):
183-
self._routes: Dict[str, Dict] = defaultdict(dict)
179+
self._routes: dict[str, dict] = defaultdict(dict)
184180

185181
def register(self, event_type: str, handler: Any, **detail: Any) -> None:
186182
"""
@@ -223,9 +219,7 @@ def dispatch(self, event: Event) -> Iterator[Any]:
223219
"""
224220
LOG.debug('Dispatching event "%s"', event.get("type"))
225221
if event["type"] in self._routes:
226-
for detail_key, detail_values in self._routes.get(
227-
event["type"], {}
228-
).items():
222+
for detail_key, detail_values in self._routes.get(event["type"], {}).items():
229223
event_value = event.get(detail_key, "*")
230224
yield from detail_values.get(event_value, [])
231225
else:
@@ -242,15 +236,15 @@ class MessageRouter:
242236
"""
243237

244238
def __init__(self):
245-
self._routes: Dict[str, Dict] = defaultdict(dict)
239+
self._routes: dict[str, dict] = defaultdict(dict)
246240

247241
def register(
248242
self,
249243
pattern: str,
250244
handler: Any,
251245
flags: int = 0,
252246
channel: str = "*",
253-
subtype: Optional[str] = None,
247+
subtype: str | None = None,
254248
) -> None:
255249
"""
256250
Register a new handler for a specific :class:`slack.events.Message`.

0 commit comments

Comments
 (0)