From 9e79cdf74f2d04a35332859940e4446e6c979b50 Mon Sep 17 00:00:00 2001 From: Trygve Aaberge Date: Tue, 9 Jan 2024 20:04:21 +0100 Subject: Handle messages without a user field This can happen in channels shared between workspaces if one of the workspaces are removed. Then messages from users in that workspace will not have a user field anymore, but instead a user_profile field. --- slack/commands.py | 4 +-- slack/slack_conversation.py | 9 +++--- slack/slack_message.py | 25 +++++++++++++--- slack/slack_user.py | 71 ++++++++++++++++++++++++++------------------- 4 files changed, 69 insertions(+), 40 deletions(-) (limited to 'slack') diff --git a/slack/commands.py b/slack/commands.py index 7638b0d..52a1f51 100644 --- a/slack/commands.py +++ b/slack/commands.py @@ -17,7 +17,7 @@ from slack.shared import MESSAGE_ID_REGEX_STRING, REACTION_CHANGE_REGEX_STRING, from slack.slack_buffer import SlackBuffer from slack.slack_conversation import SlackConversation from slack.slack_thread import SlackThread -from slack.slack_user import name_from_user_info_without_spaces +from slack.slack_user import format_user_nick, name_from_user_info from slack.slack_workspace import SlackWorkspace from slack.task import run_async, sleep from slack.util import get_callback_name, with_color @@ -608,7 +608,7 @@ async def complete_user_next( slack_buffer.completion_context = "ACTIVE_COMPLETION" suffix = nick_suffix() if is_first_word else " " slack_buffer.completion_values = [ - name_from_user_info_without_spaces(slack_buffer.workspace, user) + suffix + format_user_nick(name_from_user_info(slack_buffer.workspace, user)) + suffix for user in search["results"] ] slack_buffer.completion_index = 0 diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py index ee23303..65357a3 100644 --- a/slack/slack_conversation.py +++ b/slack/slack_conversation.py @@ -735,10 +735,11 @@ class SlackConversation(SlackBuffer): nick = await message.nick(colorize=False, only_nick=True) try: sender = await message.sender - if message.subtype in ["channel_leave", "group_leave"]: - self.nicklist_remove_user(sender) - else: - self.nicklist_add_user(sender, nick) + if sender is not None: + if message.subtype in ["channel_leave", "group_leave"]: + self.nicklist_remove_user(sender) + else: + self.nicklist_add_user(sender, nick) except Exception as e: self.nicklist_add_user(None, nick) if isinstance(e, SlackApiError) and e.response["error"] != "bots_not_found": diff --git a/slack/slack_message.py b/slack/slack_message.py index 50eca1c..5d7bb95 100644 --- a/slack/slack_message.py +++ b/slack/slack_message.py @@ -19,7 +19,14 @@ from slack.log import print_error from slack.python_compatibility import removeprefix, removesuffix from slack.shared import shared from slack.slack_emoji import get_emoji -from slack.slack_user import SlackBot, SlackUser, format_bot_nick, nick_color +from slack.slack_user import ( + SlackBot, + SlackUser, + format_bot_nick, + format_user_nick, + name_from_user_profile, + nick_color, +) from slack.task import gather from slack.util import htmlescape, intersperse, unhtmlescape, with_color @@ -389,10 +396,10 @@ class SlackMessage: return self._message_json.get("bot_id") @property - async def sender(self) -> Union[SlackUser, SlackBot]: + async def sender(self) -> Union[SlackUser, SlackBot, None]: if "user" in self._message_json: return await self.workspace.users[self._message_json["user"]] - else: + elif "bot_id" in self._message_json: return await self.workspace.bots[self._message_json["bot_id"]] @property @@ -586,13 +593,23 @@ class SlackMessage: if "user" in self._message_json: user = await self.workspace.users[self._message_json["user"]] return user.nick(colorize=colorize, only_nick=only_nick) + elif "user_profile" in self._message_json: + # TODO: is_external + nick = name_from_user_profile( + self.workspace, + self._message_json["user_profile"], + fallback_name=self._message_json["user_profile"]["name"], + ) + return format_user_nick(nick, colorize=colorize, only_nick=only_nick) else: username = self._message_json.get("username") if username: return format_bot_nick(username, colorize=colorize, only_nick=only_nick) - else: + elif "bot_id" in self._message_json: bot = await self.workspace.bots[self._message_json["bot_id"]] return bot.nick(colorize=colorize, only_nick=only_nick) + else: + return "Unknown" async def _render_prefix( self, colorize: bool = True, only_nick: bool = False diff --git a/slack/slack_user.py b/slack/slack_user.py index 7e9ddb4..9627af0 100644 --- a/slack/slack_user.py +++ b/slack/slack_user.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union import weechat @@ -11,33 +11,57 @@ from slack.util import with_color if TYPE_CHECKING: from slack_api.slack_bots_info import SlackBotInfo + from slack_api.slack_conversations_history import SlackMessageUserProfile from slack_api.slack_usergroups_info import SlackUsergroupInfo - from slack_api.slack_users_info import SlackUserInfo + from slack_api.slack_users_info import SlackProfile, SlackUserInfo from slack.slack_workspace import SlackWorkspace -def nick_color(nick: str) -> str: +def nick_color(nick: str, is_self: bool = False) -> str: + if is_self: + return weechat.config_string(weechat.config_get("weechat.color.chat_nick_self")) + return weechat.info_get("nick_color_name", nick) # TODO: Probably need to do some mapping here based on the existing users, in case some has been changed to avoid duplicate names -def _name_from_user_info(workspace: SlackWorkspace, info: SlackUserInfo) -> str: - display_name = info["profile"].get("display_name") +def name_from_user_profile( + workspace: SlackWorkspace, + profile: Union[SlackProfile, SlackMessageUserProfile], + fallback_name: str, +) -> str: + display_name = profile.get("display_name") if display_name and not workspace.config.use_real_names: return display_name - return info["profile"].get("display_name") or info.get("real_name") or info["name"] + return profile.get("display_name") or profile.get("real_name") or fallback_name -def name_from_user_info_without_spaces( - workspace: SlackWorkspace, info: SlackUserInfo -) -> str: - return _name_from_user_info(workspace, info).replace( - " ", shared.config.look.replace_space_in_nicks_with.value +def name_from_user_info(workspace: SlackWorkspace, info: SlackUserInfo) -> str: + return name_from_user_profile( + workspace, info["profile"], info.get("real_name") or info["name"] ) +def format_user_nick( + nick: str, + colorize: bool = False, + only_nick: bool = False, + is_external: bool = False, + is_self: bool = False, +) -> str: + nick = nick.replace(" ", shared.config.look.replace_space_in_nicks_with.value) + + if colorize: + nick = with_color(nick_color(nick, is_self), nick) + + if not only_nick and is_external: + nick += shared.config.look.external_user_suffix.value + + return nick + + def format_bot_nick(nick: str, colorize: bool = False, only_nick: bool = False) -> str: nick = nick.replace(" ", shared.config.look.replace_space_in_nicks_with.value) @@ -45,7 +69,7 @@ def format_bot_nick(nick: str, colorize: bool = False, only_nick: bool = False) nick = with_color(nick_color(nick), nick) if not only_nick: - nick = nick + shared.config.look.bot_user_suffix.value + nick += shared.config.look.bot_user_suffix.value return nick @@ -88,26 +112,13 @@ class SlackUser: return get_emoji(status_emoji.strip(":")) def nick(self, colorize: bool = False, only_nick: bool = False) -> str: - nick = self._name_without_spaces() - - if colorize: - nick = with_color(self.nick_color(), nick) - - if not only_nick and self.is_external: - nick += shared.config.look.external_user_suffix.value - - return nick - - def _name_without_spaces(self) -> str: - return name_from_user_info_without_spaces(self.workspace, self._info) + nick = name_from_user_info(self.workspace, self._info) + return format_user_nick( + nick, colorize, only_nick, self.is_external, self.is_self + ) def nick_color(self) -> str: - if self.is_self: - return weechat.config_string( - weechat.config_get("weechat.color.chat_nick_self") - ) - - return nick_color(self._name_without_spaces()) + return nick_color(self.nick(colorize=False, only_nick=True), self.is_self) def update_info_json(self, info_json: SlackUserInfo): self._info.update(info_json) # pyright: ignore [reportGeneralTypeIssues] -- cgit