diff --git a/pyproject.toml b/pyproject.toml index b27a9c2..89229d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.isort] multi_line_output = 3 -line_length = 100 +line_length = 120 include_trailing_comma = true [tool.black] -line-length = 100 +line-length = 120 diff --git a/src/slackv3/markdown.py b/src/slackv3/markdown.py index 43b7bfb..df6d4fb 100644 --- a/src/slackv3/markdown.py +++ b/src/slackv3/markdown.py @@ -5,7 +5,9 @@ from markdown.extensions.extra import ExtraExtension from markdown.preprocessors import Preprocessor -MARKDOWN_LINK_REGEX = re.compile(r"(?[^\]]+?)\]\((?P[a-zA-Z0-9]+?:\S+?)\)") +MARKDOWN_LINK_REGEX = re.compile( + r"(?[^\]]+?)\]\((?P[a-zA-Z0-9]+?:\S+?)\)" +) def slack_markdown_converter(compact_output=False): @@ -13,7 +15,9 @@ def slack_markdown_converter(compact_output=False): This is a Markdown converter for use with Slack. """ enable_format("imtext", IMTEXT_CHRS, borders=not compact_output) - md = Markdown(output_format="imtext", extensions=[ExtraExtension(), AnsiExtension()]) + md = Markdown( + output_format="imtext", extensions=[ExtraExtension(), AnsiExtension()] + ) md.preprocessors.register(LinkPreProcessor(md), "LinkPreProcessor", 30) md.stripTopLevelTags = False return md diff --git a/src/slackv3/person.py b/src/slackv3/person.py index 07aa05d..0c45e7a 100644 --- a/src/slackv3/person.py +++ b/src/slackv3/person.py @@ -30,12 +30,14 @@ class SlackPerson(Person): def __init__(self, webclient: WebClient, userid=None, channelid=None): if userid is not None and userid[0] not in ("U", "W", "B"): raise Exception( - f"This is not a Slack user or bot id: {userid} " "(should start with B, U or W)" + f"This is not a Slack user or bot id: {userid} " + "(should start with B, U or W)" ) if channelid is not None and channelid[0] not in ("D", "C", "G"): raise Exception( - f"This is not a valid Slack channelid: {channelid} " "(should start with D, C or G)" + f"This is not a valid Slack channelid: {channelid} " + "(should start with D, C or G)" ) self._userid = userid @@ -93,13 +95,17 @@ def _cache_user_info(self): res = self._webclient.users_info(user=self._userid) if res["ok"] is False: - log.error(f"Cannot find user with ID {self._userid}. Slack Error: {res['error']}") + log.error( + f"Cannot find user with ID {self._userid}. Slack Error: {res['error']}" + ) else: if "bot" in res: self._user_info["display_name"] = res["bot"].get("name", "") else: for attribute in ["real_name", "display_name", "email"]: - self._user_info[attribute] = res["user"]["profile"].get(attribute, "") + self._user_info[attribute] = res["user"]["profile"].get( + attribute, "" + ) team = None # Normal users diff --git a/src/slackv3/room.py b/src/slackv3/room.py index b106ffe..cef4539 100644 --- a/src/slackv3/room.py +++ b/src/slackv3/room.py @@ -163,7 +163,9 @@ def create(self, private=False): try: if private: log.info(f"Creating private conversation {self}.") - self._bot.slack_web.conversations_create(name=self.name, is_private=True) + self._bot.slack_web.conversations_create( + name=self.name, is_private=True + ) else: log.info(f"Creating conversation {self}.") self._bot.slack_web.conversations_create(name=self.name) @@ -238,7 +240,9 @@ def occupants(self): ) if res["ok"] is True: for member in res["members"]: - occupants.append(SlackRoomOccupant(self._webclient, member, self.id, self._bot)) + occupants.append( + SlackRoomOccupant(self._webclient, member, self.id, self._bot) + ) cursor = res["response_metadata"]["next_cursor"] else: log.exception( @@ -249,7 +253,8 @@ def occupants(self): def invite(self, *args): users = { - user["name"]: user["id"] for user in self._webclient.api_call("users.list")["members"] + user["name"]: user["id"] + for user in self._webclient.api_call("users.list")["members"] } for user in args: diff --git a/src/slackv3/slackv3.py b/src/slackv3/slackv3.py index b468d46..27e5a5e 100644 --- a/src/slackv3/slackv3.py +++ b/src/slackv3/slackv3.py @@ -145,7 +145,9 @@ def _register_identifiers_pickling(self): """ SlackBackend.__build_identifier = self.build_identifier for cls in (SlackPerson, SlackRoomOccupant, SlackRoom): - copyreg.pickle(cls, SlackBackend._pickle_identifier, SlackBackend._unpickle_identifier) + copyreg.pickle( + cls, SlackBackend._pickle_identifier, SlackBackend._unpickle_identifier + ) def update_alternate_prefixes(self): """Converts BOT_ALT_PREFIXES to use the slack ID instead of name @@ -168,7 +170,9 @@ def update_alternate_prefixes(self): f'Failed to look up Slack userid for alternate prefix "{prefix}": {str(e)}' ) - self.bot_alt_prefixes = tuple(x.lower() for x in self.bot_config.BOT_ALT_PREFIXES) + self.bot_alt_prefixes = tuple( + x.lower() for x in self.bot_config.BOT_ALT_PREFIXES + ) log.debug(f"Converted bot_alt_prefixes: {self.bot_config.BOT_ALT_PREFIXES}") def _setup_event_callbacks(self): @@ -366,7 +370,9 @@ def _generic_wrapper(self, event_data): except KeyError: log.debug("Ignoring unsupported Slack event!") - def _sm_generic_event_handler(self, client: SocketModeClient, req: SocketModeRequest): + def _sm_generic_event_handler( + self, client: SocketModeClient, req: SocketModeRequest + ): log.debug( f"Event type: {req.type}\n" f"Envelope ID: {req.envelope_id}\n" @@ -375,7 +381,9 @@ def _sm_generic_event_handler(self, client: SocketModeClient, req: SocketModeReq f"Retry Reason: {req.retry_reason}\n" ) # Acknowledge the request - client.send_socket_mode_response(SocketModeResponse(envelope_id=req.envelope_id)) + client.send_socket_mode_response( + SocketModeResponse(envelope_id=req.envelope_id) + ) # Dispatch event to the Event API generic event handler. self._generic_wrapper(req.payload) @@ -386,7 +394,9 @@ def _sm_handle_hello(self, *args): log.debug(f"message listeners : {sm_client.message_listeners}") if event["type"] == "hello": self.connect_callback() - self.callback_presence(Presence(identifier=self.bot_identifier, status=ONLINE)) + self.callback_presence( + Presence(identifier=self.bot_identifier, status=ONLINE) + ) # Stop calling hello handler for future events. sm_client.message_listeners.remove(self._sm_handle_hello) log.info("Unregistered 'hello' handler from socket-mode client") @@ -597,7 +607,9 @@ def username_to_userid(self, name: str): if len(user_ids) == 0: raise UserDoesNotExistError(f"Cannot find user '{username}'.") if len(user_ids) > 1: - raise UserNotUniqueError(f"'{username}' isn't unique: {len(user_ids)} matches found.") + raise UserNotUniqueError( + f"'{username}' isn't unique: {len(user_ids)} matches found." + ) return user_ids[0] @lru_cache(1024) @@ -633,10 +645,14 @@ def channels( References: - https://slack.com/api/conversations.list """ - response = self.slack_web.conversations_list(exclude_archived=exclude_archived, types=types) + response = self.slack_web.conversations_list( + exclude_archived=exclude_archived, types=types + ) channels = [ - channel for channel in response["channels"] if channel["is_member"] or not joined_only + channel + for channel in response["channels"] + if channel["is_member"] or not joined_only ] return channels @@ -663,13 +679,17 @@ def _prepare_message(self, msg): # or card if msg.is_group: to_channel_id = msg.to.id to_humanreadable = ( - msg.to.name if msg.to.name else self.channelid_to_channelname(to_channel_id) + msg.to.name + if msg.to.name + else self.channelid_to_channelname(to_channel_id) ) else: to_humanreadable = msg.to.username to_channel_id = msg.to.channelid if to_channel_id.startswith("C"): - log.debug("This is a divert to private message, sending it directly to the user.") + log.debug( + "This is a divert to private message, sending it directly to the user." + ) to_channel_id = self.get_im_channel(msg.to.userid) return to_humanreadable, to_channel_id @@ -692,7 +712,9 @@ def send_message(self, msg) -> Message: if msg.is_group: to_channel_id = msg.to.id to_humanreadable = ( - msg.to.name if msg.to.name else self.channelid_to_channelname(to_channel_id) + msg.to.name + if msg.to.name + else self.channelid_to_channelname(to_channel_id) ) else: to_humanreadable = msg.to.username @@ -707,7 +729,9 @@ def send_message(self, msg) -> Message: to_channel_id = msg.to.channelid msgtype = "direct" if msg.is_direct else "channel" - log.debug(f"Sending {msgtype} message to {to_humanreadable} ({to_channel_id}).") + log.debug( + f"Sending {msgtype} message to {to_humanreadable} ({to_channel_id})." + ) body = self.md.convert(msg.body) log.debug(f"Message size: {len(body)}.") @@ -799,7 +823,9 @@ def _slack_upload(self, stream: Stream) -> None: else: stream.error() except Exception: - log.exception(f"Upload of {stream.name} to {stream.identifier.channelname} failed.") + log.exception( + f"Upload of {stream.name} to {stream.identifier.channelname} failed." + ) def send_stream_request( self, @@ -845,11 +871,14 @@ def send_card(self, card: Card): attachment["thumb_url"] = card.thumbnail if card.color: - attachment["color"] = COLORS[card.color] if card.color in COLORS else card.color + attachment["color"] = ( + COLORS[card.color] if card.color in COLORS else card.color + ) if card.fields: attachment["fields"] = [ - {"title": key, "value": value, "short": True} for key, value in card.fields + {"title": key, "value": value, "short": True} + for key, value in card.fields ] parts = self.prepare_message_body(card.body, self.message_size_limit) @@ -888,7 +917,9 @@ def __hash__(self): return 0 # this is a singleton anyway def change_presence(self, status: str = ONLINE, message: str = "") -> None: - self.slack_web.users_setPresence(presence="auto" if status == ONLINE else "away") + self.slack_web.users_setPresence( + presence="auto" if status == ONLINE else "away" + ) @staticmethod def prepare_message_body(body, size_limit): @@ -965,7 +996,9 @@ def extract_identifiers_from_string(text): "Unparseable Slack ID, should start with U, B, C, G, D or W (got `%s`)" ) if text[1] not in ("@", "#"): - raise ValueError(f"Expected '@' or '#' Slack ID prefix but got '{text[1]}'.") + raise ValueError( + f"Expected '@' or '#' Slack ID prefix but got '{text[1]}'." + ) text = text[2:-1] if text == "": raise ValueError(exception_message % "") @@ -1001,7 +1034,9 @@ def build_identifier(self, txtrep): :func:`~extract_identifiers_from_string`. """ log.debug(f"Building an identifier from {txtrep}.") - username, userid, channelname, channelid = self.extract_identifiers_from_string(txtrep) + username, userid, channelname, channelid = self.extract_identifiers_from_string( + txtrep + ) if userid is None and username is not None: userid = self.username_to_userid(username) @@ -1113,7 +1148,9 @@ def query_room(self, room): m = SLACK_CLIENT_CHANNEL_HYPERLINK.match(room) if m is not None: - return SlackRoom(webclient=self.slack_web, channelid=m.groupdict()["id"], bot=self) + return SlackRoom( + webclient=self.slack_web, channelid=m.groupdict()["id"], bot=self + ) return SlackRoom(webclient=self.slack_web, name=room, bot=self) @@ -1167,7 +1204,10 @@ def process_mentions(self, text): try: identifier = self.build_identifier(word) except Exception as e: - log.debug(f"Tried to build an identifier from '{word}' " f"but got exception: {e}") + log.debug( + f"Tried to build an identifier from '{word}' " + f"but got exception: {e}" + ) continue # We track mentions of persons and rooms. diff --git a/tests/person_test.py b/tests/person_test.py index 1161ce0..3f8c08f 100644 --- a/tests/person_test.py +++ b/tests/person_test.py @@ -229,10 +229,14 @@ class SlackPersonTests(unittest.TestCase): def setUp(self): self.webclient = MagicMock() self.webclient.users_info.return_value = SlackPersonTests.USER_INFO_OK - self.webclient.conversations_info.return_value = SlackPersonTests.CHANNEL_INFO_PUBLIC_OK + self.webclient.conversations_info.return_value = ( + SlackPersonTests.CHANNEL_INFO_PUBLIC_OK + ) self.userid = "W012A3CDE" self.channelid = "C012AB3CD" - self.p = SlackPerson(self.webclient, userid=self.userid, channelid=self.channelid) + self.p = SlackPerson( + self.webclient, userid=self.userid, channelid=self.channelid + ) def test_wrong_userid(self): with self.assertRaises(Exception): @@ -289,7 +293,9 @@ def test_channelname(self): self.webclient.conversations_info.assert_called_once_with(channel="C012AB3CD") def test_channelname_channel_not_found(self): - self.webclient.conversations_info.return_value = SlackPersonTests.CHANNEL_INFO_FAIL + self.webclient.conversations_info.return_value = ( + SlackPersonTests.CHANNEL_INFO_FAIL + ) with self.assertRaises(RoomDoesNotExistError) as e: self.p = SlackPerson(self.webclient, channelid="C012AB3CD") self.p.channelname @@ -318,7 +324,9 @@ def test_to_string(self): self.assertEqual(str(self.p), "<@W012A3CDE>") def test_equal(self): - self.another_p = SlackPerson(self.webclient, userid=self.userid, channelid=self.channelid) + self.another_p = SlackPerson( + self.webclient, userid=self.userid, channelid=self.channelid + ) self.assertTrue(self.p == self.another_p) self.assertFalse(self.p == "this is not a person") diff --git a/tests/slack_test.py b/tests/slack_test.py index 0ddcdd3..e2e2aac 100644 --- a/tests/slack_test.py +++ b/tests/slack_test.py @@ -353,7 +353,9 @@ def test_extract_identifiers(self): self.assertEqual(extract_from("@person"), ("person", None, None, None)) - self.assertEqual(extract_from("#general/someuser"), ("someuser", None, "general", None)) + self.assertEqual( + extract_from("#general/someuser"), ("someuser", None, "general", None) + ) self.assertEqual(extract_from("#general"), (None, None, "general", None)) @@ -377,7 +379,9 @@ def test_extract_identifiers(self): def test_build_identifier(self): self.slack.slack_web = MagicMock() - self.slack.slack_web.conversations_info.return_value = CONVERSATION_INFO_PUBLIC_OK + self.slack.slack_web.conversations_info.return_value = ( + CONVERSATION_INFO_PUBLIC_OK + ) self.slack.slack_web.users_info.return_value = USER_INFO_OK self.slack.slack_web.conversations_open.return_value = CONVERSATION_OPEN_OK @@ -408,7 +412,9 @@ def test_uri_sanitization(self): ) self.assertEqual( - sanitize("Pretty URL Testing: with " "more text"), + sanitize( + "Pretty URL Testing: with " "more text" + ), "Pretty URL Testing: example.org with more text", ) @@ -462,8 +468,12 @@ def test_slack_markdown_link_preprocessor(self): ) def test_mention_processing(self): - self.slack.slack_web.conversations_info.return_value = CHANNEL_INFO_DIRECT_1TO1_OK - self.slack.slack_web.conversations_open.return_value = CHANNEL_INFO_DIRECT_1TO1_OK + self.slack.slack_web.conversations_info.return_value = ( + CHANNEL_INFO_DIRECT_1TO1_OK + ) + self.slack.slack_web.conversations_open.return_value = ( + CHANNEL_INFO_DIRECT_1TO1_OK + ) mentions = self.slack.process_mentions @@ -522,7 +532,9 @@ def test_send_message(self): def test_send_ephemeral_message(self): self.slack.slack_web = MagicMock() - self.slack.slack_web.chat_postEphemeral.return_value = SUCCESSFUL_EPHEMERAL_MESSAGE_RESPONSE + self.slack.slack_web.chat_postEphemeral.return_value = ( + SUCCESSFUL_EPHEMERAL_MESSAGE_RESPONSE + ) # Mock an empty plugin manager (we're not testing plugins here) mocked_plugin_manager = MagicMock() @@ -538,7 +550,9 @@ def test_send_ephemeral_message(self): def test_update_message(self): self.slack.slack_web = MagicMock() - self.slack.slack_web.chat_update.return_value = SUCCESSFUL_UPDATE_MESSAGE_RESPONSE + self.slack.slack_web.chat_update.return_value = ( + SUCCESSFUL_UPDATE_MESSAGE_RESPONSE + ) # Mock an empty plugin manager (we're not testing plugins here) mocked_plugin_manager = MagicMock()