14
14
from app .components .github_integration .mentions .cache import entity_cache
15
15
from app .components .github_integration .models import Comment , EntityGist , GitHubUser
16
16
from app .setup import gh
17
- from app .utils import TTRCache
17
+ from app .utils import TTRCache , escape_special
18
18
19
19
if TYPE_CHECKING :
20
20
import datetime as dt
21
- from collections .abc import AsyncIterator
21
+ from collections .abc import AsyncIterator , Callable
22
22
23
- from githubkit .versions .latest .models import PullRequestReviewComment
23
+ from githubkit .versions .latest .models import (
24
+ Issue ,
25
+ IssueEvent ,
26
+ IssueEventRename ,
27
+ PullRequestReviewComment ,
28
+ )
24
29
from pydantic import BaseModel
25
30
26
31
COMMENT_PATTERN = re .compile (
36
41
"CHANGES_REQUESTED" : 0xE74C3C , # red
37
42
}
38
43
EVENT_COLOR = 0x3498DB # blue
39
- ENTITY_UPDATE_EVENTS = frozenset ({"closed" , "locked" , "merged" , "reopened" , "unlocked" })
40
- SUPPORTED_EVENTS = {
41
- "assigned" : "Assigned `{event.assignee.login}`" ,
42
- "labeled" : "Added the `{event.label.name}` label" ,
43
- "milestoned" : "Added this to the `{event.milestone.title}` milestone" ,
44
- "review_requested" : "Requested review from `{reviewer}`" ,
45
- "unassigned" : "Unassigned `{event.assignee.login}`" ,
46
- "unlabeled" : "Removed the `{event.label.name}` label" ,
44
+ ENTITY_UPDATE_EVENTS = frozenset (
45
+ {"closed" , "locked" , "merged" , "reopened" , "unlocked" , "pinned" }
46
+ )
47
+ SUPPORTED_EVENTS : dict [str , str | Callable [[IssueEvent ], str ]] = {
48
+ "assigned" : "Assigned `{event.assignee.login}`." ,
49
+ "unassigned" : "Unassigned `{event.assignee.login}`." ,
50
+ "labeled" : "Added the `{event.label.name}` label." ,
51
+ "unlabeled" : "Removed the `{event.label.name}` label." ,
52
+ "milestoned" : "Added this to the `{event.milestone.title}` milestone." ,
53
+ "demilestoned" : "Removed this from the `{event.milestone.title}` milestone." ,
54
+ "convert_to_draft" : "Marked this pull request as draft." ,
55
+ "ready_for_review" : "Marked this pull request as ready for review." ,
56
+ "review_requested" : "Requested review from `{reviewer}`." ,
57
+ "auto_merge_enabled" : "Enabled auto-merge." ,
58
+ "auto_merge_disabled" : "Disabled auto-merge." ,
59
+ "head_ref_deleted" : "Deleted the head branch." ,
60
+ "head_ref_force_pushed" : lambda event : (
61
+ # HACK: there does not seem to be any easy way to get the HTML URL of the
62
+ # repository.
63
+ f"Force-pushed the head branch to [`{ cast ('str' , event .commit_id )[:7 ]} `](<{
64
+ cast ('Issue' , event .issue )
65
+ .repository_url .replace ('//api.' , '//' )
66
+ .replace ('/repos/' , '/' )
67
+ } /commit/{ event .commit_id } >)."
68
+ ),
69
+ "automatic_base_change_succeeded" : "Base automatically changed." ,
70
+ "converted_to_discussion" : "Converted this issue to a discussion." ,
71
+ "parent_issue_added" : "Added a parent issue." ,
72
+ "sub_issue_added" : "Added a sub-issue." ,
73
+ "referenced" : lambda event : (
74
+ f"Referenced this issue in commit [`{ cast ('str' , event .commit_id )[:7 ]} `]"
75
+ # HACK: once again, there does not seem to be any other way. And for
76
+ # some reason the HTML URL requires `commit` while the API URL requires
77
+ # `commits` (note the `s`)...
78
+ f"(<{
79
+ cast ('str' , event .commit_url )
80
+ .replace ('//api.' , '//' )
81
+ .replace ('/repos/' , '/' )
82
+ .replace ('commits' , 'commit' )
83
+ } >)."
84
+ ),
85
+ "renamed" : lambda event : (
86
+ f"Changed the title ~~{
87
+ escape_special ((rename := cast ('IssueEventRename' , event .rename )).from_ )
88
+ } ~~ { escape_special (rename .to )} ."
89
+ ),
47
90
}
48
91
49
92
@@ -160,7 +203,7 @@ async def _get_event(entity_gist: EntityGist, comment_id: int) -> Comment:
160
203
owner , repo , entity_no = entity_gist
161
204
event = (await gh .rest .issues .async_get_event (owner , repo , comment_id )).parsed_data
162
205
if event .event not in SUPPORTED_EVENTS .keys () | ENTITY_UPDATE_EVENTS :
163
- body = f":ghost: Unsupported event: `{ event .event } `"
206
+ body = f":ghost: Unsupported event: `{ event .event } `. "
164
207
elif event .event == "review_requested" :
165
208
# Special-cased to handle requests for both users and teams
166
209
if event .requested_reviewer :
@@ -170,14 +213,19 @@ async def _get_event(entity_gist: EntityGist, comment_id: int) -> Comment:
170
213
# Throwing in the org name to make it clear that it's a team
171
214
org_name = event .requested_team .html_url .split ("/" , 5 )[4 ]
172
215
reviewer = f"{ org_name } /{ event .requested_team .name } "
173
- body = SUPPORTED_EVENTS [event .event ].format (reviewer = reviewer )
216
+ formatter = SUPPORTED_EVENTS [event .event ]
217
+ assert not callable (formatter )
218
+ body = formatter .format (reviewer = reviewer )
174
219
elif event .event in ENTITY_UPDATE_EVENTS :
175
220
entity = await entity_cache .get (entity_gist )
176
- body = f"{ event .event .capitalize ()} the { entity .kind } "
221
+ body = f"{ event .event .capitalize ()} the { entity .kind . lower () } . "
177
222
if event .lock_reason :
178
- body += f"\n Reason: `{ event .lock_reason or 'unspecified' } ` "
223
+ body += f"\n Reason: `{ event .lock_reason } `. "
179
224
else :
180
- body = SUPPORTED_EVENTS [event .event ].format (event = event )
225
+ formatter = SUPPORTED_EVENTS [event .event ]
226
+ body = (
227
+ formatter (event ) if callable (formatter ) else formatter .format (event = event )
228
+ )
181
229
# The API doesn't return an html_url, gotta construct it manually.
182
230
# It's fine to say "issues" here, GitHub will resolve the correct type
183
231
url = f"https://github.com/{ owner } /{ repo } /issues/{ entity_no } #event-{ comment_id } "
0 commit comments