From 1251da5012f732ac1fe1a3714405f178e763eba5 Mon Sep 17 00:00:00 2001 From: Paul Philion Date: Thu, 5 Dec 2024 12:33:37 -0800 Subject: [PATCH] Ticket 1501, adding role-based notify --- docs/devlog.md | 18 ++++++++++++++++++ netbot/netbot.py | 18 +++++++++++++++++- redmine/users.py | 15 +++++++++++---- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/docs/devlog.md b/docs/devlog.md index cacf1d1..3f71f70 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -1,5 +1,23 @@ # Netbot Development Log +## 2024-12-05 + +Working on ticket #1501: When notifying a ticket assigned to a group, notify the matching Discord role + +Technical details: when notifying a ticket, if that ticket is assigned to a team (not an individual), convert the team name into a role. + +Notifying that because notify relies deeply on how Discord functions, it's tough to test. + +The solution is a little hacky, but it works: The only difference between @-ing a user and roles is: + + <@ID> + +vs + + <@&ID> + +When the role lookup hits, it simply prepends '&' to the numerical ID before passing to the formatting code. That wraps the ID in '<@' and '>' and everything works out. + ## 2024-11-26 Starting work on ticket #1203, `/ticket parent`. diff --git a/netbot/netbot.py b/netbot/netbot.py index 0835c55..3ea8dcc 100755 --- a/netbot/netbot.py +++ b/netbot/netbot.py @@ -235,7 +235,8 @@ async def synchronize_ticket(self, ticket, thread:discord.Thread) -> bool: del self.ticket_locks[ticket.id] log.debug(f"UNLOCK thread - id: {ticket.id}, thread: {thread}") - def get_channel_by_name(self, channel_name: str): + + def get_channel_by_name(self, channel_name: str) -> discord.TextChannel: for channel in self.get_all_channels(): if isinstance(channel, discord.TextChannel) and channel_name == channel.name: return channel @@ -244,6 +245,14 @@ def get_channel_by_name(self, channel_name: str): return None + def get_role_by_name(self, role_name: str) -> discord.Role: + # Noting: "guild" maps to discord server, and the API is designed to run on many "Discord servers" concurrently + for guild in self.guilds: + for role in guild.roles: + if role_name == role.name: + return role + + async def on_application_command_error(self, context: discord.ApplicationContext, exception: discord.DiscordException): """Bot-level error handler""" @@ -422,6 +431,13 @@ def extract_ids_from_ticket(self, ticket: Ticket) -> set[int]: discord_ids.add(user.discord_id.id) else: log.info(f"No Discord ID for {named}") + elif self.redmine.user_mgr.is_team(named.name): + # a team, look up role it. + team = self.get_role_by_name(named.name) + if team: + discord_ids.add(f"&{team.id}") + else: + log.warning(f"Team name {named} has no matching Discord role.") else: log.info(f"ERROR: user ID {named} not found") diff --git a/redmine/users.py b/redmine/users.py index ecdd25b..5fddadc 100644 --- a/redmine/users.py +++ b/redmine/users.py @@ -95,8 +95,12 @@ def find_discord_user(self, discord_user_id:str) -> User: return None - def is_user_or_group(self, name:str) -> bool: - return name in self.users or name in self.teams + #def is_user_or_group(self, name:str) -> bool: + # return name in self.users or name in self.teams + + + def is_team(self, name:str) -> bool: + return name in self.teams def get_teams(self) -> list[Team]: @@ -283,8 +287,11 @@ def find_discord_user(self, discord_user_id:str) -> User: # just a proxy return self.cache.find_discord_user(discord_user_id) - def is_user_or_group(self, term:str): - return self.cache.is_user_or_group(term) + #def is_user_or_group(self, term:str): + # return self.cache.is_user_or_group(term) + + def is_team(self, name:str): + return self.cache.is_team(name) def get(self, user_id:int): """get a user by ID, directly from redmine"""