From 5622209f4d22c7e11240dd37678d88fde98838d7 Mon Sep 17 00:00:00 2001 From: Zevaryx Date: Fri, 23 Jul 2021 13:47:04 -0600 Subject: [PATCH] Paginator viewing for issues, merge requests, and milestones --- jarvis/cogs/error.py | 3 +- jarvis/cogs/gitlab.py | 331 ++++++++++++++++++++++++++++-------------- 2 files changed, 221 insertions(+), 113 deletions(-) diff --git a/jarvis/cogs/error.py b/jarvis/cogs/error.py index f9bbbfb..df21073 100644 --- a/jarvis/cogs/error.py +++ b/jarvis/cogs/error.py @@ -34,7 +34,8 @@ class ErrorHandlerCog(commands.Cog): ) else: await ctx.send( - f"Error processing command:\n```{error}```", hidden=True + f"Error processing command:\n```{error}```", + hidden=True, ) diff --git a/jarvis/cogs/gitlab.py b/jarvis/cogs/gitlab.py index 0030950..99e551e 100644 --- a/jarvis/cogs/gitlab.py +++ b/jarvis/cogs/gitlab.py @@ -17,36 +17,6 @@ from jarvis.utils.field import Field guild_ids = [862402786116763668] -def create_layout(command: str): - buttons = [ - manage_components.create_button( - style=ButtonStyle.gray, emoji="◀", custom_id=f"back_{command}" - ), - manage_components.create_button( - style=ButtonStyle.gray, - emoji="▶", - custom_id=f"next_{command}", - ), - ] - action_row = manage_components.spread_to_rows(*buttons, max_in_row=2) - return action_row - - -def create_issues_layout(): - return create_layout("issues") - - -def edit_paging_components( - components: list, disable_left: bool = False, disable_right: bool = False -): - components[0]["components"][0]["disabled"] = disable_left - components[0]["components"][1]["disabled"] = disable_right - return components - - -command_lookup = {"issues": create_issues_layout} - - class GitlabCog(commands.Cog): def __init__(self, bot): self.bot = bot @@ -200,7 +170,7 @@ class GitlabCog(commands.Cog): @cog_ext.cog_subcommand( base="gl", - name="mr", + name="mergerequest", description="Get an merge request from GitLab", guild_ids=guild_ids, options=[ @@ -212,7 +182,7 @@ class GitlabCog(commands.Cog): ) ], ) - async def _mr(self, ctx: SlashContext, id: int): + async def _mergerequest(self, ctx: SlashContext, id: int): try: mr = self.project.mergerequests.get(int(id)) except gitlab.exceptions.GitlabGetError: @@ -283,23 +253,28 @@ class GitlabCog(commands.Cog): ) await ctx.send(embed=embed) - def build_embed_page(self, issues: list, t_state: str): + def build_embed_page(self, api_list: list, t_state: str, name: str): + title = "" + if t_state: + title = f"{t_state} " + title += f"J.A.R.V.I.S. {name}s" fields = [] - for issue in issues: + for item in api_list: fields.append( Field( - name=f"[#{issue.iid}] {issue.title}", - value=issue.description - + f"\n\n[View this issue]({issue.web_url})", + name=f"[#{item.iid}] {item.title}", + value=item.description + + f"\n\n[View this {name}]({item.web_url})", inline=False, ) ) embed = build_embed( - title=f"{t_state} J.A.R.V.I.S. issues", + title=title, description="", fields=fields, - url="https://git.zevaryx.com/stark-industries/j.a.r.v.i.s./issues", + url="https://git.zevaryx.com/stark-industries/j.a.r.v.i.s./" + + f"{name.replace(' ', '_')}s", ) embed.set_author( name="J.A.R.V.I.S.", @@ -313,6 +288,16 @@ class GitlabCog(commands.Cog): ) return embed + def check_cache(self, ctx: SlashContext, **kwargs): + if not kwargs: + kwargs = {} + return find( + lambda x: x["command"] == ctx.subcommand_name + and x["user"] == ctx.author.id + and all(x[k] == v for k, v in kwargs.items()), + self.cache.values(), + ) + @cog_ext.cog_subcommand( base="gl", name="issues", @@ -333,17 +318,12 @@ class GitlabCog(commands.Cog): ], ) async def _issues(self, ctx: SlashContext, state: str = "opened"): - exists = find( - lambda x: x["_command"] == "issues" - and x["user"] == ctx.author.id - and x["state"] == state, - self.cache.values(), - ) + exists = self.check_cache(ctx, state=state) if exists: await ctx.defer(hidden=True) await ctx.send( "Please use existing interaction: " - + f"{exists['_message'].jump_url}", + + f"{exists['paginator']._message.jump_url}", hidden=True, ) return @@ -351,91 +331,221 @@ class GitlabCog(commands.Cog): m_state = state if m_state == "all": m_state = None - # issues = [] - # page = 1 + issues = [] + page = 1 try: - # issues += self.project.issues.list(page=page, state=m_state, order_by="created_at", sort="desc", per_page=100) - issues = self.project.issues.list( - page=1, + while curr_page := self.project.issues.list( + page=page, state=m_state, order_by="created_at", sort="desc", - per_page=5, - ) - next_issues = self.project.issues.list( - page=2, - state=m_state, - order_by="created_at", - sort="desc", - per_page=5, - ) + per_page=100, + ): + issues += curr_page + page += 1 except gitlab.exceptions.GitlabGetError: - await ctx.send("Unable to get issues", hidden=True) + # Only send error on first page. Otherwise, use pages retrieved + if page == 1: + await ctx.send("Unable to get issues") + return + + if len(issues) == 0: + await ctx.send("No issues match that criteria") return + t_state = state if t_state == "opened": t_state = "open" - + pages = [] t_state = t_state[0].upper() + t_state[1:] + for i in range(0, len(issues), 5): + pages.append( + self.build_embed_page( + issues[i : i + 5], t_state=t_state, name="issue" + ) + ) - components = create_issues_layout() - components = edit_paging_components( - components, True, next_issues == [] + paginator = Paginator( + bot=self.bot, + ctx=ctx, + embeds=pages, + only=ctx.author, + timeout=60 * 5, # 5 minute timeout + disable_after_timeout=True, + use_extend=len(pages) > 2, + left_button_style=ButtonStyle.grey, + right_button_style=ButtonStyle.grey, + basic_buttons=["◀", "▶"], ) - embed = self.build_embed_page(issues, t_state=t_state) - message = await ctx.send(embed=embed, components=components) - self.cache[message.id] = { + + self.cache[hash(paginator)] = { "user": ctx.author.id, - "state": state, - "t_state": t_state, - "page": 1, "timeout": datetime.utcnow() + timedelta(minutes=5), - "_command": "issues", - "_message": message, + "command": ctx.subcommand_name, + "state": state, + "paginator": paginator, } - @cog_ext.cog_component(components=create_issues_layout()) - async def _process(self, ctx: ComponentContext): - await ctx.defer(edit_origin=True) - cache = self.cache[ctx.origin_message.id] - if ctx.author.id != cache["user"]: + await paginator.start() + + @cog_ext.cog_subcommand( + base="gl", + name="mergerequests", + description="Get open issues from GitLab", + guild_ids=guild_ids, + options=[ + create_option( + name="state", + description="State of issues to get", + option_type=3, + required=False, + choices=[ + create_choice(name="Open", value="opened"), + create_choice(name="Closed", value="closed"), + create_choice(name="Merged", value="merged"), + create_choice(name="All", value="all"), + ], + ) + ], + ) + async def _mergerequests(self, ctx: SlashContext, state: str = "opened"): + exists = self.check_cache(ctx, state=state) + if exists: + await ctx.defer(hidden=True) + await ctx.send( + "Please use existing interaction: " + + f"{exists['paginator']._message.jump_url}", + hidden=True, + ) + return + await ctx.defer() + m_state = state + if m_state == "all": + m_state = None + merges = [] + page = 1 + try: + while curr_page := self.project.mergerequests.list( + page=page, + state=m_state, + order_by="created_at", + sort="desc", + per_page=100, + ): + merges += curr_page + page += 1 + except gitlab.exceptions.GitlabGetError: + # Only send error on first page. Otherwise, use pages retrieved + if page == 1: + await ctx.send("Unable to get merge requests") + return + + if len(merges) == 0: + await ctx.send("No merge requests match that criteria") return - state = cache["state"] - page = cache["page"] - if ctx.custom_id == "back": - page -= 1 - else: - page += 1 - try: - issues = self.project.issues.list( - page=page, - state=state, - order_by="created_at", - sort="desc", - per_page=5, + t_state = state + if t_state == "opened": + t_state = "open" + pages = [] + t_state = t_state[0].upper() + t_state[1:] + for i in range(0, len(merges), 5): + pages.append( + self.build_embed_page( + merges[i : i + 5], t_state=t_state, name="merge request" + ) ) - next_issues = self.project.issues.list( - page=page + 1, - state=state, - order_by="created_at", - sort="desc", - per_page=5, + + paginator = Paginator( + bot=self.bot, + ctx=ctx, + embeds=pages, + only=ctx.author, + timeout=60 * 5, # 5 minute timeout + disable_after_timeout=True, + use_extend=len(pages) > 2, + left_button_style=ButtonStyle.grey, + right_button_style=ButtonStyle.grey, + basic_buttons=["◀", "▶"], + ) + + self.cache[hash(paginator)] = { + "user": ctx.author.id, + "timeout": datetime.utcnow() + timedelta(minutes=5), + "command": ctx.subcommand_name, + "state": state, + "paginator": paginator, + } + + await paginator.start() + + @cog_ext.cog_subcommand( + base="gl", + name="milestones", + description="Get open issues from GitLab", + guild_ids=guild_ids, + ) + async def _milestones(self, ctx: SlashContext): + exists = self.check_cache(ctx) + if exists: + await ctx.defer(hidden=True) + await ctx.send( + "Please use existing interaction: " + + f"{exists['paginator']._message.jump_url}", + hidden=True, ) - except gitlab.exceptions.GitlabGetError: - await ctx.edit_origin(content="Unable to get issues", hidden=True) return - components = create_issues_layout() - components = edit_paging_components( - components=components, - disable_left=page == 1, - disable_right=next_issues == [], + await ctx.defer() + milestones = [] + page = 1 + try: + while curr_page := self.project.milestones.list( + page=page, + order_by="created_at", + sort="desc", + per_page=100, + ): + milestones += curr_page + page += 1 + except gitlab.exceptions.GitlabGetError: + # Only send error on first page. Otherwise, use pages retrieved + if page == 1: + await ctx.send("Unable to get milestones") + return + + if len(milestones) == 0: + await ctx.send("No milestones exist") + return + + pages = [] + for i in range(0, len(milestones), 5): + pages.append( + self.build_embed_page( + milestones[i : i + 5], t_state=None, name="milestone" + ) + ) + + paginator = Paginator( + bot=self.bot, + ctx=ctx, + embeds=pages, + only=ctx.author, + timeout=60 * 5, # 5 minute timeout + disable_after_timeout=True, + use_extend=len(pages) > 2, + left_button_style=ButtonStyle.grey, + right_button_style=ButtonStyle.grey, + basic_buttons=["◀", "▶"], ) - embed = self.build_embed_page( - issues=issues, t_state=self.cache[ctx.origin_message.id]["t_state"] - ) - self.cache[ctx.origin_message.id]["page"] = page - await ctx.edit_origin(embed=embed, components=components) + + self.cache[hash(paginator)] = { + "user": ctx.author.id, + "timeout": datetime.utcnow() + timedelta(minutes=5), + "command": ctx.subcommand_name, + "paginator": paginator, + } + + await paginator.start() @loop(minutes=1) async def _expire_interaction(self): @@ -444,9 +554,6 @@ class GitlabCog(commands.Cog): if self.cache[key]["timeout"] <= datetime.utcnow() + timedelta( minutes=1 ): - components = create_layout(self.cache[key]["_command"]) - components = edit_paging_components(components, True, True) - await self.cache[key]["_message"].edit(components=components) del self.cache[key]