Skip to content

Commit 61b092e

Browse files
author
Paul Philion
committed
initial work on using discord roles for teams command. needs formatting
1 parent 1cb485e commit 61b092e

File tree

3 files changed

+131
-104
lines changed

3 files changed

+131
-104
lines changed

netbot/cog_scn.py

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class SCNCog(commands.Cog):
148148
"""Cog to mange SCN-related functions"""
149149
def __init__(self, bot):
150150
self.bot = bot
151+
self.formatter = bot.formatter
151152
self.redmine: Client = bot.redmine
152153

153154
# see https://github.com/Pycord-Development/pycord/blob/master/examples/app_commands/slash_cog_groups.py
@@ -255,55 +256,64 @@ async def reindex(self, ctx:discord.ApplicationContext):
255256
await ctx.respond("Rebuilt redmine indices.")
256257

257258

258-
@scn.command(description="join the specified team")
259-
async def join(self, ctx:discord.ApplicationContext, teamname:str , member: discord.Member=None):
260-
discord_name = ctx.user.name # by default, assume current user
261-
if member:
262-
log.info(f"Overriding current user={ctx.user.name} with member={member.name}")
263-
discord_name = member.name
264-
265-
user = self.redmine.user_mgr.find(discord_name)
266-
if user is None:
267-
await ctx.respond(f"Unknown user, no Discord mapping: {discord_name}")
268-
elif self.redmine.user_mgr.get_team_by_name(teamname) is None:
269-
await ctx.respond(f"Unknown team name: {teamname}")
270-
else:
271-
self.redmine.user_mgr.join_team(user, teamname)
272-
await ctx.respond(f"**{discord_name}** has joined *{teamname}*")
273-
274-
275-
@scn.command(description="leave the specified team")
276-
async def leave(self, ctx:discord.ApplicationContext, teamname:str, member: discord.Member=None):
277-
discord_name = ctx.user.name # by default, assume current user
278-
if member:
279-
log.info(f"Overriding current user={ctx.user.name} with member={member.name}")
280-
discord_name = member.name
281-
user = self.redmine.user_mgr.find(discord_name)
282-
283-
if user:
284-
self.redmine.user_mgr.leave_team(user, teamname)
285-
await ctx.respond(f"**{discord_name}** has left *{teamname}*")
286-
else:
287-
await ctx.respond(f"Unknown Discord user: {discord_name}.")
259+
# REMOVE - handled by discord roles
260+
# @scn.command(description="join the specified team")
261+
# async def join(self, ctx:discord.ApplicationContext, teamname:str , member: discord.Member=None):
262+
# discord_name = ctx.user.name # by default, assume current user
263+
# if member:
264+
# log.info(f"Overriding current user={ctx.user.name} with member={member.name}")
265+
# discord_name = member.name
266+
267+
# user = self.redmine.user_mgr.find(discord_name)
268+
# if user is None:
269+
# await ctx.respond(f"Unknown user, no Discord mapping: {discord_name}")
270+
# elif self.redmine.user_mgr.get_team_by_name(teamname) is None:
271+
# await ctx.respond(f"Unknown team name: {teamname}")
272+
# else:
273+
# self.redmine.user_mgr.join_team(user, teamname)
274+
# await ctx.respond(f"**{discord_name}** has joined *{teamname}*")
275+
276+
# REMOVE - handled by discord roles
277+
# @scn.command(description="leave the specified team")
278+
# async def leave(self, ctx:discord.ApplicationContext, teamname:str, member: discord.Member=None):
279+
# discord_name = ctx.user.name # by default, assume current user
280+
# if member:
281+
# log.info(f"Overriding current user={ctx.user.name} with member={member.name}")
282+
# discord_name = member.name
283+
# user = self.redmine.user_mgr.find(discord_name)
284+
285+
# if user:
286+
# self.redmine.user_mgr.leave_team(user, teamname)
287+
# await ctx.respond(f"**{discord_name}** has left *{teamname}*")
288+
# else:
289+
# await ctx.respond(f"Unknown Discord user: {discord_name}.")
290+
291+
292+
def find_role(self, ctx:discord.ApplicationContext, rolename:str) -> discord.Role | None:
293+
for role in ctx.guild.roles:
294+
if role.name == rolename:
295+
return role
288296

289297

290298
@scn.command(description="list teams and members")
291299
async def teams(self, ctx:discord.ApplicationContext, teamname:str=None):
292-
# list all teams, with members
293-
300+
# list teams, with members
294301
if teamname:
295-
team = self.redmine.user_mgr.cache.get_team_by_name(teamname)
302+
team = self.find_role(ctx, teamname)
296303
if team:
297-
await ctx.respond(self.format_team(team))
304+
await ctx.respond(self.formatter.format_team(team))
305+
return
298306
else:
299-
await ctx.respond(f"Unknown team name: {teamname}") # error
307+
all_teams = [team.name for team in ctx.guild.roles]
308+
await ctx.respond(f"Unknown team name: {teamname}\nTeams: {all_teams}") # error
300309
else:
301310
# all teams
302-
teams = self.redmine.user_mgr.cache.get_teams()
303-
buff = ""
304-
for team in teams:
305-
buff += self.format_team(team)
306-
await ctx.respond(buff[:2000]) # truncate!
311+
#teams = self.redmine.user_mgr.cache.get_teams()
312+
teams = "\n- ".join([team.name for team in ctx.guild.roles])
313+
#buff = ""
314+
#for team in teams:
315+
# buff += self.formatter.format_team(team)
316+
await ctx.respond("Available teams:\n-" + teams)
307317

308318

309319
#@scn.command()
@@ -325,7 +335,7 @@ async def epics(self, ctx:discord.ApplicationContext):
325335
async def blocked(self, ctx:discord.ApplicationContext):
326336
team = self.redmine.user_mgr.cache.get_team_by_name(BLOCKED_TEAM_NAME)
327337
if team:
328-
await ctx.respond(self.format_team(team))
338+
await ctx.respond(self.formatter.format_team(team))
329339
else:
330340
await ctx.respond(f"Expected team {BLOCKED_TEAM_NAME} not configured") # error
331341

@@ -385,26 +395,3 @@ async def approve(self, ctx:discord.ApplicationContext):
385395

386396
else:
387397
await ctx.respond("Must be authorized admin to approve Redmine users.")
388-
389-
390-
## FIXME move to DiscordFormatter
391-
392-
async def print_team(self, ctx, team):
393-
msg = f"> **{team.name}**\n"
394-
for user_rec in team.users:
395-
#user = self.redmine.get_user(user_rec.id)
396-
#discord_user = user.custom_fields[0].value or "" # FIXME cf_* lookup
397-
msg += f"{user_rec.name}, "
398-
#msg += f"[{user.id}] **{user_rec.name}** {user.login} {user.mail} {user.custom_fields[0].value}\n"
399-
msg = msg[:-2] + '\n\n'
400-
await ctx.channel.send(msg)
401-
402-
403-
def format_team(self, team) -> str:
404-
# single line format: teamname: member1, member2
405-
skip_teams = ["blocked", "users"]
406-
407-
if team and team.name not in skip_teams:
408-
return f"**{team.name}**: {', '.join([user.name for user in team.users])}\n"
409-
else:
410-
return ""

netbot/formatting.py

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async def print_tickets(self, title:str, tickets:list[Ticket], ctx:discord.Appli
6565
if len(tickets) == 1:
6666
await self.print_ticket(tickets[0], ctx)
6767
else:
68-
msg = self.format_tickets(title, tickets)
68+
msg = self.format_ticket_report(ctx, title, tickets)
6969

7070
if len(msg) > MAX_MESSAGE_LEN:
7171
log.warning(f"message over {MAX_MESSAGE_LEN} chars. truncing.")
@@ -106,27 +106,28 @@ def build_legend(self, tickets:list[Ticket]) -> dict[str,str]:
106106
return legend
107107

108108

109-
def format_tickets(self, title:str, tickets:list[Ticket], max_len=MAX_MESSAGE_LEN) -> str:
110-
if tickets is None:
111-
return "No tickets found."
109+
# # OLD
110+
# def format_tickets(self, title:str, tickets:list[Ticket], max_len=MAX_MESSAGE_LEN) -> str:
111+
# if tickets is None:
112+
# return "No tickets found."
112113

113-
legend = self.build_legend(tickets)
114-
legend_row = "` "
115-
for name, icon in legend.items():
116-
legend_row += icon + name + " "
117-
legend_row = legend_row[:-1] + '`' # remove the last space
114+
# legend = self.build_legend(tickets)
115+
# legend_row = "` "
116+
# for name, icon in legend.items():
117+
# legend_row += icon + name + " "
118+
# legend_row = legend_row[:-1] + '`' # remove the last space
118119

119-
section = "**" + title + "**\n"
120-
for ticket in tickets:
121-
ticket_line = self.format_ticket_row(ticket)
122-
if len(section) + len(ticket_line) + len(legend_row) + 1 < max_len:
123-
# make sure the lenght is less that the max
124-
section += ticket_line + "\n" # append each ticket
125-
else:
126-
break # max_len hit
120+
# section = "**" + title + "**\n"
121+
# for ticket in tickets:
122+
# ticket_line = self.format_ticket_row(ticket)
123+
# if len(section) + len(ticket_line) + len(legend_row) + 1 < max_len:
124+
# # make sure the lenght is less that the max
125+
# section += ticket_line + "\n" # append each ticket
126+
# else:
127+
# break # max_len hit
127128

128-
section += legend_row # add the legend
129-
return section.strip()
129+
# section += legend_row # add the legend
130+
# return section.strip()
130131

131132

132133
# noting for future: https://docs.python.org/3/library/string.html#string.Template
@@ -173,17 +174,52 @@ def format_icon(self, value:NamedId|None) -> str:
173174
return ""
174175

175176

176-
def format_ticket_row(self, ticket:Ticket) -> str:
177-
link = self.redmine_link(ticket)
178-
# link is mostly hidden, so we can't use the length to format.
179-
# but the length of the ticket id can be used
180-
link_padding = ' ' * (5 - len(str(ticket.id))) # field width = 6
177+
# # OLD
178+
# def format_ticket_row(self, ticket:Ticket) -> str:
179+
# link = self.redmine_link(ticket)
180+
# # link is mostly hidden, so we can't use the length to format.
181+
# # but the length of the ticket id can be used
182+
# link_padding = ' ' * (5 - len(str(ticket.id))) # field width = 6
181183

184+
# status = get_emoji(ticket.status.name) if ticket.status else get_emoji('?')
185+
# priority = get_emoji(ticket.priority.name) if ticket.priority else get_emoji('?')
186+
# age = synctime.age_str(ticket.updated_on)
187+
# assigned = ticket.assigned_to.name if ticket.assigned_to else ""
188+
# return f"`{link_padding}`{link}` {status} {priority} {age:<10} {assigned:<18} `{ticket.subject[:60]}"
189+
190+
191+
def format_ticket_item(self, ctx: discord.ApplicationContext, ticket:Ticket) -> str:
192+
link = self.discord_link(ctx, ticket)
193+
if not link:
194+
link = self.redmine_link(ticket) + " **" + ticket.subject + "**"
182195
status = get_emoji(ticket.status.name) if ticket.status else get_emoji('?')
183196
priority = get_emoji(ticket.priority.name) if ticket.priority else get_emoji('?')
184197
age = synctime.age_str(ticket.updated_on)
185198
assigned = ticket.assigned_to.name if ticket.assigned_to else ""
186-
return f"`{link_padding}`{link}` {status} {priority} {age:<10} {assigned:<18} `{ticket.subject[:60]}"
199+
return f"{status} {priority} {assigned} {age} {link}"
200+
201+
202+
def format_ticket_report(self, ctx: discord.ApplicationContext, title:str, tickets:list[Ticket], max_len=MAX_MESSAGE_LEN) -> str:
203+
if tickets is None:
204+
return "No tickets found."
205+
206+
legend = self.build_legend(tickets)
207+
legend_row = "" #"` "
208+
for name, icon in legend.items():
209+
legend_row += icon + name + " "
210+
#legend_row = legend_row[:-1] + '`' # remove the last space
211+
212+
report = "**" + title + "**\n"
213+
for ticket in tickets:
214+
ticket_line = self.format_ticket_item(ctx, ticket)
215+
if len(report) + len(ticket_line) + len(legend_row) + 1 < max_len:
216+
# make sure the len is less that the max
217+
report += ticket_line + "\n" # append each ticket
218+
else:
219+
break # max_len hit
220+
221+
report += legend_row # add the legend
222+
return report.strip()
187223

188224

189225
def format_subticket(self, ctx: discord.ApplicationContext, ticket:Ticket) -> str:
@@ -255,9 +291,8 @@ def format_dusty_reminder(self, ticket:Ticket, discord_ids: list[str], thread_ur
255291
# action row with what options?
256292
# :warning:
257293
# ⚠️
258-
# [icon] **Alert** [Ticket x](link) will expire in x hours, as xyz.
259294
ids_str = [f"<@{id}>" for id in discord_ids]
260-
return f"⚠️ {' '.join(ids_str)} Ticket gettin' dusty: {thread_url}"
295+
return f"⚠️ {' '.join(ids_str)} Ticket will soon be reassigned due to inactivity: {thread_url}"
261296

262297

263298
def format_recycled_reminder(self, ticket:Ticket, discord_ids: list[str], thread_url: str):
@@ -266,9 +301,8 @@ def format_recycled_reminder(self, ticket:Ticket, discord_ids: list[str], thread
266301
# action row with what options?
267302
# :warning:
268303
# ⚠️
269-
# [icon] **Alert** [Ticket x](link) will expire in x hours, as xyz.
270304
ids_str = [f"<@{id}>" for id in discord_ids]
271-
return f"⚠️ {' '.join(ids_str)} Dusty ticket was recycled, and needs new owner: {thread_url}"
305+
return f"⚠️ {' '.join(ids_str)} Ticket was unassigned due to inactivity, and needs new owner: {thread_url}"
272306

273307

274308
def format_ticket_alert(self, ticket: Ticket, discord_ids: set[int], msg: str) -> str:
@@ -488,16 +522,22 @@ def help_embed(self, _: discord.ApplicationContext) -> discord.Embed:
488522
return embed
489523

490524

491-
def main():
492-
pass
493-
#redmine = Client.fromenv()
525+
async def print_team(self, ctx, team):
526+
msg = f"> **{team.name}**\n"
527+
for user_rec in team.users:
528+
#user = self.redmine.get_user(user_rec.id)
529+
#discord_user = user.custom_fields[0].value or "" # FIXME cf_* lookup
530+
msg += f"{user_rec.name}, "
531+
#msg += f"[{user.id}] **{user_rec.name}** {user.login} {user.mail} {user.custom_fields[0].value}\n"
532+
msg = msg[:-2] + '\n\n'
533+
await ctx.channel.send(msg)
494534

495-
# construct the formatter
496-
#formatter = DiscordFormatter(redmine)
497535

498-
#tickets = ticket_manager.search("test")
499-
#output = formatter.format_tickets("Test Tickets", tickets)
500-
#print (output)
536+
def format_team(self, team) -> str:
537+
# single line format: teamname: member1, member2
538+
skip_teams = ["blocked", "users"]
501539

502-
if __name__ == '__main__':
503-
main()
540+
if team and team.name not in skip_teams:
541+
return f"**{team.name}**: {', '.join([user.name for user in team.members])}\n"
542+
else:
543+
return ""

redmine/users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ def get(self, user_id:int):
297297
"""get a user by ID, directly from redmine"""
298298
jresp = self.session.get(f"/users/{user_id}.json")
299299
if jresp:
300-
log.info(f"^^^ {jresp['user']}")
300+
#log.info(f"^^^ {jresp['user']}")
301301
return User(**jresp['user'])
302302

303303

0 commit comments

Comments
 (0)