22import re
33from dataclasses import dataclass
44from datetime import UTC , datetime
5+ from typing import Any
56from urllib .parse import quote
67
78import discord
2223
2324REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos?per_page=100&type=public"
2425ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}"
25- PR_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}"
2626
2727if Tokens .github :
2828 REQUEST_HEADERS ["Authorization" ] = f"token { Tokens .github .get_secret_value ()} "
@@ -83,19 +83,13 @@ def remove_codeblocks(message: str) -> str:
8383 """Remove any codeblock in a message."""
8484 return CODE_BLOCK_RE .sub ("" , message )
8585
86- async def fetch_issue (
87- self ,
88- number : int ,
89- repository : str ,
90- user : str
91- ) -> IssueState | FetchError :
86+ async def fetch_issue (self , number : int , repository : str , user : str ) -> IssueState | FetchError :
9287 """
9388 Retrieve an issue from a GitHub repository.
9489
9590 Returns IssueState on success, FetchError on failure.
9691 """
9792 url = ISSUE_ENDPOINT .format (user = user , repository = repository , number = number )
98- pulls_url = PR_ENDPOINT .format (user = user , repository = repository , number = number )
9993
10094 json_data , r = await self .fetch_data (url )
10195
@@ -109,35 +103,44 @@ async def fetch_issue(
109103 if r .status != 200 :
110104 return FetchError (r .status , "Error while fetching issue." )
111105
112- # The initial API request is made to the issues API endpoint, which will return information
113- # if the issue or PR is present. However, the scope of information returned for PRs differs
114- # from issues: if the 'issues' key is present in the response then we can pull the data we
115- # need from the initial API call.
116- if "issues" in json_data ["html_url" ]:
117- emoji = Emojis .issue_open
118- if json_data .get ("state" ) == "closed" :
119- emoji = Emojis .issue_completed
120- if json_data .get ("state_reason" ) == "not_planned" :
121- emoji = Emojis .issue_not_planned
122-
123- # If the 'issues' key is not contained in the API response and there is no error code, then
124- # we know that a PR has been requested and a call to the pulls API endpoint is necessary
125- # to get the desired information for the PR.
126- else :
127- pull_data , _ = await self .fetch_data (pulls_url )
128- if pull_data ["draft" ]:
106+ # its important to note that the issues endpoint only provides issues and pull requests
107+ # discussions are not supported, but may still be provided
108+ # this method doesn't check for discussions, it just silently ignores them
109+ log .trace ("Fetched issue/PR data: %r" , json_data )
110+ if pull_data := json_data .get ("pull_request" ):
111+ if pull_data .get ("merged_at" ):
112+ emoji = Emojis .pull_request_merged
113+ elif json_data .get ("draft" ) is True :
129114 emoji = Emojis .pull_request_draft
130- elif pull_data [ "state" ] == "open" :
115+ elif json_data . get ( "state" ) == "open" :
131116 emoji = Emojis .pull_request_open
132- # When 'merged_at' is not None, this means that the state of the PR is merged
133- elif pull_data ["merged_at" ] is not None :
134- emoji = Emojis .pull_request_merged
135- else :
117+ elif json_data .get ("state" ) == "closed" :
136118 emoji = Emojis .pull_request_closed
119+ else :
120+ # unknown state, GitHub added a new state and the emoji should be added
121+ log .error ("Unknown PR state: %s for %s" , json_data .get ("state" ), url )
122+ # fall the emoji back to a state
123+ emoji = Emojis .pull_request_open
124+ else :
125+ if json_data .get ("state" ) == "closed" :
126+ if json_data .get ("state_reason" ) == "not_planned" :
127+ emoji = Emojis .issue_not_planned
128+ else :
129+ emoji = Emojis .issue_completed
130+ elif json_data .get ("draft" ) is True :
131+ # not currently used by GitHub, but future planning
132+ emoji = Emojis .issue_draft
133+ elif json_data .get ("state" ) == "open" :
134+ emoji = Emojis .issue_open
135+ else :
136+ # unknown state, GitHub added a new state and the emoji should be added
137+ log .error ("Unknown issue state: %s for %s" , json_data .get ("state" ), url )
138+ # fall the emoji back to a state
139+ emoji = Emojis .issue_open
137140
138- issue_url = json_data . get ( "html_url" )
141+ html_url = json_data [ "html_url" ]
139142
140- return IssueState (repository , number , issue_url , json_data .get ("title" , "" ), emoji )
143+ return IssueState (repository , number , html_url , json_data .get ("title" , "" ), emoji )
141144
142145 @staticmethod
143146 def format_embed (
@@ -217,7 +220,7 @@ async def on_message(self, message: discord.Message) -> None:
217220 resp = self .format_embed (links )
218221 await message .channel .send (embed = resp )
219222
220- async def fetch_data (self , url : str ) -> tuple [dict [str ], ClientResponse ]:
223+ async def fetch_data (self , url : str ) -> tuple [dict [str , Any ], ClientResponse ]:
221224 """Retrieve data as a dictionary and the response in a tuple."""
222225 log .trace (f"Querying GH issues API: { url } " )
223226 async with self .bot .http_session .get (url , headers = REQUEST_HEADERS ) as r :
0 commit comments