From 9b62d84554fe8a27e3baef5e73eaac9127b2e54d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 23 Jan 2021 17:39:51 +0530 Subject: Add ability to automatically send issues if matching # --- bot/exts/evergreen/issues.py | 96 +++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 27 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index e419a6f5..b701346c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -1,5 +1,7 @@ import logging import random +import re +import typing as t import discord from discord.ext import commands @@ -13,9 +15,7 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } - MAX_REQUESTS = 10 - REQUEST_HEADERS = dict() if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -27,31 +27,19 @@ class Issues(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @commands.command(aliases=("pr",)) - @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) - async def issue( - self, - ctx: commands.Context, - numbers: commands.Greedy[int], - repository: str = "sir-lancebot", - user: str = "python-discord" - ) -> None: - """Command to retrieve issue(s) from a GitHub repository.""" + async def fetch_issues( + self, + numbers: set, + repository: str, + user: str + ) -> t.Union[str, list]: + """Retrieve issue(s) from a GitHub repository.""" links = [] - numbers = set(numbers) # Convert from list to set to remove duplicates, if any - if not numbers: - await ctx.invoke(self.bot.get_command('help'), 'issue') - return + return "Numbers not found." if len(numbers) > MAX_REQUESTS: - embed = discord.Embed( - title=random.choice(ERROR_REPLIES), - color=Colours.soft_red, - description=f"Too many issues/PRs! (maximum of {MAX_REQUESTS})" - ) - await ctx.send(embed=embed) - return + return "Max requests hit." for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" @@ -63,7 +51,7 @@ class Issues(commands.Cog): if r.status in BAD_RESPONSE: log.warning(f"Received response {r.status} from: {url}") - return await ctx.send(f"[{str(r.status)}] #{number} {BAD_RESPONSE.get(r.status)}") + return f"[{str(r.status)}] #{number} {BAD_RESPONSE.get(r.status)}" # The initial API request is made to the issues API endpoint, which will return information # if the issue or PR is present. However, the scope of information returned for PRs differs @@ -92,15 +80,69 @@ class Issues(commands.Cog): issue_url = json_data.get("html_url") links.append([icon_url, f"[{repository}] #{number} {json_data.get('title')}", issue_url]) - # Issue/PR format: emoji to show if open/closed/merged, number and the title as a singular link. - description_list = ["{0} [{1}]({2})".format(*link) for link in links] + return links + + @staticmethod + def get_embed(result: list, user: str = "python-discord", repository: str = "") -> discord.Embed: + """Get Response Embed.""" + description_list = ["{0} [{1}]({2})".format(*link) for link in result] resp = discord.Embed( colour=Colours.bright_green, description='\n'.join(description_list) ) resp.set_author(name="GitHub", url=f"https://github.com/{user}/{repository}") - await ctx.send(embed=resp) + return resp + + @commands.command(aliases=("pr",)) + @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) + async def issue( + self, + ctx: commands.Context, + numbers: commands.Greedy[int], + repository: str = "sir-lancebot", + user: str = "python-discord" + ) -> None: + """Command to retrieve issue(s) from a GitHub repository.""" + print(numbers) + result = await self.fetch_issues(set(numbers), repository, user) + + if result == "Numbers not found.": + await ctx.invoke(self.bot.get_command('help'), 'issue') + + elif result == "Max requests hit.": + embed = discord.Embed( + title=random.choice(ERROR_REPLIES), + color=Colours.soft_red, + description=f"Too many issues/PRs! (maximum of {MAX_REQUESTS})" + ) + await ctx.send(embed=embed) + + elif isinstance(result, list): + # Issue/PR format: emoji to show if open/closed/merged, number and the title as a singular link. + resp = self.get_embed(result, user, repository) + await ctx.send(embed=resp) + + elif isinstance(result, str): + await ctx.send(result) + + @commands.Cog.listener() + async def on_message(self, message: discord.Message) -> None: + """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" + message_repo_issue_map = re.findall(r".+?(bot|meta|sir-lancebot|logcord)#(\d+)", message.content) + links = [] + + if message_repo_issue_map: + for repo_issue in message_repo_issue_map: + result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") + if isinstance(result, list): + links.append(*result) + + if not links: + return + + resp = self.get_embed(links, "python-discord") + await message.channel.send(embed=resp) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From fbe073cb5e8dca831b4126c5b221844032870d26 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 16:28:36 +0530 Subject: Add in codeblock check --- bot/exts/evergreen/issues.py | 48 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index b701346c..1924d822 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -4,7 +4,7 @@ import re import typing as t import discord -from discord.ext import commands +from discord.ext import commands, tasks from bot.constants import Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS from bot.utils.decorators import override_in_channel @@ -17,15 +17,43 @@ BAD_RESPONSE = { } MAX_REQUESTS = 10 REQUEST_HEADERS = dict() +PYDIS_REPOS = "https://api.github.com/orgs/python-discord/repos" + if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" - class Issues(commands.Cog): """Cog that allows users to retrieve issues from GitHub.""" def __init__(self, bot: commands.Bot): self.bot = bot + self.repos = [] + self.get_pydis_repos.start() + + @tasks.loop(minutes=30) + async def get_pydis_repos(self) -> None: + async with self.bot.http_session.get(PYDIS_REPOS) as resp: + if resp.status == 200: + data = await resp.json() + for repo in data: + self.repos.append(repo["full_name"].split("/")[1]) + else: + log.debug(f"Failed to get latest Pydis repositories. Status code {resp.status}") + + @staticmethod + def check_in_block(message: discord.Message, repo_issue: str) -> bool: + block = ( + re.findall(r"```([\s\S]*)?```", message.content) + or re.findall(r"```*\n([\s\S]*)?\n```", message.content) + or re.findall(r"```*([\s\S]*)?\n```", message.content) + or re.findall(r"```*\n([\s\S]*)?```", message.content) + or re.findall(r"`([\s\S]*)?`", message.content) + ) + print(block) + + if "#".join(repo_issue.split(" ")) in "".join([*block]): + return True + return False async def fetch_issues( self, @@ -44,7 +72,7 @@ class Issues(commands.Cog): for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" - + print(url) log.trace(f"Querying GH issues API: {url}") async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: json_data = await r.json() @@ -104,7 +132,6 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - print(numbers) result = await self.fetch_issues(set(numbers), repository, user) if result == "Numbers not found.": @@ -129,14 +156,19 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - message_repo_issue_map = re.findall(r".+?(bot|meta|sir-lancebot|logcord)#(\d+)", message.content) + repo_regex = "|".join(repo for repo in self.repos) + message_repo_issue_map = re.findall(fr".+?({repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: for repo_issue in message_repo_issue_map: - result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") - if isinstance(result, list): - links.append(*result) + if self.check_in_block(message, " ".join([*repo_issue])): + print("in") + continue + else: + result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") + if isinstance(result, list): + links.append(*result) if not links: return -- cgit v1.2.3 From 2dcdec0defb0f75478cd344cabd431d02b506741 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 16:36:24 +0530 Subject: Remove debug code and add docstrings. --- bot/exts/evergreen/issues.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 1924d822..ba8a70cf 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -22,6 +22,7 @@ PYDIS_REPOS = "https://api.github.com/orgs/python-discord/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" + class Issues(commands.Cog): """Cog that allows users to retrieve issues from GitHub.""" @@ -32,6 +33,7 @@ class Issues(commands.Cog): @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: + """Get all python-discord repositories on github.""" async with self.bot.http_session.get(PYDIS_REPOS) as resp: if resp.status == 200: data = await resp.json() @@ -42,6 +44,7 @@ class Issues(commands.Cog): @staticmethod def check_in_block(message: discord.Message, repo_issue: str) -> bool: + """Check whether the # is in codeblocks.""" block = ( re.findall(r"```([\s\S]*)?```", message.content) or re.findall(r"```*\n([\s\S]*)?\n```", message.content) @@ -49,7 +52,6 @@ class Issues(commands.Cog): or re.findall(r"```*\n([\s\S]*)?```", message.content) or re.findall(r"`([\s\S]*)?`", message.content) ) - print(block) if "#".join(repo_issue.split(" ")) in "".join([*block]): return True @@ -163,7 +165,6 @@ class Issues(commands.Cog): if message_repo_issue_map: for repo_issue in message_repo_issue_map: if self.check_in_block(message, " ".join([*repo_issue])): - print("in") continue else: result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From d80308f509489bfe6120020758721093af2df7f9 Mon Sep 17 00:00:00 2001 From: Shivansh-007 <69356296+Shivansh-007@users.noreply.github.com> Date: Tue, 26 Jan 2021 16:39:02 +0530 Subject: Remove debug code. Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/issues.py | 1 - 1 file changed, 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index ba8a70cf..946da354 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -74,7 +74,6 @@ class Issues(commands.Cog): for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" - print(url) log.trace(f"Querying GH issues API: {url}") async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: json_data = await r.json() -- cgit v1.2.3 From ec45aac56861f3b7b8813957bf7e0e3a890fc44a Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 16:54:30 +0530 Subject: Cache repo regex --- bot/exts/evergreen/issues.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index ba8a70cf..55bc8cf0 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -39,6 +39,7 @@ class Issues(commands.Cog): data = await resp.json() for repo in data: self.repos.append(repo["full_name"].split("/")[1]) + self.repo_regex = "|".join(repo for repo in self.repos) else: log.debug(f"Failed to get latest Pydis repositories. Status code {resp.status}") @@ -158,8 +159,7 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - repo_regex = "|".join(repo for repo in self.repos) - message_repo_issue_map = re.findall(fr".+?({repo_regex})#(\d+)", message.content) + message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: -- cgit v1.2.3 From 65e5558df35892c2136f9182d07307b496582019 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 16:56:18 +0530 Subject: Use list.extend while appending issue links --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 55bc8cf0..724be737 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -169,7 +169,7 @@ class Issues(commands.Cog): else: result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") if isinstance(result, list): - links.append(*result) + links.extend(result) if not links: return -- cgit v1.2.3 From d973c1266b93a5baeb32a2f04ed797b8d0f0cd1b Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 17:07:48 +0530 Subject: Change PYDIS_REPOS to PYTHON_DISCORD_REPOS --- bot/exts/evergreen/issues.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 724be737..95187551 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -17,7 +17,7 @@ BAD_RESPONSE = { } MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -PYDIS_REPOS = "https://api.github.com/orgs/python-discord/repos" +PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/python-discord/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -34,7 +34,7 @@ class Issues(commands.Cog): @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: """Get all python-discord repositories on github.""" - async with self.bot.http_session.get(PYDIS_REPOS) as resp: + async with self.bot.http_session.get(PYTHON_DISCORD_REPOS) as resp: if resp.status == 200: data = await resp.json() for repo in data: @@ -155,8 +155,9 @@ class Issues(commands.Cog): elif isinstance(result, str): await ctx.send(result) - + @commands.Cog.listener() + @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) -- cgit v1.2.3 From 24f915bbe94452f80c7ab57b90b0f48d09e07e63 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 17:13:12 +0530 Subject: Make the python_discord_repos' orgranisation name configurable --- bot/exts/evergreen/issues.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 95187551..d816770c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -17,7 +17,7 @@ BAD_RESPONSE = { } MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/python-discord/repos" +PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/{repo}/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -34,7 +34,7 @@ class Issues(commands.Cog): @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: """Get all python-discord repositories on github.""" - async with self.bot.http_session.get(PYTHON_DISCORD_REPOS) as resp: + async with self.bot.http_session.get(PYTHON_DISCORD_REPOS.format(repo="python-discord")) as resp: if resp.status == 200: data = await resp.json() for repo in data: @@ -155,7 +155,7 @@ class Issues(commands.Cog): elif isinstance(result, str): await ctx.send(result) - + @commands.Cog.listener() @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) async def on_message(self, message: discord.Message) -> None: -- cgit v1.2.3 From b6cda1a14a2a2e69dac59de675cbea75638c3f64 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 17:23:30 +0530 Subject: Use enums for fetch issue errors. --- bot/exts/evergreen/issues.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d816770c..bd62fc22 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -2,6 +2,7 @@ import logging import random import re import typing as t +from enum import Enum import discord from discord.ext import commands, tasks @@ -23,6 +24,12 @@ if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" +class FetchIssueErrors(Enum): + value_error = "Numbers not found." + max_requests = "Max requests hit." + + + class Issues(commands.Cog): """Cog that allows users to retrieve issues from GitHub.""" @@ -67,10 +74,10 @@ class Issues(commands.Cog): """Retrieve issue(s) from a GitHub repository.""" links = [] if not numbers: - return "Numbers not found." + return FetchIssueErrors.value_error if len(numbers) > MAX_REQUESTS: - return "Max requests hit." + return FetchIssueErrors.max_requests for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" @@ -137,10 +144,10 @@ class Issues(commands.Cog): """Command to retrieve issue(s) from a GitHub repository.""" result = await self.fetch_issues(set(numbers), repository, user) - if result == "Numbers not found.": + if result == FetchIssueErrors.value_error: await ctx.invoke(self.bot.get_command('help'), 'issue') - elif result == "Max requests hit.": + elif result == FetchIssueErrors.max_requests: embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, -- cgit v1.2.3 From 9d5352fcf1c4b9d09d9aba9632689f54beeb4087 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 17:24:26 +0530 Subject: Fix lint issues --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bd62fc22..d258cc3c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -2,7 +2,7 @@ import logging import random import re import typing as t -from enum import Enum +from enum import Enum import discord from discord.ext import commands, tasks -- cgit v1.2.3 From 8b71066b6dd645918cdceef389eb98d334c62434 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Tue, 26 Jan 2021 17:27:32 +0530 Subject: Add docstring to FetchIssueErrors and remove extra new line --- bot/exts/evergreen/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d6790edf..c9f87957 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -25,11 +25,12 @@ if GITHUB_TOKEN := Tokens.github: class FetchIssueErrors(Enum): + """Errors returned in fetch issues.""" + value_error = "Numbers not found." max_requests = "Max requests hit." - class Issues(commands.Cog): """Cog that allows users to retrieve issues from GitHub.""" -- cgit v1.2.3 From d2f9aa0d2236426e88334483959938328950f357 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 27 Jan 2021 10:09:05 +0530 Subject: Improve code block regex --- bot/exts/evergreen/issues.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index c9f87957..d2c70d4b 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -23,6 +23,12 @@ PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/{repo}/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" +CODE_BLOCK_RE = re.compile( + r"^`([^`\n]+)`" # Inline codeblock + r"|```(.+?)```", # Multiline codeblock + re.DOTALL | re.MULTILINE +) + class FetchIssueErrors(Enum): """Errors returned in fetch issues.""" @@ -54,15 +60,11 @@ class Issues(commands.Cog): @staticmethod def check_in_block(message: discord.Message, repo_issue: str) -> bool: """Check whether the # is in codeblocks.""" - block = ( - re.findall(r"```([\s\S]*)?```", message.content) - or re.findall(r"```*\n([\s\S]*)?\n```", message.content) - or re.findall(r"```*([\s\S]*)?\n```", message.content) - or re.findall(r"```*\n([\s\S]*)?```", message.content) - or re.findall(r"`([\s\S]*)?`", message.content) - ) + block = re.findall(CODE_BLOCK_RE, message.content) - if "#".join(repo_issue.split(" ")) in "".join([*block]): + if not block: + return False + elif "#".join(repo_issue.split(" ")) in "".join([*block[0]]): return True return False -- cgit v1.2.3 From a7ffa0926b47bf40cd034eeb2b5e9d5dcd04798f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 27 Jan 2021 10:43:19 +0530 Subject: Add check for in category and in remove overide_channel check and do it without decorator --- bot/constants.py | 8 ++++++++ bot/exts/evergreen/issues.py | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/constants.py b/bot/constants.py index 1d41a53e..ce1ca29a 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -9,6 +9,7 @@ __all__ = ( "AdventOfCode", "Branding", "Channels", + "Categories", "Client", "Colours", "Emojis", @@ -100,6 +101,7 @@ class Channels(NamedTuple): big_brother_logs = 468507907357409333 bot = 267659945086812160 checkpoint_test = 422077681434099723 + organisation = 551789653284356126 devalerts = 460181980097675264 devlog = int(environ.get("CHANNEL_DEVLOG", 622895325144940554)) dev_contrib = 635950537262759947 @@ -128,6 +130,12 @@ class Channels(NamedTuple): voice_chat_1 = 799647045886541885 +class Categories(NamedTuple): + development = 411199786025484308 + devprojects = 787641585624940544 + media = 799054581991997460 + + class Client(NamedTuple): name = "Sir Lancebot" guild = int(environ.get("BOT_GUILD", 267624335836053506)) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d2c70d4b..4ab3df2c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,8 +7,7 @@ from enum import Enum import discord from discord.ext import commands, tasks -from bot.constants import Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS -from bot.utils.decorators import override_in_channel +from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS log = logging.getLogger(__name__) @@ -16,13 +15,19 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } + MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/{repo}/repos" +PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/{repo}/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" +WHITELISTED_CATEGORIES = ( + Categories.devprojects, Categories.media, Categories.development +) +WHITELISTED_CHANNELS += (Channels.organisation) + CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock r"|```(.+?)```", # Multiline codeblock @@ -135,7 +140,6 @@ class Issues(commands.Cog): return resp @commands.command(aliases=("pr",)) - @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) async def issue( self, ctx: commands.Context, @@ -144,6 +148,9 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" + if ctx.channel.category not in WHITELISTED_CATEGORIES or ctx.channel.category in WHITELISTED_CHANNELS: + return + result = await self.fetch_issues(set(numbers), repository, user) if result == FetchIssueErrors.value_error: @@ -166,9 +173,11 @@ class Issues(commands.Cog): await ctx.send(result) @commands.Cog.listener() - @override_in_channel(WHITELISTED_CHANNELS + (Channels.dev_contrib, Channels.dev_branding)) async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" + if message.channel.category not in WHITELISTED_CATEGORIES or message.channel.category in WHITELISTED_CHANNELS: + return + message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) links = [] -- cgit v1.2.3 From 65260f66e6e7d3a9bff8b19c4a498643d2e664aa Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Wed, 27 Jan 2021 16:58:53 +0530 Subject: Fix return type annotations --- bot/exts/evergreen/issues.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4ab3df2c..8b02e874 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -26,7 +26,7 @@ if GITHUB_TOKEN := Tokens.github: WHITELISTED_CATEGORIES = ( Categories.devprojects, Categories.media, Categories.development ) -WHITELISTED_CHANNELS += (Channels.organisation) +WHITELISTED_CHANNELS += Channels.organisation CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock @@ -78,7 +78,7 @@ class Issues(commands.Cog): numbers: set, repository: str, user: str - ) -> t.Union[str, list]: + ) -> t.Union[FetchIssueErrors, str, list]: """Retrieve issue(s) from a GitHub repository.""" links = [] if not numbers: -- cgit v1.2.3 From 0ae98d0d79c62cb71cc8287c0b391eef14f284ba Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 30 Jan 2021 06:50:31 +0530 Subject: Fix channel and category check logic --- bot/exts/evergreen/issues.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 8b02e874..55ded054 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -26,7 +26,7 @@ if GITHUB_TOKEN := Tokens.github: WHITELISTED_CATEGORIES = ( Categories.devprojects, Categories.media, Categories.development ) -WHITELISTED_CHANNELS += Channels.organisation +WHITELISTED_CHANNELS_ON_MESSAGE = (Channels.organisation,) CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock @@ -58,7 +58,7 @@ class Issues(commands.Cog): data = await resp.json() for repo in data: self.repos.append(repo["full_name"].split("/")[1]) - self.repo_regex = "|".join(repo for repo in self.repos) + self.repo_regex = "|".join(self.repos) else: log.debug(f"Failed to get latest Pydis repositories. Status code {resp.status}") @@ -148,7 +148,10 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - if ctx.channel.category not in WHITELISTED_CATEGORIES or ctx.channel.category in WHITELISTED_CHANNELS: + if not( + ctx.channel.category.id in WHITELISTED_CATEGORIES + or ctx.channel.id in WHITELISTED_CHANNELS + ): return result = await self.fetch_issues(set(numbers), repository, user) @@ -175,7 +178,10 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if message.channel.category not in WHITELISTED_CATEGORIES or message.channel.category in WHITELISTED_CHANNELS: + if not( + message.channel.category.id in WHITELISTED_CATEGORIES + or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE + ): return message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) @@ -183,9 +189,7 @@ class Issues(commands.Cog): if message_repo_issue_map: for repo_issue in message_repo_issue_map: - if self.check_in_block(message, " ".join([*repo_issue])): - continue - else: + if not self.check_in_block(message, " ".join([*repo_issue])): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") if isinstance(result, list): links.extend(result) -- cgit v1.2.3 From ac44307a4d4c7aabfafee0af89630a0f748fee00 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 30 Jan 2021 17:19:22 +0530 Subject: Fix PYTHON_DISCORD_REPOS format variable name and give it a better name --- bot/exts/evergreen/issues.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 55ded054..72d88d04 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -19,7 +19,7 @@ BAD_RESPONSE = { MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -PYTHON_DISCORD_REPOS = "https://api.github.com/orgs/{repo}/repos" +REPOS_API = "https://api.github.com/orgs/{org}/repos" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -53,7 +53,7 @@ class Issues(commands.Cog): @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: """Get all python-discord repositories on github.""" - async with self.bot.http_session.get(PYTHON_DISCORD_REPOS.format(repo="python-discord")) as resp: + async with self.bot.http_session.get(REPOS_API.format(org="python-discord")) as resp: if resp.status == 200: data = await resp.json() for repo in data: -- cgit v1.2.3 From 45af7ff6803e75d70be8d78e232b3d30803ae65d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 31 Jan 2021 05:13:35 +0530 Subject: Add mod_meta and mod_tools channels to whitelisted --- bot/constants.py | 2 ++ bot/exts/evergreen/issues.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/constants.py b/bot/constants.py index ce1ca29a..1234ef3b 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -116,6 +116,8 @@ class Channels(NamedTuple): message_log = 467752170159079424 mod_alerts = 473092532147060736 modlog = 282638479504965634 + mod_meta = 775412552795947058 + mod_tools = 775413915391098921 off_topic_0 = 291284109232308226 off_topic_1 = 463035241142026251 off_topic_2 = 463035268514185226 diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 72d88d04..f24e0717 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -26,7 +26,7 @@ if GITHUB_TOKEN := Tokens.github: WHITELISTED_CATEGORIES = ( Categories.devprojects, Categories.media, Categories.development ) -WHITELISTED_CHANNELS_ON_MESSAGE = (Channels.organisation,) +WHITELISTED_CHANNELS_ON_MESSAGE = (Channels.organisation, Channels.mod_meta, Channels.mod_tools) CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock -- cgit v1.2.3 From 8a92054bab4c3dbd0e42d633d11a87461e1189e1 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Mon, 1 Feb 2021 05:31:48 +0530 Subject: Remove brackets from .join --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index f24e0717..72ca6de4 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -189,7 +189,7 @@ class Issues(commands.Cog): if message_repo_issue_map: for repo_issue in message_repo_issue_map: - if not self.check_in_block(message, " ".join([*repo_issue])): + if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") if isinstance(result, list): links.extend(result) -- cgit v1.2.3 From 4239b1d07cd0bba543aca8e7c77cee1d8f437b14 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com> Date: Mon, 1 Feb 2021 11:47:52 +0300 Subject: Fixes Issue Matching Regex Changes the automatic issue matching regex to make it work for repos at the very beginning of messages. --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 72ca6de4..73ebe547 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -184,7 +184,7 @@ class Issues(commands.Cog): ): return - message_repo_issue_map = re.findall(fr".+?({self.repo_regex})#(\d+)", message.content) + message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] if message_repo_issue_map: -- cgit v1.2.3 From 0d5da9e1304865034e8a2349d33b132e149ad890 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 5 Feb 2021 19:17:59 +0000 Subject: First pass of easy to produce errors --- bot/exts/easter/easter_riddle.py | 13 ++++++++++++- bot/exts/evergreen/issues.py | 7 ++++++- bot/exts/evergreen/wolfram.py | 3 ++- bot/exts/halloween/candy_collection.py | 3 +++ 4 files changed, 23 insertions(+), 3 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 3c612eb1..a93b3745 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -7,7 +7,7 @@ from pathlib import Path import discord from discord.ext import commands -from bot.constants import Colours +from bot.constants import Colours, NEGATIVE_REPLIES log = logging.getLogger(__name__) @@ -36,6 +36,17 @@ class EasterRiddle(commands.Cog): if self.current_channel: return await ctx.send(f"A riddle is already being solved in {self.current_channel.mention}!") + # Don't let users start in a DM + if not ctx.guild: + await ctx.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description="You can't start riddles in DMs", + colour=discord.Colour.red() + ) + ) + return + self.current_channel = ctx.message.channel random_question = random.choice(RIDDLE_QUESTIONS) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 73ebe547..1f22f287 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -178,7 +178,12 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not( + # Ignore messages from DMs + if not message.guild: + return + + # Ignore messages not in whitelisted categories / channels + if not ( message.channel.category.id in WHITELISTED_CATEGORIES or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE ): diff --git a/bot/exts/evergreen/wolfram.py b/bot/exts/evergreen/wolfram.py index 898e8d2a..077a99f5 100644 --- a/bot/exts/evergreen/wolfram.py +++ b/bot/exts/evergreen/wolfram.py @@ -62,7 +62,8 @@ def custom_cooldown(*ignore: List[int]) -> Callable: # if the invoked command is help we don't want to increase the ratelimits since it's not actually # invoking the command/making a request, so instead just check if the user/guild are on cooldown. guild_cooldown = not guildcd.get_bucket(ctx.message).get_tokens() == 0 # if guild is on cooldown - if not any(r.id in ignore for r in ctx.author.roles): # check user bucket if user is not ignored + # check the message is in a guild, and check user bucket if user is not ignored + if ctx.guild and not any(r.id in ignore for r in ctx.author.roles): return guild_cooldown and not usercd.get_bucket(ctx.message).get_tokens() == 0 return guild_cooldown diff --git a/bot/exts/halloween/candy_collection.py b/bot/exts/halloween/candy_collection.py index 0cb37ecd..40e21f40 100644 --- a/bot/exts/halloween/candy_collection.py +++ b/bot/exts/halloween/candy_collection.py @@ -47,6 +47,9 @@ class CandyCollection(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Randomly adds candy or skull reaction to non-bot messages in the Event channel.""" + # Ignore messages in DMs + if not message.guild: + return # make sure its a human message if message.author.bot: return -- cgit v1.2.3 From f429ce6aaa57d680893be0cbd5a7760de947aac6 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 28 Feb 2021 19:48:13 +0000 Subject: Add issue cog to staff channels. --- bot/constants.py | 2 ++ bot/exts/evergreen/issues.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/constants.py b/bot/constants.py index 682ccf6f..9b7e37f8 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -124,6 +124,7 @@ class Channels(NamedTuple): hacktoberfest_2020 = 760857070781071431 voice_chat_0 = 412357430186344448 voice_chat_1 = 799647045886541885 + staff_voice = 541638762007101470 class Categories(NamedTuple): @@ -131,6 +132,7 @@ class Categories(NamedTuple): development = 411199786025484308 devprojects = 787641585624940544 media = 799054581991997460 + staff = 364918151625965579 class Client(NamedTuple): diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 73ebe547..03d0a86b 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -24,9 +24,11 @@ if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" WHITELISTED_CATEGORIES = ( - Categories.devprojects, Categories.media, Categories.development + Categories.devprojects, Categories.media, Categories.development, Categories.staff +) +WHITELISTED_CHANNELS_ON_MESSAGE = ( + Channels.organisation, Channels.mod_meta, Channels.mod_tools, Channels.staff_voice ) -WHITELISTED_CHANNELS_ON_MESSAGE = (Channels.organisation, Channels.mod_meta, Channels.mod_tools) CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock -- cgit v1.2.3 From b61aa3079d7d1bcaf4e5e6754544143ce1a156a8 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 28 Feb 2021 20:13:08 +0000 Subject: Fix ordering of categories in issues cog --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 03d0a86b..bbcbf611 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -24,7 +24,7 @@ if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" WHITELISTED_CATEGORIES = ( - Categories.devprojects, Categories.media, Categories.development, Categories.staff + Categories.development, Categories.devprojects, Categories.media, Categories.staff ) WHITELISTED_CHANNELS_ON_MESSAGE = ( Channels.organisation, Channels.mod_meta, Channels.mod_tools, Channels.staff_voice -- cgit v1.2.3 From dfe0e6001d4c1886e29b20eafacf4de1cc3a938b Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 27 Mar 2021 18:17:26 +0100 Subject: Allow automatic linking of issues everywhere We have been limiting the channels where automatic linking issues can be used in (See #566). Since we haven't seen any potential issue with it, we can allow it everywhere to avoid updating it every two days or because of missing channels. --- bot/exts/evergreen/issues.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bbcbf611..6ca0c3c9 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -180,12 +180,6 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not( - message.channel.category.id in WHITELISTED_CATEGORIES - or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE - ): - return - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] -- cgit v1.2.3 From d03a41dfbeb3653fbc96ae4c6e3160fe581b1d16 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sat, 27 Mar 2021 18:33:36 +0100 Subject: Remove unused WHITELISTED_CHANNELS_ON_MESSAGE constant --- bot/exts/evergreen/issues.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 6ca0c3c9..3d23b869 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,7 +7,7 @@ from enum import Enum import discord from discord.ext import commands, tasks -from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.constants import Categories, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS log = logging.getLogger(__name__) @@ -26,9 +26,6 @@ if GITHUB_TOKEN := Tokens.github: WHITELISTED_CATEGORIES = ( Categories.development, Categories.devprojects, Categories.media, Categories.staff ) -WHITELISTED_CHANNELS_ON_MESSAGE = ( - Channels.organisation, Channels.mod_meta, Channels.mod_tools, Channels.staff_voice -) CODE_BLOCK_RE = re.compile( r"^`([^`\n]+)`" # Inline codeblock -- cgit v1.2.3 From e3b7b3bc5b56b805112558377e25e2537cc86083 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 2 Apr 2021 14:53:27 +0100 Subject: Don't allow users to run the issue command in DMs, given error feedback. --- bot/exts/evergreen/issues.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 1f22f287..0f0be33a 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -7,7 +7,16 @@ from enum import Enum import discord from discord.ext import commands, tasks -from bot.constants import Categories, Channels, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.constants import ( + Categories, + Channels, + Colours, + ERROR_REPLIES, + Emojis, + NEGATIVE_REPLIES, + Tokens, + WHITELISTED_CHANNELS +) log = logging.getLogger(__name__) @@ -148,11 +157,20 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - if not( + if not ctx.guild or not( ctx.channel.category.id in WHITELISTED_CATEGORIES or ctx.channel.id in WHITELISTED_CHANNELS ): - return + await ctx.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description=( + "You can't run this command in this channel. " + f"Try again in {Channels.community_bot_commands}" + ), + colour=discord.Colour.red() + ) + ) result = await self.fetch_issues(set(numbers), repository, user) -- cgit v1.2.3 From 6a8a582c49c2a3d97a2a061eca157502acb0618d Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 2 Apr 2021 17:02:47 +0100 Subject: Send error embed when a user tries to retrieve an issue in DMs --- bot/exts/evergreen/issues.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index ca0e4494..4a73d20b 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -168,11 +168,12 @@ class Issues(commands.Cog): title=random.choice(NEGATIVE_REPLIES), description=( "You can't run this command in this channel. " - f"Try again in {Channels.community_bot_commands}" + f"Try again in <#{Channels.community_bot_commands}>" ), colour=discord.Colour.red() ) ) + return result = await self.fetch_issues(set(numbers), repository, user) @@ -198,12 +199,8 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - # Ignore messages from DMs - if not message.guild: - return - - # Ignore messages not in whitelisted categories / channels - if not ( + # Ignore messages not in whitelisted categories / channels, only when in guild. + if message.guild and not ( message.channel.category.id in WHITELISTED_CATEGORIES or message.channel.id in WHITELISTED_CHANNELS_ON_MESSAGE ): @@ -213,6 +210,18 @@ class Issues(commands.Cog): links = [] if message_repo_issue_map: + if not message.guild: + await message.channel.send( + embed=discord.Embed( + title=random.choice(NEGATIVE_REPLIES), + description=( + "You can't retreive issues from DMs. " + f"Try again in <#{Channels.community_bot_commands}>" + ), + colour=discord.Colour.red() + ) + ) + return for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 9edeff2eea5df363446e98d2905283562849619b Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 5 Apr 2021 16:08:24 +0200 Subject: Issues: remove duplicates --- bot/exts/evergreen/issues.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 3d23b869..d7e29a90 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -181,6 +181,9 @@ class Issues(commands.Cog): links = [] if message_repo_issue_map: + # Remove duplicates + message_repo_issue_map = set(message_repo_issue_map) + for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 0e2422f7caaec73fbee9708c507d9be23635dd78 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 5 Apr 2021 16:43:28 +0200 Subject: Issues: limit results to 5 --- bot/exts/evergreen/issues.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d7e29a90..afaa7012 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -15,8 +15,6 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } - -MAX_REQUESTS = 10 REQUEST_HEADERS = dict() REPOS_API = "https://api.github.com/orgs/{org}/repos" @@ -33,6 +31,9 @@ CODE_BLOCK_RE = re.compile( re.DOTALL | re.MULTILINE ) +# Maximum number of issues in one message +MAXIMUM_ISSUES = 5 + class FetchIssueErrors(Enum): """Errors returned in fetch issues.""" @@ -83,7 +84,7 @@ class Issues(commands.Cog): if not numbers: return FetchIssueErrors.value_error - if len(numbers) > MAX_REQUESTS: + if len(numbers) > MAXIMUM_ISSUES: return FetchIssueErrors.max_requests for number in numbers: @@ -162,7 +163,7 @@ class Issues(commands.Cog): embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, - description=f"Too many issues/PRs! (maximum of {MAX_REQUESTS})" + description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) @@ -184,6 +185,15 @@ class Issues(commands.Cog): # Remove duplicates message_repo_issue_map = set(message_repo_issue_map) + if len(message_repo_issue_map) > MAXIMUM_ISSUES: + embed = discord.Embed( + title=random.choice(ERROR_REPLIES), + color=Colours.soft_red, + description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" + ) + await message.channel.send(embed=embed) + return + for repo_issue in message_repo_issue_map: if not self.check_in_block(message, " ".join(repo_issue)): result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") -- cgit v1.2.3 From 08b1348b33d2d865987f2677a68ba62645db2abe Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 6 Apr 2021 10:05:40 +0200 Subject: Rewrite issue cog - Switch to a dataclass based communication - Use GitHub headers when querying the repo_regex - Properly handle non-200 return codes - Use @whitelist_override --- bot/exts/evergreen/issues.py | 205 +++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 86 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index afaa7012..dce11678 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -2,12 +2,13 @@ import logging import random import re import typing as t -from enum import Enum +from dataclasses import dataclass import discord from discord.ext import commands, tasks from bot.constants import Categories, Colours, ERROR_REPLIES, Emojis, Tokens, WHITELISTED_CHANNELS +from bot.utils.decorators import whitelist_override log = logging.getLogger(__name__) @@ -17,7 +18,10 @@ BAD_RESPONSE = { } REQUEST_HEADERS = dict() -REPOS_API = "https://api.github.com/orgs/{org}/repos" +REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos" +ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" +PR_MERGE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" + if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" @@ -35,11 +39,23 @@ CODE_BLOCK_RE = re.compile( MAXIMUM_ISSUES = 5 -class FetchIssueErrors(Enum): - """Errors returned in fetch issues.""" +@dataclass +class FetchError: + """Dataclass representing an error while fetching an issue.""" + + return_code: int + message: str + - value_error = "Numbers not found." - max_requests = "Max requests hit." +@dataclass +class IssueState: + """Dataclass representing the state of an issue.""" + + repository: str + number: int + url: str + title: str + icon_url: str class Issues(commands.Cog): @@ -48,19 +64,27 @@ class Issues(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.repos = [] + self.repo_regex = None self.get_pydis_repos.start() @tasks.loop(minutes=30) async def get_pydis_repos(self) -> None: - """Get all python-discord repositories on github.""" - async with self.bot.http_session.get(REPOS_API.format(org="python-discord")) as resp: + """ + Get all python-discord repositories on github. + + This task will update a pipe-separated list of repositories in self.repo_regex. + """ + async with self.bot.http_session.get( + REPOSITORY_ENDPOINT.format(org="python-discord"), + headers=REQUEST_HEADERS + ) as resp: if resp.status == 200: data = await resp.json() for repo in data: self.repos.append(repo["full_name"].split("/")[1]) self.repo_regex = "|".join(self.repos) else: - log.debug(f"Failed to get latest Pydis repositories. Status code {resp.status}") + log.warning(f"Failed to get latest Pydis repositories. Status code {resp.status}") @staticmethod def check_in_block(message: discord.Message, repo_issue: str) -> bool: @@ -75,70 +99,85 @@ class Issues(commands.Cog): async def fetch_issues( self, - numbers: set, + number: int, repository: str, user: str - ) -> t.Union[FetchIssueErrors, str, list]: - """Retrieve issue(s) from a GitHub repository.""" - links = [] - if not numbers: - return FetchIssueErrors.value_error - - if len(numbers) > MAXIMUM_ISSUES: - return FetchIssueErrors.max_requests - - for number in numbers: - url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" - merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" - log.trace(f"Querying GH issues API: {url}") - async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: - json_data = await r.json() - - if r.status in BAD_RESPONSE: - log.warning(f"Received response {r.status} from: {url}") - return f"[{str(r.status)}] #{number} {BAD_RESPONSE.get(r.status)}" - - # The initial API request is made to the issues API endpoint, which will return information - # if the issue or PR is present. However, the scope of information returned for PRs differs - # from issues: if the 'issues' key is present in the response then we can pull the data we - # need from the initial API call. - if "issues" in json_data.get("html_url"): + ) -> t.Union[IssueState, FetchError]: + """ + Retrieve an issue from a GitHub repository. + + returns IssueState on success, FetchError on failure. + """ + url = ISSUE_ENDPOINT.format(user=user, repository=repository, number=number) + merge_url = PR_MERGE_ENDPOINT.format(user=user, repository=repository, number=number) + log.trace(f"Querying GH issues API: {url}") + + async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: + json_data = await r.json() + + if r.status == 403: + if "X-RateLimit-Remaining" in r.headers and r.headers["X-RateLimit-Remaining"] == "0": + log.info(f"Ratelimit reached while fetching {url}") + return FetchError(403, "Ratelimit reached, please retry in a few minutes.") + return FetchError(403, "Cannot access issue.") + elif r.status in (404, 410): + return FetchError(r.status, "Issue not found.") + elif r.status != 200: + return FetchError(r.status, "Error while fetching issue.") + + # The initial API request is made to the issues API endpoint, which will return information + # if the issue or PR is present. However, the scope of information returned for PRs differs + # from issues: if the 'issues' key is present in the response then we can pull the data we + # need from the initial API call. + if "issues" in json_data.get("html_url"): + if json_data.get("state") == "open": + icon_url = Emojis.issue + else: + icon_url = Emojis.issue_closed + + # If the 'issues' key is not contained in the API response and there is no error code, then + # we know that a PR has been requested and a call to the pulls API endpoint is necessary + # to get the desired information for the PR. + else: + log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") + async with self.bot.http_session.get(merge_url) as m: if json_data.get("state") == "open": - icon_url = Emojis.issue + icon_url = Emojis.pull_request + # When the status is 204 this means that the state of the PR is merged + elif m.status == 204: + icon_url = Emojis.merge else: - icon_url = Emojis.issue_closed - - # If the 'issues' key is not contained in the API response and there is no error code, then - # we know that a PR has been requested and a call to the pulls API endpoint is necessary - # to get the desired information for the PR. - else: - log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") - async with self.bot.http_session.get(merge_url) as m: - if json_data.get("state") == "open": - icon_url = Emojis.pull_request - # When the status is 204 this means that the state of the PR is merged - elif m.status == 204: - icon_url = Emojis.merge - else: - icon_url = Emojis.pull_request_closed + icon_url = Emojis.pull_request_closed - issue_url = json_data.get("html_url") - links.append([icon_url, f"[{repository}] #{number} {json_data.get('title')}", issue_url]) + issue_url = json_data.get("html_url") - return links + return IssueState(repository, number, issue_url, json_data.get('title', ''), icon_url) @staticmethod - def get_embed(result: list, user: str = "python-discord", repository: str = "") -> discord.Embed: - """Get Response Embed.""" - description_list = ["{0} [{1}]({2})".format(*link) for link in result] + def format_embed( + results: t.List[t.Union[IssueState, FetchError]], + user: str, + repository: t.Optional[str] = None + ) -> discord.Embed: + """Take a list of IssueState or FetchError and format a Discord embed for them.""" + description_list = [] + + for result in results: + if isinstance(result, IssueState): + description_list.append(f"{result.icon_url} [{result.title}]({result.url})") + elif isinstance(result, FetchError): + description_list.append(f"[{result.return_code}] {result.message}") + resp = discord.Embed( colour=Colours.bright_green, description='\n'.join(description_list) ) - resp.set_author(name="GitHub", url=f"https://github.com/{user}/{repository}") + embed_url = f"https://github.com/{user}/{repository}" if repository else f"https://github.com/{user}" + resp.set_author(name="GitHub", url=embed_url) return resp + @whitelist_override(channels=WHITELISTED_CHANNELS, categories=WHITELISTED_CATEGORIES) @commands.command(aliases=("pr",)) async def issue( self, @@ -148,62 +187,56 @@ class Issues(commands.Cog): user: str = "python-discord" ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" - if not( - ctx.channel.category.id in WHITELISTED_CATEGORIES - or ctx.channel.id in WHITELISTED_CHANNELS - ): - return - - result = await self.fetch_issues(set(numbers), repository, user) - - if result == FetchIssueErrors.value_error: - await ctx.invoke(self.bot.get_command('help'), 'issue') + # Remove duplicates + numbers = set(numbers) - elif result == FetchIssueErrors.max_requests: + if len(numbers) > MAXIMUM_ISSUES: embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) + await ctx.invoke(self.bot.get_command('help'), 'issue') - elif isinstance(result, list): - # Issue/PR format: emoji to show if open/closed/merged, number and the title as a singular link. - resp = self.get_embed(result, user, repository) - await ctx.send(embed=resp) - - elif isinstance(result, str): - await ctx.send(result) + results = [await self.fetch_issues(number, repository, user) for number in numbers] + await ctx.send(embed=self.format_embed(results)) @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - message_repo_issue_map = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + if not self.repo_regex: + log.warning("repo_regex isn't ready, cannot look for issues.") + return + + # `issues` will hold a list of two element tuples `(repository, issue_number)` + issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] - if message_repo_issue_map: + if issues: + log.trace(f"Found {issues = }") # Remove duplicates - message_repo_issue_map = set(message_repo_issue_map) + issues = set(issues) - if len(message_repo_issue_map) > MAXIMUM_ISSUES: + if len(issues) > MAXIMUM_ISSUES: embed = discord.Embed( title=random.choice(ERROR_REPLIES), color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) - await message.channel.send(embed=embed) + await message.channel.send(embed=embed).delete(delay=5) return - for repo_issue in message_repo_issue_map: + for repo_issue in issues: if not self.check_in_block(message, " ".join(repo_issue)): - result = await self.fetch_issues({repo_issue[1]}, repo_issue[0], "python-discord") - if isinstance(result, list): - links.extend(result) + result = await self.fetch_issues(repo_issue[1], repo_issue[0], "python-discord") + if isinstance(result, IssueState): + links.append(result) if not links: return - resp = self.get_embed(links, "python-discord") + resp = self.format_embed(links, "python-discord") await message.channel.send(embed=resp) -- cgit v1.2.3 From f8e14d089e87cc2628a9087a60bf0c3cc6eefc39 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Tue, 6 Apr 2021 10:13:31 +0200 Subject: Issues: ignore bots --- bot/exts/evergreen/issues.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index dce11678..7e9defbe 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -209,6 +209,10 @@ class Issues(commands.Cog): log.warning("repo_regex isn't ready, cannot look for issues.") return + # Ignore bots + if message.author.bot: + return + # `issues` will hold a list of two element tuples `(repository, issue_number)` issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) links = [] -- cgit v1.2.3 From fd2ff1d06e31a9b0f412c2a81e373bf46fe192f7 Mon Sep 17 00:00:00 2001 From: vcokltfre Date: Thu, 8 Apr 2021 03:15:47 +0100 Subject: increase the number of repos per page we fetch from github --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4a73d20b..58f9d7aa 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -28,7 +28,7 @@ BAD_RESPONSE = { MAX_REQUESTS = 10 REQUEST_HEADERS = dict() -REPOS_API = "https://api.github.com/orgs/{org}/repos" +REPOS_API = "https://api.github.com/orgs/{org}/repos?per_page=100" if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" -- cgit v1.2.3 From 291997462779b79ff088432d7333bee9b2ff2f2c Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 09:07:01 +0200 Subject: Refactor issue cog Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/issues.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 7255d450..bcaeee1f 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -115,7 +115,7 @@ class Issues(commands.Cog): """ Retrieve an issue from a GitHub repository. - returns IssueState on success, FetchError on failure. + Returns IssueState on success, FetchError on failure. """ url = ISSUE_ENDPOINT.format(user=user, repository=repository, number=number) merge_url = PR_MERGE_ENDPOINT.format(user=user, repository=repository, number=number) @@ -125,7 +125,7 @@ class Issues(commands.Cog): json_data = await r.json() if r.status == 403: - if "X-RateLimit-Remaining" in r.headers and r.headers["X-RateLimit-Remaining"] == "0": + if r.headers.get("X-RateLimit-Remaining") == "0": log.info(f"Ratelimit reached while fetching {url}") return FetchError(403, "Ratelimit reached, please retry in a few minutes.") return FetchError(403, "Cannot access issue.") @@ -138,7 +138,7 @@ class Issues(commands.Cog): # if the issue or PR is present. However, the scope of information returned for PRs differs # from issues: if the 'issues' key is present in the response then we can pull the data we # need from the initial API call. - if "issues" in json_data.get("html_url"): + if "issues" in json_data["html_url"]: if json_data.get("state") == "open": icon_url = Emojis.issue else: @@ -251,7 +251,7 @@ class Issues(commands.Cog): color=Colours.soft_red, description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) - await message.channel.send(embed=embed).delete(delay=5) + await message.channel.send(embed=embed, delete_after=5) return for repo_issue in issues: -- cgit v1.2.3 From cd0153d36f081f2a0a41b610eac86ff52211e5c5 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 09:10:48 +0200 Subject: Issues: make use of invoke_help_command Co-authored-by: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> --- bot/exts/evergreen/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bcaeee1f..d8b373d7 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -18,6 +18,7 @@ from bot.constants import ( WHITELISTED_CHANNELS ) from bot.utils.decorators import whitelist_override +from bot.utils.extensions import invoke_help_command log = logging.getLogger(__name__) @@ -206,7 +207,7 @@ class Issues(commands.Cog): description=f"Too many issues/PRs! (maximum of {MAXIMUM_ISSUES})" ) await ctx.send(embed=embed) - await ctx.invoke(self.bot.get_command('help'), 'issue') + await invoke_help_command(ctx) results = [await self.fetch_issues(number, repository, user) for number in numbers] await ctx.send(embed=self.format_embed(results, user, repository)) -- cgit v1.2.3 From a52eeb5e253a31c447d28fe663f3a60c0c9efcdc Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:35:43 +0200 Subject: Issues: add accept header --- bot/exts/evergreen/issues.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index d8b373d7..5ea8a6bf 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -26,7 +26,9 @@ BAD_RESPONSE = { 404: "Issue/pull request not located! Please enter a valid number!", 403: "Rate limit has been hit! Please try again later!" } -REQUEST_HEADERS = dict() +REQUEST_HEADERS = { + "Accept": "application/vnd.github.v3+json" +} REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos" ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" -- cgit v1.2.3 From 37a3b571467bb889526b7d21db17f7644453a85e Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:39:45 +0200 Subject: Issues: icon_url -> emoji_url --- bot/exts/evergreen/issues.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 5ea8a6bf..9b04c8c1 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -67,7 +67,7 @@ class IssueState: number: int url: str title: str - icon_url: str + emoji_url: str class Issues(commands.Cog): @@ -176,7 +176,7 @@ class Issues(commands.Cog): for result in results: if isinstance(result, IssueState): - description_list.append(f"{result.icon_url} [{result.title}]({result.url})") + description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") elif isinstance(result, FetchError): description_list.append(f"[{result.return_code}] {result.message}") -- cgit v1.2.3 From c272bdf0149d741efad477b77a7e123a42569f68 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:41:23 +0200 Subject: Issues: add red cross emoji for failed issues Co-authored-by: Shivansh-007 --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 83cafcc8..5ad13628 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -178,7 +178,7 @@ class Issues(commands.Cog): if isinstance(result, IssueState): description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") elif isinstance(result, FetchError): - description_list.append(f"[{result.return_code}] {result.message}") + description_list.append(f":x: [{result.return_code}] {result.message}") resp = discord.Embed( colour=Colours.bright_green, -- cgit v1.2.3 From 0ff42e4e2197822bd1e1a63e5ebd73d75588b1eb Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 8 Apr 2021 11:51:16 +0200 Subject: Issues: emoji_url -> emoji --- bot/exts/evergreen/issues.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 83cafcc8..10aa347d 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -67,7 +67,7 @@ class IssueState: number: int url: str title: str - emoji_url: str + emoji: str class Issues(commands.Cog): @@ -143,9 +143,9 @@ class Issues(commands.Cog): # need from the initial API call. if "issues" in json_data["html_url"]: if json_data.get("state") == "open": - icon_url = Emojis.issue + emoji = Emojis.issue else: - icon_url = Emojis.issue_closed + emoji = Emojis.issue_closed # If the 'issues' key is not contained in the API response and there is no error code, then # we know that a PR has been requested and a call to the pulls API endpoint is necessary @@ -154,16 +154,16 @@ class Issues(commands.Cog): log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") async with self.bot.http_session.get(merge_url) as m: if json_data.get("state") == "open": - icon_url = Emojis.pull_request + emoji = Emojis.pull_request # When the status is 204 this means that the state of the PR is merged elif m.status == 204: - icon_url = Emojis.merge + emoji = Emojis.merge else: - icon_url = Emojis.pull_request_closed + emoji = Emojis.pull_request_closed issue_url = json_data.get("html_url") - return IssueState(repository, number, issue_url, json_data.get('title', ''), icon_url) + return IssueState(repository, number, issue_url, json_data.get('title', ''), emoji) @staticmethod def format_embed( @@ -176,7 +176,7 @@ class Issues(commands.Cog): for result in results: if isinstance(result, IssueState): - description_list.append(f"{result.emoji_url} [{result.title}]({result.url})") + description_list.append(f"{result.emoji} [{result.title}]({result.url})") elif isinstance(result, FetchError): description_list.append(f"[{result.return_code}] {result.message}") -- cgit v1.2.3 From c94a45a10bac63eff1ce066707fe2df0b1243daa Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Thu, 8 Apr 2021 12:37:14 -0400 Subject: Use PRDraft emoji when the pr is a draft pr for the .issue|.pr command --- bot/constants.py | 1 + bot/exts/evergreen/issues.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/constants.py b/bot/constants.py index 416dd0e7..e59fa641 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -168,6 +168,7 @@ class Emojis: issue_closed = "<:IssueClosed:629695470570307614>" pull_request = "<:PROpen:629695470175780875>" pull_request_closed = "<:PRClosed:629695470519713818>" + pull_request_draft = "<:PRDraft:829755345425399848>" merge = "<:PRMerged:629695470570176522>" number_emojis = { diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4a73d20b..e83f1a3e 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -100,7 +100,7 @@ class Issues(commands.Cog): for number in numbers: url = f"https://api.github.com/repos/{user}/{repository}/issues/{number}" - merge_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}/merge" + pulls_url = f"https://api.github.com/repos/{user}/{repository}/pulls/{number}" log.trace(f"Querying GH issues API: {url}") async with self.bot.http_session.get(url, headers=REQUEST_HEADERS) as r: json_data = await r.json() @@ -123,12 +123,15 @@ class Issues(commands.Cog): # we know that a PR has been requested and a call to the pulls API endpoint is necessary # to get the desired information for the PR. else: - log.trace(f"PR provided, querying GH pulls API for additional information: {merge_url}") - async with self.bot.http_session.get(merge_url) as m: - if json_data.get("state") == "open": + log.trace(f"PR provided, querying GH pulls API for additional information: {pulls_url}") + async with self.bot.http_session.get(pulls_url) as p: + pull_data = await p.json() + if pull_data["draft"]: + icon_url = Emojis.pull_request_draft + elif pull_data["state"] == "open": icon_url = Emojis.pull_request # When the status is 204 this means that the state of the PR is merged - elif m.status == 204: + elif pull_data["merged_at"] is not None: icon_url = Emojis.merge else: icon_url = Emojis.pull_request_closed -- cgit v1.2.3 From a36a58d81b64412458a7b51fbfb4a37e88a060fe Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 8 Apr 2021 19:28:59 +0100 Subject: Add word boundaries to the issues regex --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 0d43326d..b653f7ae 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -226,7 +226,7 @@ class Issues(commands.Cog): return # `issues` will hold a list of two element tuples `(repository, issue_number)` - issues = re.findall(fr"({self.repo_regex})#(\d+)", message.content) + issues = re.findall(fr"\b({self.repo_regex})#(\d+)\b", message.content) links = [] if issues: -- cgit v1.2.3 From 0f2c076e6e64cf8f09fc8a89872767121acdfd83 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Fri, 9 Apr 2021 09:16:40 +0200 Subject: Issues: use soft_red instead of red Co-authored-by: Shivansh-007 --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index b653f7ae..3fb69127 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -239,7 +239,7 @@ class Issues(commands.Cog): "You can't retrieve issues from DMs. " f"Try again in <#{Channels.community_bot_commands}>" ), - colour=discord.Colour.red() + colour=Colours.soft_red ) ) return -- cgit v1.2.3 From edb6ffab317499689d7b8f5d0b884a8d10a01a76 Mon Sep 17 00:00:00 2001 From: ToxicKidz <78174417+ToxicKidz@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:28:36 -0400 Subject: Update comment --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 7604c438..fa07b674 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -158,7 +158,7 @@ class Issues(commands.Cog): emoji = Emojis.pull_request_draft elif pull_data["state"] == "open": emoji = Emojis.pull_request - # When the status is 204 this means that the state of the PR is merged + # When 'merged_at' is not None, this means that the state of the PR is merged elif pull_data["merged_at"] is not None: emoji = Emojis.merge else: -- cgit v1.2.3 From 51d9b5413ceb8319e0ce9847ee048f112b21fdff Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Sun, 11 Apr 2021 16:48:25 +0200 Subject: Issues: add `type=public` This will make sure that even if a privileged token is passed we won't leak any private information. --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index fa07b674..42ab97c3 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -30,7 +30,7 @@ REQUEST_HEADERS = { "Accept": "application/vnd.github.v3+json" } -REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos?per_page=100" +REPOSITORY_ENDPOINT = "https://api.github.com/orgs/{org}/repos?per_page=100&type=public" ISSUE_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/issues/{number}" PR_ENDPOINT = "https://api.github.com/repos/{user}/{repository}/pulls/{number}" -- cgit v1.2.3 From e187a3c67bcc93e8a688bac1a49541914f8b4871 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 12 Apr 2021 17:00:55 +0200 Subject: Allow automatic linking of issues outside of the python-discord organisation Actions python-discord/organisation#345 --- bot/exts/evergreen/issues.py | 75 ++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 41 deletions(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 42ab97c3..6797559c 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -5,7 +5,7 @@ import typing as t from dataclasses import dataclass import discord -from discord.ext import commands, tasks +from discord.ext import commands from bot.constants import ( Categories, @@ -50,6 +50,21 @@ CODE_BLOCK_RE = re.compile( # Maximum number of issues in one message MAXIMUM_ISSUES = 5 +# Regex used when looking for automatic linking in messages +AUTOMATIC_REGEX = re.compile(r"((?P.+?)\/)?(?P.+?)#(?P.+?)") + + +@dataclass +class FoundIssue: + """Dataclass representing an issue found by the regex.""" + + organisation: t.Optional[str] + repository: str + number: str + + def __hash__(self) -> int: + return hash(self.organisation) + hash(self.repository) + hash(self.number) + @dataclass class FetchError: @@ -76,38 +91,11 @@ class Issues(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.repos = [] - self.repo_regex = None - self.get_pydis_repos.start() - - @tasks.loop(minutes=30) - async def get_pydis_repos(self) -> None: - """ - Get all python-discord repositories on github. - - This task will update a pipe-separated list of repositories in self.repo_regex. - """ - async with self.bot.http_session.get( - REPOSITORY_ENDPOINT.format(org="python-discord"), - headers=REQUEST_HEADERS - ) as resp: - if resp.status == 200: - data = await resp.json() - for repo in data: - self.repos.append(repo["full_name"].split("/")[1]) - self.repo_regex = "|".join(self.repos) - else: - log.warning(f"Failed to get latest Pydis repositories. Status code {resp.status}") @staticmethod - def check_in_block(message: discord.Message, repo_issue: str) -> bool: - """Check whether the # is in codeblocks.""" - block = re.findall(CODE_BLOCK_RE, message.content) - - if not block: - return False - elif "#".join(repo_issue.split(" ")) in "".join([*block[0]]): - return True - return False + def remove_codeblocks(message: str) -> str: + """Remove any codeblock in a message.""" + return re.sub(CODE_BLOCK_RE, "", message) async def fetch_issues( self, @@ -219,17 +207,19 @@ class Issues(commands.Cog): @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: - """Command to retrieve issue(s) from a GitHub repository using automatic linking if matching #.""" - if not self.repo_regex: - log.warning("repo_regex isn't ready, cannot look for issues.") - return + """ + Automatic issue linking. + Listener to retrieve issue(s) from a GitHub repository using automatic linking if matching /#. + """ # Ignore bots if message.author.bot: return - # `issues` will hold a list of two element tuples `(repository, issue_number)` - issues = re.findall(fr"\b({self.repo_regex})#(\d+)\b", message.content) + issues = [ + FoundIssue(*match.group("org", "repo", "number")) + for match in AUTOMATIC_REGEX.finditer(self.remove_codeblocks(message.content)) + ] links = [] if issues: @@ -261,10 +251,13 @@ class Issues(commands.Cog): return for repo_issue in issues: - if not self.check_in_block(message, " ".join(repo_issue)): - result = await self.fetch_issues(repo_issue[1], repo_issue[0], "python-discord") - if isinstance(result, IssueState): - links.append(result) + result = await self.fetch_issues( + int(repo_issue.number), + repo_issue.repository, + repo_issue.organisation or "python-discord" + ) + if isinstance(result, IssueState): + links.append(result) if not links: return -- cgit v1.2.3 From 52dbe86fb8ff7d659e3ea7f7e49f1442e004e34d Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Mon, 12 Apr 2021 17:07:39 +0200 Subject: Issues: change hashing of FoundIssue to use a tuple --- bot/exts/evergreen/issues.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 6797559c..bb6273bb 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -63,7 +63,7 @@ class FoundIssue: number: str def __hash__(self) -> int: - return hash(self.organisation) + hash(self.repository) + hash(self.number) + return hash((self.organisation, self.repository, self.number)) @dataclass -- cgit v1.2.3 From ccdf1c9efc273f2945baed90008ab3fdd73a53a1 Mon Sep 17 00:00:00 2001 From: laundmo Date: Tue, 13 Apr 2021 15:54:03 +0200 Subject: Update issue matching regex fixes it being unable to get issue numbers larger than 9 limits it somewhat length-wise and character-wise to the actual github limits --- bot/exts/evergreen/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index bb6273bb..4dd10d13 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -51,7 +51,8 @@ CODE_BLOCK_RE = re.compile( MAXIMUM_ISSUES = 5 # Regex used when looking for automatic linking in messages -AUTOMATIC_REGEX = re.compile(r"((?P.+?)\/)?(?P.+?)#(?P.+?)") +# regex101 of current regex https://regex101.com/r/V2ji8M/6 +AUTOMATIC_REGEX = re.compile(r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)") @dataclass -- cgit v1.2.3 From a3ce39f9d2cb5a91743ce3e2a35535a65fa4034b Mon Sep 17 00:00:00 2001 From: laundmo Date: Tue, 13 Apr 2021 15:57:50 +0200 Subject: Linebreak to hopefully not run into linter issues editing this from the web version because im at work and this fixes the issue linking being basically unusable --- bot/exts/evergreen/issues.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'bot/exts/evergreen/issues.py') diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 4dd10d13..a0316080 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -52,7 +52,9 @@ MAXIMUM_ISSUES = 5 # Regex used when looking for automatic linking in messages # regex101 of current regex https://regex101.com/r/V2ji8M/6 -AUTOMATIC_REGEX = re.compile(r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)") +AUTOMATIC_REGEX = re.compile( + r"((?P[a-zA-Z0-9][a-zA-Z0-9\-]{1,39})\/)?(?P[\w\-\.]{1,100})#(?P[0-9]+)" +) @dataclass -- cgit v1.2.3