4848 r"((?P<org>[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P<repo>[\w\-\.]{1,100})#(?P<number>[0-9]+)"
4949)
5050
51+ class GithubAPIError (Exception ):
52+ """Raised when GitHub API returns a non 200 status code."""
53+
54+ def __init__ (self , status : int , message : str = "GitHub API error" ):
55+ self .status = status
56+ self .message = message
57+ super ().__init__ (f"{ message } (Status: { status } )" )
58+
59+ class StargazersLimitError (Exception ):
60+ """Raised when a repository exceeds the searchable stargazer limit."""
61+
5162
5263@dataclass (eq = True , frozen = True )
5364class FoundIssue :
@@ -374,7 +385,7 @@ async def get_issue_count(self, repo: str, start: str, end: str, state: str) ->
374385
375386 async with self .bot .http_session .get (url , headers = REQUEST_HEADERS , params = params ) as response :
376387 if response .status != 200 :
377- return - 1
388+ raise GithubAPIError ( response . status )
378389 data = await response .json ()
379390 return data .get ("total_count" , 0 )
380391
@@ -396,7 +407,7 @@ async def get_pr_count(self, repo: str, start: str, end: str, action: str) -> in
396407
397408 async with self .bot .http_session .get (url , headers = REQUEST_HEADERS , params = params ) as response :
398409 if response .status != 200 :
399- return - 1
410+ raise GithubAPIError ( response . status )
400411
401412 data = await response .json ()
402413 return data .get ("total_count" , 0 )
@@ -411,7 +422,7 @@ async def get_commit_count(self, repo_str: str, start_str: str, end_str: str) ->
411422
412423 async with self .bot .http_session .get (url , headers = REQUEST_HEADERS , params = params ) as response :
413424 if response .status != 200 :
414- return - 1
425+ raise GithubAPIError ( response . status )
415426
416427 commits_json = await response .json ()
417428 # No commits
@@ -437,7 +448,7 @@ async def _fetch_page(self, url: str, headers: dict, page: int, cache: dict) ->
437448 params = {"per_page" : 100 , "page" : page }
438449 async with self .bot .http_session .get (url , headers = headers , params = params ) as response :
439450 if response .status != 200 :
440- return []
451+ raise GithubAPIError ( response . status )
441452 cache [page ] = await response .json ()
442453 return cache [page ]
443454
@@ -462,7 +473,7 @@ async def get_stars_gained(self, repo: str, start: str, end: str) -> int:
462473
463474 repo_data , response = await self .fetch_data (f"{ GITHUB_API_URL } /repos/{ repo } " )
464475 if response .status != 200 :
465- return - 1
476+ raise GithubAPIError ( response . status )
466477
467478 max_stars = repo_data .get ("stargazers_count" , 0 )
468479
@@ -473,7 +484,7 @@ async def get_stars_gained(self, repo: str, start: str, end: str) -> int:
473484 # Because of this the output is not consistent for projects with more than 40 000 stars so we default to -2
474485 github_stargazer_limit = 40000
475486 if max_stars > github_stargazer_limit :
476- return - 2
487+ raise StargazersLimitError ( "Repository exceeds the 40,000 star limit." )
477488 searchable_stars = max_stars
478489
479490 # We use a cache and binary search to limit the number of requests to the GitHub API
@@ -566,7 +577,7 @@ async def github_stats(self, ctx: commands.Context, start: str, end: str, repo:
566577 return
567578
568579 url = f"{ GITHUB_API_URL } /repos/{ repo } "
569- repo_data , response = await self .fetch_data (url )
580+ repo_data , _ = await self .fetch_data (url )
570581
571582 if "message" in repo_data :
572583 embed = discord .Embed (
@@ -577,22 +588,28 @@ async def github_stats(self, ctx: commands.Context, start: str, end: str, repo:
577588 await ctx .send (embed = embed )
578589 return
579590
580- open_issues = await self .get_issue_count (repo , start , end , state = "created" )
581- closed_issues = await self .get_issue_count (repo , start , end , state = "closed" )
582- prs_opened = await self .get_pr_count (repo , start , end , "opened" )
583- prs_closed = await self .get_pr_count (repo , start , end , "closed" )
584- prs_merged = await self .get_pr_count (repo , start , end , "merged" )
585- commits = await self .get_commit_count (repo , start , end )
586- stars_gained = await self .get_stars_gained (repo , start , end )
587-
588- if stars_gained == - 2 :
589- stars = "N/A (repo exceeds API limit)"
590- elif stars_gained > 0 :
591- stars = f"+{ stars_gained } "
592- elif stars_gained == 0 :
593- stars = "0"
594- else :
595- stars = "unavailable"
591+ try :
592+ open_issues = await self .get_issue_count (repo , start , end , state = "created" )
593+ closed_issues = await self .get_issue_count (repo , start , end , state = "closed" )
594+ prs_opened = await self .get_pr_count (repo , start , end , "opened" )
595+ prs_closed = await self .get_pr_count (repo , start , end , "closed" )
596+ prs_merged = await self .get_pr_count (repo , start , end , "merged" )
597+ commits = await self .get_commit_count (repo , start , end )
598+
599+ try :
600+ stars_gained = await self .get_stars_gained (repo , start , end )
601+ stars = f"+{ stars_gained } " if stars_gained > 0 else "0"
602+ except StargazersLimitError :
603+ stars = "N/A (repo exceeded API limit)"
604+
605+ except GithubAPIError as e :
606+ embed = discord .Embed (
607+ title = random .choice (NEGATIVE_REPLIES ),
608+ description = f"Failed to fetch data from GitHub API. (Status Code: { e .status } )" ,
609+ colour = Colours .soft_red ,
610+ )
611+ await ctx .send (embed = embed )
612+ return
596613
597614 stats_text = (
598615 f"Issues opened: { open_issues } \n "
0 commit comments