Skip to content

Commit 125ee12

Browse files
committed
Add refresh buttons to issue and commit messages
1 parent d73c48f commit 125ee12

File tree

9 files changed

+408
-160
lines changed

9 files changed

+408
-160
lines changed

bot/src/ghutils/cogs/app_commands/context_menus.py

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
from types import MethodType
55
from typing import Any, Callable
66

7-
from discord import Embed, Interaction, Message, app_commands
7+
from discord import Interaction, Message, app_commands
88
from discord.abc import MISSING
99
from discord.app_commands import ContextMenu
1010
from discord.utils import Coro
1111

1212
from ghutils.core.cog import GHUtilsCog
13+
from ghutils.utils.discord.components import RefreshIssueButton
1314
from ghutils.utils.discord.embeds import create_issue_embed
14-
from ghutils.utils.discord.references import IssueReferenceTransformer
15+
from ghutils.utils.discord.references import IssueReference, IssueReferenceTransformer
1516
from ghutils.utils.discord.visibility import respond_with_visibility
1617

1718
logger = logging.getLogger(__name__)
@@ -90,37 +91,50 @@ async def show_issues(self, interaction: Interaction, message: Message):
9091
await interaction.response.defer()
9192

9293
seen = set[str]()
93-
embeds = list[Embed]()
94+
issues = list[IssueReference]()
9495
transformer = IssueReferenceTransformer()
9596

96-
for match in _issue_pattern.finditer(message.content):
97-
value = match.group("value")
98-
try:
99-
repo, issue = await transformer.transform(interaction, value)
100-
except Exception:
101-
logger.warning(
102-
f"Failed to transform issue reference: {value}", exc_info=True
103-
)
104-
continue
105-
106-
if issue.html_url in seen:
107-
continue
108-
seen.add(issue.html_url)
109-
110-
embeds.append(create_issue_embed(repo, issue, add_body=False))
111-
if len(embeds) >= 10:
112-
break
113-
114-
if not embeds:
115-
await respond_with_visibility(
116-
interaction,
117-
"public",
118-
content="No issue references found.",
119-
)
120-
return
121-
122-
await respond_with_visibility(
123-
interaction,
124-
"public",
125-
embeds=embeds,
126-
)
97+
async with self.bot.github_app(interaction) as (github, _):
98+
for match in _issue_pattern.finditer(message.content):
99+
value = match.group("value")
100+
try:
101+
repo, issue = await transformer.transform_with_github(
102+
github, interaction, value
103+
)
104+
except Exception:
105+
logger.warning(
106+
f"Failed to transform issue reference: {value}", exc_info=True
107+
)
108+
continue
109+
110+
if issue.html_url in seen:
111+
continue
112+
113+
seen.add(issue.html_url)
114+
issues.append((repo, issue))
115+
116+
visibility = "public"
117+
match issues:
118+
case []:
119+
await respond_with_visibility(
120+
interaction,
121+
visibility,
122+
content="❌ No issue references found.",
123+
)
124+
case [reference]:
125+
button = await RefreshIssueButton.from_reference(github, reference)
126+
await respond_with_visibility(
127+
interaction,
128+
visibility,
129+
embed=create_issue_embed(*reference),
130+
items=[button],
131+
)
132+
case _:
133+
await respond_with_visibility(
134+
interaction,
135+
visibility,
136+
embeds=[
137+
create_issue_embed(*reference, add_body=False)
138+
for reference in issues
139+
],
140+
)

bot/src/ghutils/cogs/app_commands/github.py

Lines changed: 23 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@
55
import uuid
66
from datetime import datetime
77
from pathlib import Path
8-
from typing import Any
98

109
import pfzy
1110
from discord import Color, Embed, Interaction, app_commands
1211
from discord.app_commands import Range
1312
from discord.ext.commands import GroupCog
1413
from discord.ui import Button, View
15-
from githubkit import GitHub
16-
from githubkit.exception import GitHubException, RequestFailed
17-
from githubkit.rest import SimpleUser
14+
from githubkit.exception import RequestFailed
1815
from more_itertools import consecutive_groups, ilen
1916
from Pylette import extract_colors # pyright: ignore[reportUnknownVariableType]
2017
from yarl import URL
@@ -27,23 +24,21 @@
2724
UserGitHubTokens,
2825
UserLogin,
2926
)
30-
from ghutils.utils.discord.embeds import create_issue_embed, set_embed_author
27+
from ghutils.utils.discord.components import RefreshCommitButton, RefreshIssueButton
28+
from ghutils.utils.discord.embeds import (
29+
create_commit_embed,
30+
create_issue_embed,
31+
set_embed_author,
32+
)
3133
from ghutils.utils.discord.references import (
3234
CommitReference,
3335
IssueReference,
3436
PRReference,
3537
)
3638
from ghutils.utils.discord.transformers import RepositoryOption, UserOption
3739
from ghutils.utils.discord.visibility import MessageVisibility, respond_with_visibility
38-
from ghutils.utils.github import (
39-
CommitCheckState,
40-
RepositoryName,
41-
SmartPaginator,
42-
gh_request,
43-
shorten_sha,
44-
)
40+
from ghutils.utils.github import gh_request
4541
from ghutils.utils.l10n import translate_text
46-
from ghutils.utils.strings import truncate_str
4742

4843
logger = logging.getLogger(__name__)
4944

@@ -61,10 +56,14 @@ async def issue(
6156
reference: IssueReference,
6257
visibility: MessageVisibility = "private",
6358
):
59+
async with self.bot.github_app(interaction) as (github, _):
60+
button = await RefreshIssueButton.from_reference(github, reference)
61+
6462
await respond_with_visibility(
6563
interaction,
6664
visibility,
6765
embed=create_issue_embed(*reference),
66+
items=[button],
6867
)
6968

7069
@app_commands.command()
@@ -75,10 +74,14 @@ async def pr(
7574
reference: PRReference,
7675
visibility: MessageVisibility = "private",
7776
):
77+
async with self.bot.github_app(interaction) as (github, _):
78+
button = await RefreshIssueButton.from_reference(github, reference)
79+
7880
await respond_with_visibility(
7981
interaction,
8082
visibility,
8183
embed=create_issue_embed(*reference),
84+
items=[button],
8285
)
8386

8487
@app_commands.command()
@@ -89,39 +92,17 @@ async def commit(
8992
reference: CommitReference,
9093
visibility: MessageVisibility = "private",
9194
):
92-
repo, commit = reference
93-
9495
async with self.bot.github_app(interaction) as (github, _):
95-
state = await _get_commit_check_state(github, repo, commit.sha)
96-
97-
short_sha = shorten_sha(commit.sha)
98-
99-
message = commit.commit.message
100-
description = None
101-
if "\n" in message:
102-
message, description = message.split("\n", maxsplit=1)
103-
description = truncate_str(description.strip(), 200)
96+
embed = await create_commit_embed(github, *reference)
97+
button = await RefreshCommitButton.from_reference(github, reference)
10498

105-
embed = Embed(
106-
title=truncate_str(f"[{short_sha}] {message}", 256),
107-
description=description,
108-
url=commit.html_url,
109-
color=state.color,
110-
).set_footer(
111-
text=f"{repo}@{short_sha}",
99+
await respond_with_visibility(
100+
interaction,
101+
visibility,
102+
embed=embed,
103+
items=[button],
112104
)
113105

114-
if (author := commit.commit.author) and author.date:
115-
try:
116-
embed.timestamp = datetime.fromisoformat(author.date)
117-
except ValueError:
118-
pass
119-
120-
if isinstance(commit.author, SimpleUser):
121-
set_embed_author(embed, commit.author)
122-
123-
await respond_with_visibility(interaction, visibility, embed=embed)
124-
125106
@app_commands.command()
126107
async def repo(
127108
self,
@@ -476,72 +457,3 @@ def _discord_date(timestamp: int | float | datetime):
476457
case datetime():
477458
timestamp = int(timestamp.timestamp())
478459
return f"<t:{timestamp}:f> (<t:{timestamp}:R>)"
479-
480-
481-
# we need to look at both checks and commit statuses
482-
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#types-of-status-checks-on-github
483-
# if anything is in progress, return PENDING
484-
# else if anything failed, return FAILURE
485-
# else if anything succeeded, return SUCCESS
486-
# else return PENDING
487-
async def _get_commit_check_state(
488-
github: GitHub[Any],
489-
repo: RepositoryName,
490-
sha: str,
491-
) -> CommitCheckState:
492-
state = CommitCheckState.NEUTRAL
493-
494-
# checks
495-
try:
496-
async for suite in SmartPaginator(
497-
github.rest.checks.async_list_suites_for_ref,
498-
owner=repo.owner,
499-
repo=repo.repo,
500-
ref=sha,
501-
map_func=lambda resp: resp.parsed_data.check_suites,
502-
limit_func=lambda resp: resp.parsed_data.total_count,
503-
):
504-
match suite.status:
505-
case "queued":
506-
# this is the default status
507-
# it seems to show up for suites that aren't actually in the UI
508-
# so just ignore it
509-
pass
510-
case "completed":
511-
match suite.conclusion:
512-
case "success":
513-
if state is not CommitCheckState.FAILURE:
514-
state = CommitCheckState.SUCCESS
515-
case "failure" | "timed_out" | "startup_failure":
516-
state = CommitCheckState.FAILURE
517-
case _:
518-
pass
519-
case _:
520-
return CommitCheckState.PENDING
521-
except GitHubException:
522-
pass
523-
524-
if state is CommitCheckState.FAILURE:
525-
return state
526-
527-
# commit statuses
528-
# if we get to this point, either all checks passed or there are no checks
529-
try:
530-
combined_status = await gh_request(
531-
github.rest.repos.async_get_combined_status_for_ref(
532-
owner=repo.owner,
533-
repo=repo.repo,
534-
ref=sha,
535-
)
536-
)
537-
match combined_status.state:
538-
case "success":
539-
return CommitCheckState.SUCCESS
540-
case "failure":
541-
return CommitCheckState.FAILURE
542-
case _:
543-
pass
544-
except GitHubException:
545-
pass
546-
547-
return state

bot/src/ghutils/cogs/events.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ghutils.core.cog import GHUtilsCog
77
from ghutils.utils.discord.commands import get_command, print_command
8+
from ghutils.utils.discord.components import RefreshCommitButton, RefreshIssueButton
89
from ghutils.utils.discord.visibility import PermanentDeleteButton
910

1011
logger = logging.getLogger(__name__)
@@ -16,7 +17,11 @@ class EventsCog(GHUtilsCog):
1617
@Cog.listener()
1718
async def on_ready(self):
1819
logger.info(f"Logged in as {self.bot.user}")
19-
self.bot.add_dynamic_items(PermanentDeleteButton)
20+
self.bot.add_dynamic_items(
21+
PermanentDeleteButton,
22+
RefreshCommitButton,
23+
RefreshIssueButton,
24+
)
2025
await self.bot.fetch_custom_emojis()
2126

2227
@Cog.listener()

bot/src/ghutils/core/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ class LoginState(Enum):
1010
LOGGED_OUT = auto()
1111
EXPIRED = auto()
1212

13+
def logged_in(self):
14+
return self is LoginState.LOGGED_IN
15+
16+
def logged_out(self):
17+
return not self.logged_in()
18+
1319

1420
class CustomEmoji(Enum):
1521
apps_icon = "apps_icon.png"

0 commit comments

Comments
 (0)