From 0fea1b597c98e5fdde355283bad5a11c46acbbd3 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Thu, 1 Oct 2020 02:28:20 +0530 Subject: GithubInfo command --- bot/exts/evergreen/githubinfo.py | 88 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 bot/exts/evergreen/githubinfo.py diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py new file mode 100644 index 00000000..fd1ba47f --- /dev/null +++ b/bot/exts/evergreen/githubinfo.py @@ -0,0 +1,88 @@ +import logging +import aiohttp +from datetime import datetime + +import discord +from discord.ext import commands +from discord.ext.commands.cooldowns import BucketType + +log = logging.getLogger(__name__) + + +class GithubInfo(commands.Cog): + + def __init__(self, bot: commands.Bot): + self.bot = bot + + async def fetch_data(self, url: str) -> dict: + """Retrieve data as a dictionary""" + + async with aiohttp.ClientSession() as cs: + async with cs.get(url) as r: + return await r.json() + + @commands.command(name='github', aliases=['gh']) + @commands.cooldown(1, 5, BucketType.user) + async def get_github_info(self, ctx: commands.Context, username: str = 'python-discord') -> None: + """Fetches a user's GitHub information + + User defaults to python-discord + + Usage: .gh [username] + """ + + # fetch user data + user_data = await self.fetch_data(f"https://api.github.com/users/{username}") + + # user_data will not have a message key if the user exists + if user_data.get('message') is not None: + return await ctx.send(embed=discord.Embed(title=f"The profile `{username}` was not found", colour=0xff0022)) + + # fetching organization data and forming the organization string + org_data = await self.fetch_data(user_data['organizations_url']) + + orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] + orgs_to_add = ' | '.join(orgs) + + # fetching starred repos data + starred_data = await self.fetch_data(user_data['starred_url']) + + # forming blog link + if user_data['blog'].startswith("http"): # blog link is clickable + blog = f"[Direct link]({user_data['blog']})" + elif user_data['blog']: # blog exists but the link is not clickable + blog = f"[Direct link](https://{user_data['blog']})" + else: # blog does not exist + blog = "No blog link available" + + # building embed + embed = discord.Embed( + title=f"`{user_data['login']}`'s GitHub profile info", + description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", + colour=0xf5faf6, + url=user_data['html_url'], + timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") + ) + embed.set_thumbnail(url=user_data['avatar_url']) + embed.set_footer(text="Account created at") + + embed.add_field(name="Followers", value=f"[{user_data['followers']}]{user_data['html_url']}?tab=followers)") + embed.add_field(name="\u200b", value="\u200b") # zero width space + embed.add_field(name="Following", value=f"[{user_data['following']}]{user_data['html_url']}?tab=following)") + + embed.add_field(name="Public repos", + value=f"[{user_data['public_repos']}]{user_data['html_url']}?tab=repositories)") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]{user_data['html_url']}?tab=stars)") + + embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", + value=orgs_to_add if orgs else "No organizations") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Blog", value=blog) + + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot) -> None: + """Load the cog""" + bot.add_cog(GithubInfo(bot)) -- cgit v1.2.3 From cd20d5499ee74bccd5280663f374fbfefaeccc17 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Thu, 1 Oct 2020 09:55:17 +0530 Subject: fixed checks failing by running Installing dependencies from Pipfile.lock (2801e4)… To activate this project's virtualenv, run pipenv shell. Alternatively, run a command inside the virtualenv with pipenv run. All dependencies are now up-to-date! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index fd1ba47f..e2541a23 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -84,5 +84,5 @@ class GithubInfo(commands.Cog): def setup(bot: commands.Bot) -> None: - """Load the cog""" + """Adding the cog to the bot""" bot.add_cog(GithubInfo(bot)) -- cgit v1.2.3 From 7d593edd30d1d14cb367c5896333fa343fe3e6ea Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Thu, 1 Oct 2020 10:08:12 +0530 Subject: fixed issues showing up in Check for merge conflicts................................................Passed Check Toml...............................................................Passed Check Yaml...............................................................Passed Fix End of Files.........................................................Passed Mixed line ending........................................................Passed Trim Trailing Whitespace.................................................Passed check blanket noqa.......................................................Passed Flake8...................................................................Passed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/exts/evergreen/githubinfo.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index e2541a23..46f6d167 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,7 +1,7 @@ import logging -import aiohttp from datetime import datetime +import aiohttp import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType @@ -10,13 +10,13 @@ log = logging.getLogger(__name__) class GithubInfo(commands.Cog): + """Fetches GitHub.""" def __init__(self, bot: commands.Bot): self.bot = bot async def fetch_data(self, url: str) -> dict: - """Retrieve data as a dictionary""" - + """Retrieve data as a dictionary.""" async with aiohttp.ClientSession() as cs: async with cs.get(url) as r: return await r.json() @@ -24,13 +24,13 @@ class GithubInfo(commands.Cog): @commands.command(name='github', aliases=['gh']) @commands.cooldown(1, 5, BucketType.user) async def get_github_info(self, ctx: commands.Context, username: str = 'python-discord') -> None: - """Fetches a user's GitHub information + """ + Fetches a user's GitHub information. - User defaults to python-discord + User defaults to python-discord. Usage: .gh [username] """ - # fetch user data user_data = await self.fetch_data(f"https://api.github.com/users/{username}") @@ -84,5 +84,5 @@ class GithubInfo(commands.Cog): def setup(bot: commands.Bot) -> None: - """Adding the cog to the bot""" + """Adding the cog to the bot.""" bot.add_cog(GithubInfo(bot)) -- cgit v1.2.3 From 2600a1c541abb8f2470fca2e14c66b628688d0fa Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Fri, 2 Oct 2020 12:37:36 +0530 Subject: implemented requested changes --- bot/exts/evergreen/githubinfo.py | 110 +++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 46f6d167..4935e1de 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,5 +1,6 @@ import logging from datetime import datetime +from typing import Optional import aiohttp import discord @@ -10,12 +11,13 @@ log = logging.getLogger(__name__) class GithubInfo(commands.Cog): - """Fetches GitHub.""" + """Fetches info from GitHub.""" def __init__(self, bot: commands.Bot): self.bot = bot - async def fetch_data(self, url: str) -> dict: + @staticmethod + async def fetch_data(url: str) -> dict: """Retrieve data as a dictionary.""" async with aiohttp.ClientSession() as cs: async with cs.get(url) as r: @@ -23,7 +25,7 @@ class GithubInfo(commands.Cog): @commands.command(name='github', aliases=['gh']) @commands.cooldown(1, 5, BucketType.user) - async def get_github_info(self, ctx: commands.Context, username: str = 'python-discord') -> None: + async def get_github_info(self, ctx: commands.Context, username: Optional[str]) -> None: """ Fetches a user's GitHub information. @@ -31,54 +33,60 @@ class GithubInfo(commands.Cog): Usage: .gh [username] """ - # fetch user data - user_data = await self.fetch_data(f"https://api.github.com/users/{username}") - - # user_data will not have a message key if the user exists - if user_data.get('message') is not None: - return await ctx.send(embed=discord.Embed(title=f"The profile `{username}` was not found", colour=0xff0022)) - - # fetching organization data and forming the organization string - org_data = await self.fetch_data(user_data['organizations_url']) - - orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] - orgs_to_add = ' | '.join(orgs) - - # fetching starred repos data - starred_data = await self.fetch_data(user_data['starred_url']) - - # forming blog link - if user_data['blog'].startswith("http"): # blog link is clickable - blog = f"[Direct link]({user_data['blog']})" - elif user_data['blog']: # blog exists but the link is not clickable - blog = f"[Direct link](https://{user_data['blog']})" - else: # blog does not exist - blog = "No blog link available" - - # building embed - embed = discord.Embed( - title=f"`{user_data['login']}`'s GitHub profile info", - description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", - colour=0xf5faf6, - url=user_data['html_url'], - timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") - ) - embed.set_thumbnail(url=user_data['avatar_url']) - embed.set_footer(text="Account created at") - - embed.add_field(name="Followers", value=f"[{user_data['followers']}]{user_data['html_url']}?tab=followers)") - embed.add_field(name="\u200b", value="\u200b") # zero width space - embed.add_field(name="Following", value=f"[{user_data['following']}]{user_data['html_url']}?tab=following)") - - embed.add_field(name="Public repos", - value=f"[{user_data['public_repos']}]{user_data['html_url']}?tab=repositories)") - embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]{user_data['html_url']}?tab=stars)") - - embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", - value=orgs_to_add if orgs else "No organizations") - embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Blog", value=blog) + + if username is None: + await ctx.invoke(self.bot.get_command('help'), 'github') + return + + async with ctx.typing(): + + user_data = await self.fetch_data(f"https://api.github.com/users/{username}") + + # user_data will not have a message key if the user exists + if user_data.get('message') is not None: + await ctx.send(embed=discord.Embed(title=f"The profile for `{username}` was not found.", + colour=0xff0022)) + return + + org_data = await self.fetch_data(user_data['organizations_url']) + orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] + orgs_to_add = ' | '.join(orgs) + + starred_data = await self.fetch_data(user_data['starred_url']) + + # Forming blog link + if user_data['blog'].startswith("http"): # Blog link is clickable + blog = f"[Direct link]({user_data['blog']})" + elif user_data['blog']: # Blog exists but the link is not clickable + blog = f"[Direct link](https://{user_data['blog']})" + else: + blog = "No blog link available" + + embed = discord.Embed( + title=f"`{user_data['login']}`'s GitHub profile info", + description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", + colour=0xf5faf6, + url=user_data['html_url'], + timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") + ) + embed.set_thumbnail(url=user_data['avatar_url']) + embed.set_footer(text="Account created at") + + embed.add_field(name="Followers", value=f"""[{user_data['followers']}] + ({user_data['html_url']}?tab=followers)""") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Following", value=f"""[{user_data['following']}] + ({user_data['html_url']}?tab=following)""") + + embed.add_field(name="Public repos", + value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]({user_data['html_url']}?tab=stars)") + + embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", + value=orgs_to_add if orgs else "No organizations") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Blog", value=blog) await ctx.send(embed=embed) -- cgit v1.2.3 From fa1364622c0768186eb5919dd5661c9c64c565a7 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Fri, 2 Oct 2020 12:42:42 +0530 Subject: linting smh --- bot/exts/evergreen/githubinfo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 4935e1de..b7ebcb3e 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -29,11 +29,10 @@ class GithubInfo(commands.Cog): """ Fetches a user's GitHub information. - User defaults to python-discord. + Username is optional and sends the help command if not specified. Usage: .gh [username] """ - if username is None: await ctx.invoke(self.bot.get_command('help'), 'github') return -- cgit v1.2.3 From f7e26fb4d3b8c6869eb4dd8dc8b9ef31ceb8352a Mon Sep 17 00:00:00 2001 From: Lakshya Shastri <62901541+LakshyaShastri@users.noreply.github.com> Date: Sat, 3 Oct 2020 03:46:10 +0530 Subject: final changes --- bot/exts/evergreen/githubinfo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index b7ebcb3e..2a540bac 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -41,7 +41,7 @@ class GithubInfo(commands.Cog): user_data = await self.fetch_data(f"https://api.github.com/users/{username}") - # user_data will not have a message key if the user exists + # User_data will not have a message key if the user exists if user_data.get('message') is not None: await ctx.send(embed=discord.Embed(title=f"The profile for `{username}` was not found.", colour=0xff0022)) @@ -54,9 +54,9 @@ class GithubInfo(commands.Cog): starred_data = await self.fetch_data(user_data['starred_url']) # Forming blog link - if user_data['blog'].startswith("http"): # Blog link is clickable + if user_data['blog'].startswith("http"): # Blog link is complete blog = f"[Direct link]({user_data['blog']})" - elif user_data['blog']: # Blog exists but the link is not clickable + elif user_data['blog']: # Blog exists but the link is not complete blog = f"[Direct link](https://{user_data['blog']})" else: blog = "No blog link available" -- cgit v1.2.3 From 9a5219bf62f9d502387a22701ca0c329088042f3 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Sat, 3 Oct 2020 16:06:57 +0800 Subject: exclude spam label in get_october_prs for hacktoberstats, prs with spam labels doesnt count --- bot/exts/halloween/hacktoberstats.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index ed1755e3..a6ed765e 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -162,7 +162,7 @@ class HacktoberStats(commands.Cog): """ Query GitHub's API for PRs created by a GitHub user during the month of October. - PRs with the 'invalid' tag are ignored + PRs with an 'invalid' or 'spam' label are ignored If a valid github_username is provided, an embed is generated and posted to the channel @@ -220,7 +220,7 @@ class HacktoberStats(commands.Cog): """ Query GitHub's API for PRs created during the month of October by github_username. - PRs with an 'invalid' tag are ignored + PRs with an 'invalid' or 'spam' label are ignored If PRs are found, return a list of dicts with basic PR information @@ -235,7 +235,7 @@ class HacktoberStats(commands.Cog): """ logging.info(f"Generating Hacktoberfest PR query for GitHub user: '{github_username}'") base_url = "https://api.github.com/search/issues?q=" - not_label = "invalid" + not_labels = ("invalid", "spam") action_type = "pr" is_query = f"public+author:{github_username}" not_query = "draft" @@ -243,7 +243,8 @@ class HacktoberStats(commands.Cog): per_page = "300" query_url = ( f"{base_url}" - f"-label:{not_label}" + f"-label:{not_labels[0]}" + f"-label:{not_labels[1]}" f"+type:{action_type}" f"+is:{is_query}" f"+-is:{not_query}" -- cgit v1.2.3 From 0cc321f5694c0366297f1d46ba160d65853732a9 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Sat, 3 Oct 2020 13:38:45 +0530 Subject: made code more consistent --- bot/exts/evergreen/githubinfo.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 2a540bac..f52454ba 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -1,4 +1,5 @@ import logging +import random from datetime import datetime from typing import Optional @@ -7,6 +8,8 @@ import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType +from bot.constants import NEGATIVE_REPLIES + log = logging.getLogger(__name__) @@ -38,13 +41,12 @@ class GithubInfo(commands.Cog): return async with ctx.typing(): - user_data = await self.fetch_data(f"https://api.github.com/users/{username}") # User_data will not have a message key if the user exists if user_data.get('message') is not None: - await ctx.send(embed=discord.Embed(title=f"The profile for `{username}` was not found.", - colour=0xff0022)) + await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), + colour=discord.Colour.red())) return org_data = await self.fetch_data(user_data['organizations_url']) @@ -64,7 +66,7 @@ class GithubInfo(commands.Cog): embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", description=f"```{user_data['bio']}```\n" if user_data['bio'] is not None else "", - colour=0xf5faf6, + colour=0x7289da, url=user_data['html_url'], timestamp=datetime.strptime(user_data['created_at'], "%Y-%m-%dT%H:%M:%SZ") ) -- cgit v1.2.3 From b80d2640c12ed4c2779c2d25737a290bfd423505 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Sun, 4 Oct 2020 15:57:59 +0530 Subject: added animated emoji check --- bot/exts/evergreen/emoji_count.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index ef900199..f360ed26 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -34,7 +34,10 @@ class EmojiCount(commands.Cog): for key, value in emoji.items(): emoji_choice = random.choice(value) emoji_info = f'There are **{len(value)}** emojis in the **{key}** category\n' - msg += f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}' + if emoji_choice.animated: + msg += f' {emoji_info}' + else: + msg += f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}' embed.description = msg return embed -- cgit v1.2.3 From f9341adb8bf5cd0e5329b0b2db9b2ff9528823f8 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Sun, 4 Oct 2020 19:15:13 +0530 Subject: added animated emoji check + handled embed exceeding problem --- bot/exts/evergreen/emoji_count.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index f360ed26..6e0c3909 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -7,6 +7,7 @@ import discord from discord.ext import commands from bot.constants import Colours, ERROR_REPLIES +from bot.utils.pagination import LinePaginator log = logging.getLogger(__name__) @@ -27,8 +28,9 @@ class EmojiCount(commands.Cog): if len(emoji) == 1: for key, value in emoji.items(): - embed.description = f"There are **{len(value)}** emojis in the **{key}** category" + msg = f"There are **{len(value)}** emojis in the **{key}** category" embed.set_thumbnail(url=random.choice(value).url) + embed.description = msg else: msg = '' for key, value in emoji.items(): @@ -39,7 +41,7 @@ class EmojiCount(commands.Cog): else: msg += f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}' embed.description = msg - return embed + return embed, msg @staticmethod def generate_invalid_embed(ctx: commands.Context) -> discord.Embed: @@ -54,8 +56,8 @@ class EmojiCount(commands.Cog): emoji_dict[emoji.name.split("_")[0]] = [] error_comp = ', '.join(key for key in emoji_dict.keys()) - embed.description = f"These are the valid categories\n```{error_comp}```" - return embed + msg = f"These are the valid categories\n```{error_comp}```" + return embed, msg def emoji_list(self, ctx: commands.Context, categories: dict) -> Dict: """Generates an embed with the emoji names and count.""" @@ -83,10 +85,11 @@ class EmojiCount(commands.Cog): emoji_dict = self.emoji_list(ctx, emoji_dict) if len(emoji_dict) == 0: - embed = self.generate_invalid_embed(ctx) + embed, msg = self.generate_invalid_embed(ctx) else: - embed = self.embed_builder(emoji_dict) - await ctx.send(embed=embed) + embed, msg = self.embed_builder(emoji_dict) + msg = msg.split("\n") + await LinePaginator.paginate([line for line in msg], ctx=ctx, embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 7909c4d6c764b33705d1b9bebdbc38bed188bb5d Mon Sep 17 00:00:00 2001 From: Gustav Odinger Date: Sun, 4 Oct 2020 22:33:54 +0200 Subject: Add space between dice in roll command - Looks much better - Cleans up the code of the roll command --- bot/exts/evergreen/fun.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index de6a92c6..9d6ee29c 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -60,15 +60,13 @@ class Fun(Cog): @commands.command() async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" - output = "" if num_rolls > 6: num_rolls = 6 - elif num_rolls < 1: - output = ":no_entry: You must roll at least once." - for _ in range(num_rolls): - dice = f"dice_{random.randint(1, 6)}" - output += getattr(Emojis, dice, '') - await ctx.send(output) + + dice = (f"dice_{random.randint(1, 6)}" for _ in range(num_rolls)) + output = " ".join(getattr(Emojis, die, '') for die in dice) + + await ctx.send(output or ":no_entry: You must roll at least once.") @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) async def uwu_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: -- cgit v1.2.3 From 6969fc1973261bcba51b7346c8e168bcc011b833 Mon Sep 17 00:00:00 2001 From: Gustav Odinger Date: Sun, 4 Oct 2020 23:30:47 +0200 Subject: Revert and update roll command - Returns to previous version of code - Improves readability - Adds spaced between dice --- bot/exts/evergreen/fun.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 9d6ee29c..23715906 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -60,13 +60,15 @@ class Fun(Cog): @commands.command() async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" + output = "" if num_rolls > 6: num_rolls = 6 - - dice = (f"dice_{random.randint(1, 6)}" for _ in range(num_rolls)) - output = " ".join(getattr(Emojis, die, '') for die in dice) - - await ctx.send(output or ":no_entry: You must roll at least once.") + elif num_rolls < 1: + output = ":no_entry: You must roll at least once." + for _ in range(num_rolls): + dice = f"dice_{random.randint(1, 6)}" + output += getattr(Emojis, dice, '') + " " + await ctx.send(output.rstrip()) @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) async def uwu_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: -- cgit v1.2.3 From c97d27e1faee410ee52e736143f5e925a7b68d04 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 07:36:56 +0800 Subject: fix query syntax --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index a6ed765e..47b83023 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -244,7 +244,7 @@ class HacktoberStats(commands.Cog): query_url = ( f"{base_url}" f"-label:{not_labels[0]}" - f"-label:{not_labels[1]}" + f"+-label:{not_labels[1]}" f"+type:{action_type}" f"+is:{is_query}" f"+-is:{not_query}" -- cgit v1.2.3 From a5e5afff1a6422ff4fcd6685f81346083ffb120f Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 08:02:05 +0800 Subject: improve no PR message --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 47b83023..2f10a8e8 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -175,7 +175,7 @@ class HacktoberStats(commands.Cog): stats_embed = self.build_embed(github_username, prs) await ctx.send('Here are some stats!', embed=stats_embed) else: - await ctx.send(f"No October GitHub contributions found for '{github_username}'") + await ctx.send(f"No valid October GitHub contributions found for '{github_username}'") def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" -- cgit v1.2.3 From 65b2228595925fc13f3c3c73062be0494f53e622 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 08:04:15 +0800 Subject: add logging generated url --- bot/exts/halloween/hacktoberstats.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 2f10a8e8..53f6d9dc 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -251,6 +251,7 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) + logging.info(f"Query URL generated: {query_url}") async with aiohttp.ClientSession() as session: async with session.get(query_url, headers=REQUEST_HEADERS) as resp: -- cgit v1.2.3 From cb25b1557c3d90b27d6e4b3d619aedfb60e1765b Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Mon, 5 Oct 2020 12:36:45 +0530 Subject: removed emoji list and added default dict --- bot/exts/evergreen/emoji_count.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 6e0c3909..544a0019 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -1,7 +1,8 @@ import datetime import logging import random -from typing import Dict, Optional +from typing import Optional +from collections import defaultdict import discord from discord.ext import commands @@ -59,30 +60,18 @@ class EmojiCount(commands.Cog): msg = f"These are the valid categories\n```{error_comp}```" return embed, msg - def emoji_list(self, ctx: commands.Context, categories: dict) -> Dict: - """Generates an embed with the emoji names and count.""" - out = {category: [] for category in categories} - - for emoji in ctx.guild.emojis: - category = emoji.name.split('_')[0] - if category in out: - out[category].append(emoji) - return out - @commands.command(name="emoji_count", aliases=["ec"]) - async def ec(self, ctx: commands.Context, *, emoji: str = None) -> Optional[str]: + async def ec(self, ctx: commands.Context, *, emoji_category: str = None) -> Optional[str]: """Returns embed with emoji category and info given by the user.""" - emoji_dict = {} + emoji_dict = defaultdict(list) - for a in ctx.guild.emojis: - if emoji is None: + for emoji in ctx.guild.emojis: + if emoji_category is None: log.trace("Emoji Category not provided by the user") - emoji_dict.update({a.name.split("_")[0]: []}) - elif a.name.split("_")[0] in emoji: + emoji_dict[emoji.name.split("_")[0]].append(emoji) + elif emoji.name.split("_")[0] in emoji_category: log.trace("Emoji Category provided by the user") - emoji_dict.update({a.name.split("_")[0]: []}) - - emoji_dict = self.emoji_list(ctx, emoji_dict) + emoji_dict[emoji.name.split("_")[0]].append(emoji) if len(emoji_dict) == 0: embed, msg = self.generate_invalid_embed(ctx) -- cgit v1.2.3 From adb5d2db1a01cc32bab10286a617e27853349ff8 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Mon, 5 Oct 2020 13:36:16 +0530 Subject: corrected annotation --- bot/exts/evergreen/emoji_count.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 544a0019..c030fa75 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -1,7 +1,6 @@ import datetime import logging import random -from typing import Optional from collections import defaultdict import discord @@ -19,49 +18,49 @@ class EmojiCount(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - def embed_builder(self, emoji: dict) -> discord.Embed: + def embed_builder(self, emoji: dict) -> [discord.Embed, str]: """Generates an embed with the emoji names and count.""" embed = discord.Embed( color=Colours.orange, title="Emoji Count", timestamp=datetime.datetime.utcnow() ) + msg = [] if len(emoji) == 1: for key, value in emoji.items(): - msg = f"There are **{len(value)}** emojis in the **{key}** category" + msg.append(f"There are **{len(value)}** emojis in the **{key}** category") embed.set_thumbnail(url=random.choice(value).url) - embed.description = msg + else: - msg = '' for key, value in emoji.items(): emoji_choice = random.choice(value) emoji_info = f'There are **{len(value)}** emojis in the **{key}** category\n' if emoji_choice.animated: - msg += f' {emoji_info}' + msg.append(f' {emoji_info}') else: - msg += f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}' - embed.description = msg + msg.append(f'<:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}') return embed, msg @staticmethod - def generate_invalid_embed(ctx: commands.Context) -> discord.Embed: + def generate_invalid_embed(ctx: commands.Context) -> [discord.Embed, str]: """Genrates error embed.""" embed = discord.Embed( color=Colours.soft_red, title=random.choice(ERROR_REPLIES) ) + msg = [] - emoji_dict = {} + emoji_dict = defaultdict(list) for emoji in ctx.guild.emojis: - emoji_dict[emoji.name.split("_")[0]] = [] + emoji_dict[emoji.name.split("_")[0]].append(emoji) error_comp = ', '.join(key for key in emoji_dict.keys()) - msg = f"These are the valid categories\n```{error_comp}```" + msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg @commands.command(name="emoji_count", aliases=["ec"]) - async def ec(self, ctx: commands.Context, *, emoji_category: str = None) -> Optional[str]: + async def emoji_count(self, ctx: commands.Context, *, emoji_category: str = None) -> None: """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) @@ -77,8 +76,7 @@ class EmojiCount(commands.Cog): embed, msg = self.generate_invalid_embed(ctx) else: embed, msg = self.embed_builder(emoji_dict) - msg = msg.split("\n") - await LinePaginator.paginate([line for line in msg], ctx=ctx, embed=embed) + await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From adb383a84007eb2c84a63f9b1f9324fdd2fcfbcb Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 16:41:27 +0800 Subject: update logging query url --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 53f6d9dc..af79ce7e 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -251,7 +251,7 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - logging.info(f"Query URL generated: {query_url}") + logging.debug(f"GitHub query URL generated: {query_url}") async with aiohttp.ClientSession() as session: async with session.get(query_url, headers=REQUEST_HEADERS) as resp: -- cgit v1.2.3 From 1e21838a47365d319e9c305ed2ff515d9f6392ce Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 16:43:05 +0800 Subject: update generation of query url moved `author:` outside of `is_query` for better readability and understanding --- bot/exts/halloween/hacktoberstats.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index af79ce7e..9d91a310 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -237,7 +237,7 @@ class HacktoberStats(commands.Cog): base_url = "https://api.github.com/search/issues?q=" not_labels = ("invalid", "spam") action_type = "pr" - is_query = f"public+author:{github_username}" + is_query = f"public" not_query = "draft" date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-31T23:59:59-11:00" per_page = "300" @@ -247,6 +247,7 @@ class HacktoberStats(commands.Cog): f"+-label:{not_labels[1]}" f"+type:{action_type}" f"+is:{is_query}" + f"+author:{github_username}" f"+-is:{not_query}" f"+created:{date_range}" f"&per_page={per_page}" -- cgit v1.2.3 From d8138538a66d69760c1eac36af05c93745c3e076 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 5 Oct 2020 18:51:25 +0800 Subject: fix for flake8 - no need fstring --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 9d91a310..be750dd4 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -237,7 +237,7 @@ class HacktoberStats(commands.Cog): base_url = "https://api.github.com/search/issues?q=" not_labels = ("invalid", "spam") action_type = "pr" - is_query = f"public" + is_query = "public" not_query = "draft" date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-31T23:59:59-11:00" per_page = "300" -- cgit v1.2.3 From 0397361cc16ebc646d83d38b9dc8411df73e871d Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Mon, 5 Oct 2020 17:07:42 +0530 Subject: added 1 logging and modified for loop in emoji_count --- bot/exts/evergreen/emoji_count.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index c030fa75..4999fc15 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -60,19 +60,21 @@ class EmojiCount(commands.Cog): return embed, msg @commands.command(name="emoji_count", aliases=["ec"]) - async def emoji_count(self, ctx: commands.Context, *, emoji_category: str = None) -> None: + async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) + log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user") for emoji in ctx.guild.emojis: - if emoji_category is None: - log.trace("Emoji Category not provided by the user") - emoji_dict[emoji.name.split("_")[0]].append(emoji) - elif emoji.name.split("_")[0] in emoji_category: - log.trace("Emoji Category provided by the user") - emoji_dict[emoji.name.split("_")[0]].append(emoji) + emoji_category = emoji.name.split("_")[0] + + if category_query is not None and emoji_category not in category_query: + continue + + emoji_dict[emoji_category].append(emoji) if len(emoji_dict) == 0: + log.trace("Invalid name provided by the user") embed, msg = self.generate_invalid_embed(ctx) else: embed, msg = self.embed_builder(emoji_dict) -- cgit v1.2.3 From 225811f1a804a13af1235cd5d5c4994c9a1e2635 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Mon, 5 Oct 2020 17:57:24 +0530 Subject: removed keys() --- bot/exts/evergreen/emoji_count.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 4999fc15..365dc09d 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -55,7 +55,7 @@ class EmojiCount(commands.Cog): for emoji in ctx.guild.emojis: emoji_dict[emoji.name.split("_")[0]].append(emoji) - error_comp = ', '.join(key for key in emoji_dict.keys()) + error_comp = ', '.join(key for key in emoji_dict) msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg -- cgit v1.2.3 From cfb9237d38b2e2961d1aa2f7dd41f81c7346fb57 Mon Sep 17 00:00:00 2001 From: Gustav Odinger Date: Mon, 5 Oct 2020 18:54:12 +0200 Subject: Rewrite roll command - Improves readability - Sends dice separated by single spaces --- bot/exts/evergreen/fun.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 23715906..09400d23 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -7,7 +7,7 @@ from typing import Callable, Iterable, Tuple, Union from discord import Embed, Message from discord.ext import commands -from discord.ext.commands import Bot, Cog, Context, MessageConverter, clean_content +from discord.ext.commands import BadArgument, Bot, Cog, Context, MessageConverter, clean_content from bot import utils from bot.constants import Colours, Emojis @@ -60,15 +60,17 @@ class Fun(Cog): @commands.command() async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" - output = "" - if num_rolls > 6: - num_rolls = 6 - elif num_rolls < 1: - output = ":no_entry: You must roll at least once." - for _ in range(num_rolls): - dice = f"dice_{random.randint(1, 6)}" - output += getattr(Emojis, dice, '') + " " - await ctx.send(output.rstrip()) + def _get_random_die() -> str: + """Generate a random die emoji, ready to be sent on Discord.""" + die_name = f"dice_{random.randint(1, 6)}" + return getattr(Emojis, die_name) + + # Only support between 1 and 6 rolls + if 1 <= num_rolls <= 6: + dice = " ".join(_get_random_die() for _ in range(num_rolls)) + await ctx.send(dice) + else: + raise BadArgument("`!roll` only supports between 1 and 6 rolls.") @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) async def uwu_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: -- cgit v1.2.3 From ebf41d3edd012378bb3b150c355ab726310d1681 Mon Sep 17 00:00:00 2001 From: Gustav Odinger Date: Mon, 5 Oct 2020 19:22:34 +0200 Subject: Move _get_random_die to me a separate function - The function is only created once, instead of every time the roll command is run --- bot/exts/evergreen/fun.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 09400d23..49c5d69e 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -57,17 +57,17 @@ class Fun(Cog): with Path("bot/resources/evergreen/caesar_info.json").open("r", encoding="UTF-8") as f: self._caesar_cipher_embed = json.load(f) + @staticmethod + def _get_random_die() -> str: + """Generate a random die emoji, ready to be sent on Discord.""" + die_name = f"dice_{random.randint(1, 6)}" + return getattr(Emojis, die_name) + @commands.command() async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" - def _get_random_die() -> str: - """Generate a random die emoji, ready to be sent on Discord.""" - die_name = f"dice_{random.randint(1, 6)}" - return getattr(Emojis, die_name) - - # Only support between 1 and 6 rolls if 1 <= num_rolls <= 6: - dice = " ".join(_get_random_die() for _ in range(num_rolls)) + dice = " ".join(Fun._get_random_die() for _ in range(num_rolls)) await ctx.send(dice) else: raise BadArgument("`!roll` only supports between 1 and 6 rolls.") -- cgit v1.2.3 From d438b275ea13a3ab3118e6d02869e16a8ef64ffe Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Wed, 7 Oct 2020 08:43:21 +0000 Subject: WIP: update stats fetching logic - draft code" --- bot/exts/halloween/hacktoberstats.py | 81 ++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index be750dd4..2b46192f 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -239,7 +239,7 @@ class HacktoberStats(commands.Cog): action_type = "pr" is_query = "public" not_query = "draft" - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-31T23:59:59-11:00" + date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-03T23:59:59-11:00" per_page = "300" query_url = ( f"{base_url}" @@ -252,22 +252,54 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - logging.debug(f"GitHub query URL generated: {query_url}") + logging.debug(f"First GitHub query URL generated: {query_url}") async with aiohttp.ClientSession() as session: async with session.get(query_url, headers=REQUEST_HEADERS) as resp: jsonresp = await resp.json() - if "message" in jsonresp.keys(): - # One of the parameters is invalid, short circuit for now - api_message = jsonresp["errors"][0]["message"] + if not _valid_github_response(jsonresp, github_username): + return - # Ignore logging non-existent users or users we do not have permission to see - if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: - logging.debug(f"No GitHub user found named '{github_username}'") + else: + if jsonresp["total_count"] == 0: + # Short circuit if there aren't any PRs + logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") + return else: - logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") + # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") + outlist = [] + for item in jsonresp["items"]: + shortname = HacktoberStats._get_shortname(item["repository_url"]) + itemdict = { + "repo_url": f"https://www.github.com/{shortname}", + "repo_shortname": shortname, + "created_at": datetime.strptime( + item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" + ), + } + outlist.append(itemdict) + # return outlist + + date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T23:59:59-11:00" + query_url = ( + f"{base_url}" + f"-label:{not_labels[0]}" + f"+-label:{not_labels[1]}" + f"+type:{action_type}" + f"+is:{is_query}" + f"+author:{github_username}" + f"+-is:{not_query}" + f"+created:{date_range}" + f"&per_page={per_page}" + ) + logging.debug(f"Second GitHub query URL generated: {query_url}") + + async with aiohttp.ClientSession() as session: + async with session.get(query_url, headers=REQUEST_HEADERS) as resp: + jsonresp = await resp.json() + if not _valid_github_response(jsonresp, github_username): return else: @@ -276,8 +308,6 @@ class HacktoberStats(commands.Cog): logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") return else: - logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") - outlist = [] for item in jsonresp["items"]: shortname = HacktoberStats._get_shortname(item["repository_url"]) itemdict = { @@ -287,9 +317,36 @@ class HacktoberStats(commands.Cog): item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" ), } - outlist.append(itemdict) + query_url = f"https://api.github.com/repos/{shortname}/topics" + accept_header = {"Accept": "application/vnd.github.mercy-preview+json"} + + async with aiohttp.ClientSession() as session: + async with session.get(query_url, headers=accept_header) as resp: + jsonresp2 = await resp.json() + + if not _valid_github_response(jsonresp2, github_username): + return + + if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): + outlist.append(itemdict) return outlist + @staticmethod + def _valid_github_response(jsonresp: str, username: str): + if "message" in jsonresp.keys(): + # One of the parameters is invalid, short circuit for now + api_message = jsonresp["errors"][0]["message"] + + # Ignore logging non-existent users or users we do not have permission to see + if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: + logging.debug(f"No GitHub user found named '{username}'") + return False + else: + logging.error(f"GitHub API request for '{username}' failed with message: {api_message}") + return False + + return True + @staticmethod def _get_shortname(in_url: str) -> str: """ -- cgit v1.2.3 From 8c754b158be92c119126ba86ea73510aa93a0e57 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Wed, 7 Oct 2020 11:01:22 +0000 Subject: almost finished --- bot/exts/halloween/hacktoberstats.py | 119 +++++++++++++---------------------- 1 file changed, 42 insertions(+), 77 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 2b46192f..be4534b4 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -29,6 +29,8 @@ GITHUB_NONEXISTENT_USER_MESSAGE = ( "or you do not have permission to view the users." ) +GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} + class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -239,7 +241,7 @@ class HacktoberStats(commands.Cog): action_type = "pr" is_query = "public" not_query = "draft" - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-03T23:59:59-11:00" + date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T00:00:00-11:00" per_page = "300" query_url = ( f"{base_url}" @@ -252,87 +254,12 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - logging.debug(f"First GitHub query URL generated: {query_url}") + logging.debug(f"GitHub query URL generated: {query_url}") async with aiohttp.ClientSession() as session: async with session.get(query_url, headers=REQUEST_HEADERS) as resp: jsonresp = await resp.json() - if not _valid_github_response(jsonresp, github_username): - return - - else: - if jsonresp["total_count"] == 0: - # Short circuit if there aren't any PRs - logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") - return - else: - # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") - outlist = [] - for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) - itemdict = { - "repo_url": f"https://www.github.com/{shortname}", - "repo_shortname": shortname, - "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" - ), - } - outlist.append(itemdict) - # return outlist - - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T23:59:59-11:00" - query_url = ( - f"{base_url}" - f"-label:{not_labels[0]}" - f"+-label:{not_labels[1]}" - f"+type:{action_type}" - f"+is:{is_query}" - f"+author:{github_username}" - f"+-is:{not_query}" - f"+created:{date_range}" - f"&per_page={per_page}" - ) - logging.debug(f"Second GitHub query URL generated: {query_url}") - - async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=REQUEST_HEADERS) as resp: - jsonresp = await resp.json() - - if not _valid_github_response(jsonresp, github_username): - return - - else: - if jsonresp["total_count"] == 0: - # Short circuit if there aren't any PRs - logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") - return - else: - for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) - itemdict = { - "repo_url": f"https://www.github.com/{shortname}", - "repo_shortname": shortname, - "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" - ), - } - query_url = f"https://api.github.com/repos/{shortname}/topics" - accept_header = {"Accept": "application/vnd.github.mercy-preview+json"} - - async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=accept_header) as resp: - jsonresp2 = await resp.json() - - if not _valid_github_response(jsonresp2, github_username): - return - - if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): - outlist.append(itemdict) - return outlist - - @staticmethod - def _valid_github_response(jsonresp: str, username: str): if "message" in jsonresp.keys(): # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] @@ -347,6 +274,44 @@ class HacktoberStats(commands.Cog): return True + + if jsonresp["total_count"] == 0: + # Short circuit if there aren't any PRs + logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") + return + else: + # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") + outlist = [] + oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) + topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + for item in jsonresp["items"]: + shortname = HacktoberStats._get_shortname(item["repository_url"]) + itemdict = { + "repo_url": f"https://www.github.com/{shortname}", + "repo_shortname": shortname, + "created_at": datetime.strptime( + item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" + ), + } + # outlist.append(itemdict) + + async with aiohttp.ClientSession() as session: + async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: + jsonresp2 = await resp.json() + + if not _valid_github_response(jsonresp2, github_username): + return + + # PRs before oct 3 without invalid/spam willl count + if itemdict["created_at"] < oct3: + outlist.append(itemdict) + continue + # PRs after must be in repo with 'hacktoberfest' topic + # unless it has 'hacktoberfest-accepted' label + if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): + outlist.append(itemdict) + return outlist + @staticmethod def _get_shortname(in_url: str) -> str: """ -- cgit v1.2.3 From 2f13cc65d8ec14f40f9c8bbd52edddf91038b912 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Wed, 7 Oct 2020 11:01:22 +0000 Subject: almost finished --- bot/exts/halloween/hacktoberstats.py | 119 +++++++++++++---------------------- 1 file changed, 42 insertions(+), 77 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 2b46192f..be4534b4 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -29,6 +29,8 @@ GITHUB_NONEXISTENT_USER_MESSAGE = ( "or you do not have permission to view the users." ) +GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} + class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -239,7 +241,7 @@ class HacktoberStats(commands.Cog): action_type = "pr" is_query = "public" not_query = "draft" - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-10-03T23:59:59-11:00" + date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T00:00:00-11:00" per_page = "300" query_url = ( f"{base_url}" @@ -252,87 +254,12 @@ class HacktoberStats(commands.Cog): f"+created:{date_range}" f"&per_page={per_page}" ) - logging.debug(f"First GitHub query URL generated: {query_url}") + logging.debug(f"GitHub query URL generated: {query_url}") async with aiohttp.ClientSession() as session: async with session.get(query_url, headers=REQUEST_HEADERS) as resp: jsonresp = await resp.json() - if not _valid_github_response(jsonresp, github_username): - return - - else: - if jsonresp["total_count"] == 0: - # Short circuit if there aren't any PRs - logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") - return - else: - # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") - outlist = [] - for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) - itemdict = { - "repo_url": f"https://www.github.com/{shortname}", - "repo_shortname": shortname, - "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" - ), - } - outlist.append(itemdict) - # return outlist - - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T23:59:59-11:00" - query_url = ( - f"{base_url}" - f"-label:{not_labels[0]}" - f"+-label:{not_labels[1]}" - f"+type:{action_type}" - f"+is:{is_query}" - f"+author:{github_username}" - f"+-is:{not_query}" - f"+created:{date_range}" - f"&per_page={per_page}" - ) - logging.debug(f"Second GitHub query URL generated: {query_url}") - - async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=REQUEST_HEADERS) as resp: - jsonresp = await resp.json() - - if not _valid_github_response(jsonresp, github_username): - return - - else: - if jsonresp["total_count"] == 0: - # Short circuit if there aren't any PRs - logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") - return - else: - for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) - itemdict = { - "repo_url": f"https://www.github.com/{shortname}", - "repo_shortname": shortname, - "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" - ), - } - query_url = f"https://api.github.com/repos/{shortname}/topics" - accept_header = {"Accept": "application/vnd.github.mercy-preview+json"} - - async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=accept_header) as resp: - jsonresp2 = await resp.json() - - if not _valid_github_response(jsonresp2, github_username): - return - - if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): - outlist.append(itemdict) - return outlist - - @staticmethod - def _valid_github_response(jsonresp: str, username: str): if "message" in jsonresp.keys(): # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] @@ -347,6 +274,44 @@ class HacktoberStats(commands.Cog): return True + + if jsonresp["total_count"] == 0: + # Short circuit if there aren't any PRs + logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") + return + else: + # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") + outlist = [] + oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) + topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + for item in jsonresp["items"]: + shortname = HacktoberStats._get_shortname(item["repository_url"]) + itemdict = { + "repo_url": f"https://www.github.com/{shortname}", + "repo_shortname": shortname, + "created_at": datetime.strptime( + item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" + ), + } + # outlist.append(itemdict) + + async with aiohttp.ClientSession() as session: + async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: + jsonresp2 = await resp.json() + + if not _valid_github_response(jsonresp2, github_username): + return + + # PRs before oct 3 without invalid/spam willl count + if itemdict["created_at"] < oct3: + outlist.append(itemdict) + continue + # PRs after must be in repo with 'hacktoberfest' topic + # unless it has 'hacktoberfest-accepted' label + if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): + outlist.append(itemdict) + return outlist + @staticmethod def _get_shortname(in_url: str) -> str: """ -- cgit v1.2.3 From 8e8fce3692a9d6603cccdef7d77287c73485a91e Mon Sep 17 00:00:00 2001 From: Anubhav <57266248+Anubhav1603@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:09:31 +0530 Subject: Corrected doc string Co-authored-by: Rohan Reddy Alleti --- bot/exts/evergreen/emoji_count.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 365dc09d..b9838020 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -44,7 +44,7 @@ class EmojiCount(commands.Cog): @staticmethod def generate_invalid_embed(ctx: commands.Context) -> [discord.Embed, str]: - """Genrates error embed.""" + """Generates error embed.""" embed = discord.Embed( color=Colours.soft_red, title=random.choice(ERROR_REPLIES) -- cgit v1.2.3 From 7068babf1dae498b75b3b1390c1d635135747a84 Mon Sep 17 00:00:00 2001 From: Anubhav <57266248+Anubhav1603@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:13:30 +0530 Subject: Corrected msg Co-authored-by: Rohan Reddy Alleti --- bot/exts/evergreen/emoji_count.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index b9838020..da7a03ba 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -29,7 +29,7 @@ class EmojiCount(commands.Cog): if len(emoji) == 1: for key, value in emoji.items(): - msg.append(f"There are **{len(value)}** emojis in the **{key}** category") + msg.append(f"There is **{len(value)}** emoji in the **{key}** category") embed.set_thumbnail(url=random.choice(value).url) else: -- cgit v1.2.3 From 2ec7b2071f96b332619d8d829429374490e5ba11 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Wed, 7 Oct 2020 19:43:10 +0530 Subject: added category name and added check for no emoji --- bot/exts/evergreen/emoji_count.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index da7a03ba..82ee1798 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -18,7 +18,8 @@ class EmojiCount(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - def embed_builder(self, emoji: dict) -> [discord.Embed, str]: + @staticmethod + def embed_builder(emoji: dict) -> [discord.Embed, str]: """Generates an embed with the emoji names and count.""" embed = discord.Embed( color=Colours.orange, @@ -28,14 +29,14 @@ class EmojiCount(commands.Cog): msg = [] if len(emoji) == 1: - for key, value in emoji.items(): - msg.append(f"There is **{len(value)}** emoji in the **{key}** category") - embed.set_thumbnail(url=random.choice(value).url) + for category_name, category_emojis in emoji.items(): + msg.append(f"There is **{len(category_emojis)}** emoji in the **{category_name}** category") + embed.set_thumbnail(url=random.choice(category_emojis).url) else: - for key, value in emoji.items(): - emoji_choice = random.choice(value) - emoji_info = f'There are **{len(value)}** emojis in the **{key}** category\n' + for category_name, category_emojis in emoji.items(): + emoji_choice = random.choice(category_emojis) + emoji_info = f'There are **{len(category_emojis)}** emojis in the **{category_name}** category\n' if emoji_choice.animated: msg.append(f' {emoji_info}') else: @@ -55,7 +56,7 @@ class EmojiCount(commands.Cog): for emoji in ctx.guild.emojis: emoji_dict[emoji.name.split("_")[0]].append(emoji) - error_comp = ', '.join(key for key in emoji_dict) + error_comp = ', '.join(emoji_category for emoji_category in emoji_dict) msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg @@ -64,6 +65,9 @@ class EmojiCount(commands.Cog): """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) + if not ctx.guild.emojis: + await ctx.send("No emojis found.") + return log.trace(f"Emoji Category {'' if category_query else 'not '}provided by the user") for emoji in ctx.guild.emojis: emoji_category = emoji.name.split("_")[0] -- cgit v1.2.3 From b7bdf7a3782dac65da0fbfc47b4f0e786aac6950 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Wed, 7 Oct 2020 20:07:55 +0530 Subject: added typing.tuple --- bot/exts/evergreen/emoji_count.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 82ee1798..6b56afc3 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -2,6 +2,7 @@ import datetime import logging import random from collections import defaultdict +from typing import Tuple import discord from discord.ext import commands @@ -19,7 +20,7 @@ class EmojiCount(commands.Cog): self.bot = bot @staticmethod - def embed_builder(emoji: dict) -> [discord.Embed, str]: + def embed_builder(emoji: dict) -> Tuple[discord.Embed, str]: """Generates an embed with the emoji names and count.""" embed = discord.Embed( color=Colours.orange, @@ -44,7 +45,7 @@ class EmojiCount(commands.Cog): return embed, msg @staticmethod - def generate_invalid_embed(ctx: commands.Context) -> [discord.Embed, str]: + def generate_invalid_embed(ctx: commands.Context) -> Tuple[discord.Embed, str]: """Generates error embed.""" embed = discord.Embed( color=Colours.soft_red, @@ -56,7 +57,7 @@ class EmojiCount(commands.Cog): for emoji in ctx.guild.emojis: emoji_dict[emoji.name.split("_")[0]].append(emoji) - error_comp = ', '.join(emoji_category for emoji_category in emoji_dict) + error_comp = ', '.join(emoji_dict) msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg -- cgit v1.2.3 From f5d38198a70f36407c4b582de887fada854031e0 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Wed, 7 Oct 2020 20:10:14 +0530 Subject: added s in emoji_count --- bot/exts/evergreen/emoji_count.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 6b56afc3..fba7216c 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -61,8 +61,8 @@ class EmojiCount(commands.Cog): msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg - @commands.command(name="emoji_count", aliases=["ec"]) - async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: + @commands.command(name="emojis_count", aliases=["ec"]) + async def emojis_count(self, ctx: commands.Context, *, category_query: str = None) -> None: """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) -- cgit v1.2.3 From 5d247d4ebdd81d3e937cf395ddcba162b8b4c621 Mon Sep 17 00:00:00 2001 From: gustavwilliam <65498475+gustavwilliam@users.noreply.github.com> Date: Wed, 7 Oct 2020 16:48:43 +0200 Subject: Change call of `_get_random_dice` call to use self MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Considered best practice Co-authored-by: Leon Sandøy --- bot/exts/evergreen/fun.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 49c5d69e..231e6d54 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -67,7 +67,7 @@ class Fun(Cog): async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" if 1 <= num_rolls <= 6: - dice = " ".join(Fun._get_random_die() for _ in range(num_rolls)) + dice = " ".join(self._get_random_die() for _ in range(num_rolls)) await ctx.send(dice) else: raise BadArgument("`!roll` only supports between 1 and 6 rolls.") -- cgit v1.2.3 From 2847ec637cd7eec23d781692bc03b3323bfdf4ec Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 00:43:50 +0000 Subject: small fixes, still need testing --- bot/exts/halloween/hacktoberstats.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index be4534b4..76afde46 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -266,15 +266,14 @@ class HacktoberStats(commands.Cog): # Ignore logging non-existent users or users we do not have permission to see if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: - logging.debug(f"No GitHub user found named '{username}'") + logging.debug(f"No GitHub user found named '{github_username}'") return False else: - logging.error(f"GitHub API request for '{username}' failed with message: {api_message}") + logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") return False - + return True - if jsonresp["total_count"] == 0: # Short circuit if there aren't any PRs logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") @@ -283,7 +282,7 @@ class HacktoberStats(commands.Cog): # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") outlist = [] oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) - topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + for item in jsonresp["items"]: shortname = HacktoberStats._get_shortname(item["repository_url"]) itemdict = { @@ -293,25 +292,26 @@ class HacktoberStats(commands.Cog): item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" ), } - # outlist.append(itemdict) - - async with aiohttp.ClientSession() as session: - async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: - jsonresp2 = await resp.json() - - if not _valid_github_response(jsonresp2, github_username): - return - # PRs before oct 3 without invalid/spam willl count + # PRs before oct 3 without invalid/spam will count if itemdict["created_at"] < oct3: outlist.append(itemdict) continue + + topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + async with aiohttp.ClientSession() as session: + async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: + jsonresp2 = await resp.json() + + if not ("names" in jsonresp2.keys()): + log.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) + # PRs after must be in repo with 'hacktoberfest' topic # unless it has 'hacktoberfest-accepted' label if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): outlist.append(itemdict) return outlist - + @staticmethod def _get_shortname(in_url: str) -> str: """ -- cgit v1.2.3 From 1e12cc720b4db72a8b76f19f21456f10a6ef4f8e Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:31:46 +0000 Subject: fix for when pr has no labels --- bot/exts/halloween/hacktoberstats.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 50608d56..f4b8f51c 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -298,6 +298,7 @@ class HacktoberStats(commands.Cog): continue topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + log.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) async with aiohttp.ClientSession() as session: async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: jsonresp2 = await resp.json() @@ -307,6 +308,8 @@ class HacktoberStats(commands.Cog): # PRs after must be in repo with 'hacktoberfest' topic # unless it has 'hacktoberfest-accepted' label + if not ("labels" in jsonresp.keys()): + continue if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): outlist.append(itemdict) return outlist -- cgit v1.2.3 From 1f6d9fbf3535ca4a2c77f4a69f3211c7ec8072f3 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:34:13 +0000 Subject: return instead of return boolean --- bot/exts/halloween/hacktoberstats.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index f4b8f51c..fc66bfa8 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -267,12 +267,10 @@ class HacktoberStats(commands.Cog): # Ignore logging non-existent users or users we do not have permission to see if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: logging.debug(f"No GitHub user found named '{github_username}'") - return False + return else: logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") - return False - - return True + return if jsonresp["total_count"] == 0: # Short circuit if there aren't any PRs -- cgit v1.2.3 From ff9abdaad65000ef8b56ed21e58bca2ac227773e Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:36:35 +0000 Subject: refactor if else --- bot/exts/halloween/hacktoberstats.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index fc66bfa8..4f4ea122 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -267,10 +267,9 @@ class HacktoberStats(commands.Cog): # Ignore logging non-existent users or users we do not have permission to see if api_message == GITHUB_NONEXISTENT_USER_MESSAGE: logging.debug(f"No GitHub user found named '{github_username}'") - return else: logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") - return + return if jsonresp["total_count"] == 0: # Short circuit if there aren't any PRs -- cgit v1.2.3 From 9864520a4b91ab42a694f0db5a9d1589c90a5c7e Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:38:41 +0000 Subject: fix logging --- bot/exts/halloween/hacktoberstats.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 4f4ea122..2a81b91b 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -276,7 +276,7 @@ class HacktoberStats(commands.Cog): logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") return else: - # logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") + logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") outlist = [] oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) for item in jsonresp["items"]: @@ -295,13 +295,13 @@ class HacktoberStats(commands.Cog): continue topics_query_url = f"https://api.github.com/repos/{shortname}/topics" - log.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) + logging.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) async with aiohttp.ClientSession() as session: async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: jsonresp2 = await resp.json() if not ("names" in jsonresp2.keys()): - log.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) + logging.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) # PRs after must be in repo with 'hacktoberfest' topic # unless it has 'hacktoberfest-accepted' label -- cgit v1.2.3 From 3f14e0999bb8cde85db530fbb939e3355f9ff597 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:45:10 +0000 Subject: updated comments --- bot/exts/halloween/hacktoberstats.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 2a81b91b..c5c4b383 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -289,11 +289,12 @@ class HacktoberStats(commands.Cog): ), } - # PRs before oct 3 without invalid/spam will count + # PRs before oct 3 no need to check for topics if itemdict["created_at"] < oct3: outlist.append(itemdict) continue + # fetch topics for the pr repo topics_query_url = f"https://api.github.com/repos/{shortname}/topics" logging.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) async with aiohttp.ClientSession() as session: @@ -303,7 +304,7 @@ class HacktoberStats(commands.Cog): if not ("names" in jsonresp2.keys()): logging.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) - # PRs after must be in repo with 'hacktoberfest' topic + # PRs after oct 3 must be in repo with 'hacktoberfest' topic # unless it has 'hacktoberfest-accepted' label if not ("labels" in jsonresp.keys()): continue -- cgit v1.2.3 From 7092b8933968b439ae9ae406ae78d43fd7531cc9 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 03:54:24 +0000 Subject: refactor --- bot/exts/halloween/hacktoberstats.py | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index c5c4b383..2258dd70 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -275,42 +275,42 @@ class HacktoberStats(commands.Cog): # Short circuit if there aren't any PRs logging.info(f"No Hacktoberfest PRs found for GitHub user: '{github_username}'") return - else: - logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") - outlist = [] - oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) - for item in jsonresp["items"]: - shortname = HacktoberStats._get_shortname(item["repository_url"]) - itemdict = { - "repo_url": f"https://www.github.com/{shortname}", - "repo_shortname": shortname, - "created_at": datetime.strptime( - item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" - ), - } - # PRs before oct 3 no need to check for topics - if itemdict["created_at"] < oct3: - outlist.append(itemdict) - continue - - # fetch topics for the pr repo - topics_query_url = f"https://api.github.com/repos/{shortname}/topics" - logging.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) - async with aiohttp.ClientSession() as session: - async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: - jsonresp2 = await resp.json() - - if not ("names" in jsonresp2.keys()): - logging.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) - - # PRs after oct 3 must be in repo with 'hacktoberfest' topic - # unless it has 'hacktoberfest-accepted' label - if not ("labels" in jsonresp.keys()): - continue - if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): - outlist.append(itemdict) - return outlist + logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") + outlist = [] + oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) + for item in jsonresp["items"]: + shortname = HacktoberStats._get_shortname(item["repository_url"]) + itemdict = { + "repo_url": f"https://www.github.com/{shortname}", + "repo_shortname": shortname, + "created_at": datetime.strptime( + item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" + ), + } + + # PRs before oct 3 no need to check for topics + if itemdict["created_at"] < oct3: + outlist.append(itemdict) + continue + + # fetch topics for the pr repo + topics_query_url = f"https://api.github.com/repos/{shortname}/topics" + logging.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) + async with aiohttp.ClientSession() as session: + async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: + jsonresp2 = await resp.json() + + if not ("names" in jsonresp2.keys()): + logging.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) + + # PRs after oct 3 must be in repo with 'hacktoberfest' topic + # unless it has 'hacktoberfest-accepted' label + if not ("labels" in jsonresp.keys()): + continue + if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): + outlist.append(itemdict) + return outlist @staticmethod def _get_shortname(in_url: str) -> str: -- cgit v1.2.3 From 8994e4eb1e6d38fad14e8d7cd20c6545d3356954 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 08:15:01 +0000 Subject: update docstr and comments --- bot/exts/halloween/hacktoberstats.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 2258dd70..4f5436af 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -29,6 +29,7 @@ GITHUB_NONEXISTENT_USER_MESSAGE = ( "or you do not have permission to view the users." ) +# using repo topics API during preview period requires an accept header GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} @@ -166,6 +167,10 @@ class HacktoberStats(commands.Cog): PRs with an 'invalid' or 'spam' label are ignored + For PRs created after October 3rd, they have to be in a repository that has a + 'hacktoberfest' topic, unless the PR is labelled 'hacktoberfest-accepted' for it + to count. + If a valid github_username is provided, an embed is generated and posted to the channel Otherwise, post a helpful error message @@ -224,6 +229,10 @@ class HacktoberStats(commands.Cog): PRs with an 'invalid' or 'spam' label are ignored + For PRs created after October 3rd, they have to be in a repository that has a + 'hacktoberfest' topic, unless the PR is labelled 'hacktoberfest-accepted' for it + to count. + If PRs are found, return a list of dicts with basic PR information For each PR: @@ -277,7 +286,7 @@ class HacktoberStats(commands.Cog): return logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") - outlist = [] + outlist = [] # list of pr information dicts that will get returned oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) for item in jsonresp["items"]: shortname = HacktoberStats._get_shortname(item["repository_url"]) @@ -296,17 +305,17 @@ class HacktoberStats(commands.Cog): # fetch topics for the pr repo topics_query_url = f"https://api.github.com/repos/{shortname}/topics" - logging.debug("Fetching repo topics for " + shortname + " with url: " + topics_query_url) + logging.debug(f"Fetching repo topics for {shortname} with url: {topics_query_url}") async with aiohttp.ClientSession() as session: async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: jsonresp2 = await resp.json() if not ("names" in jsonresp2.keys()): - logging.error("Error fetching topics for " + shortname + ": " + jsonresp2["message"]) + logging.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") # PRs after oct 3 must be in repo with 'hacktoberfest' topic # unless it has 'hacktoberfest-accepted' label - if not ("labels" in jsonresp.keys()): + if not ("labels" in jsonresp.keys()): # if PR has no labels continue if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): outlist.append(itemdict) -- cgit v1.2.3 From 6a1ddc00d553074655613f0972815da3579f50e8 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 8 Oct 2020 08:36:37 +0000 Subject: fix topics and label logic --- bot/exts/halloween/hacktoberstats.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 4f5436af..ab12bf1e 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -299,7 +299,15 @@ class HacktoberStats(commands.Cog): } # PRs before oct 3 no need to check for topics - if itemdict["created_at"] < oct3: + # continue the loop if 'hacktoberfest-accepted' is labeled then + # there is no need to check for its topics + if (itemdict["created_at"] < oct3): + outlist.append(itemdict) + continue + if not ("labels" in item.keys()): # if PR has no labels + continue + # checking whether "hacktoberfest-accepted" is one of the PR's labels + if any(label["name"].casefold() == "hacktoberfest-accepted" for label in item["labels"]): outlist.append(itemdict) continue @@ -313,11 +321,9 @@ class HacktoberStats(commands.Cog): if not ("names" in jsonresp2.keys()): logging.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") - # PRs after oct 3 must be in repo with 'hacktoberfest' topic - # unless it has 'hacktoberfest-accepted' label - if not ("labels" in jsonresp.keys()): # if PR has no labels - continue - if ("hacktoberfest" in jsonresp2["names"]) or ("hacktoberfest-accpeted" in jsonresp["labels"]): + # PRs after oct 3 that doesn't have 'hacktoberfest-accepted' label + # must be in repo with 'hacktoberfest' topic + if "hacktoberfest" in jsonresp2["names"]: outlist.append(itemdict) return outlist -- cgit v1.2.3 From 08d064ee05f2fe30bf518ff599dfc2feca9a1aef Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 9 Oct 2020 08:52:40 +0000 Subject: update return type for `get_october_prs` it returns None when no PRs found, so I put that in `Union` with `List[dict]` --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index ab12bf1e..7db2d86a 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -223,7 +223,7 @@ class HacktoberStats(commands.Cog): return stats_embed @staticmethod - async def get_october_prs(github_username: str) -> List[dict]: + async def get_october_prs(github_username: str) -> Union[List[dict], None]: """ Query GitHub's API for PRs created during the month of October by github_username. -- cgit v1.2.3 From 8f43a0bd261e200e49562bed109eadf3219934f8 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 9 Oct 2020 08:52:40 +0000 Subject: update return type for `get_october_prs` it returns None when no PRs found, so I put that in `Union` with `List[dict]` --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index ab12bf1e..7db2d86a 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -223,7 +223,7 @@ class HacktoberStats(commands.Cog): return stats_embed @staticmethod - async def get_october_prs(github_username: str) -> List[dict]: + async def get_october_prs(github_username: str) -> Union[List[dict], None]: """ Query GitHub's API for PRs created during the month of October by github_username. -- cgit v1.2.3 From 6d79f76b57d9fab52a32c68c572db4f8a7175da1 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 9 Oct 2020 09:00:39 +0000 Subject: forgot to import --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 7db2d86a..e77a71f1 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -4,7 +4,7 @@ import re from collections import Counter from datetime import datetime from pathlib import Path -from typing import List, Tuple +from typing import List, Tuple, Union import aiohttp import discord -- cgit v1.2.3 From 8c88cbe2b361dbd0e983a04f0b5d85803066f326 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Fri, 9 Oct 2020 17:20:07 +0530 Subject: removed len,\n and now passing ctx.guild.emojis --- bot/exts/evergreen/emoji_count.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index fba7216c..0c18c9ee 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -2,7 +2,7 @@ import datetime import logging import random from collections import defaultdict -from typing import Tuple +from typing import List, Tuple import discord from discord.ext import commands @@ -20,7 +20,7 @@ class EmojiCount(commands.Cog): self.bot = bot @staticmethod - def embed_builder(emoji: dict) -> Tuple[discord.Embed, str]: + def embed_builder(emoji: dict) -> Tuple[discord.Embed, List[str]]: """Generates an embed with the emoji names and count.""" embed = discord.Embed( color=Colours.orange, @@ -37,7 +37,7 @@ class EmojiCount(commands.Cog): else: for category_name, category_emojis in emoji.items(): emoji_choice = random.choice(category_emojis) - emoji_info = f'There are **{len(category_emojis)}** emojis in the **{category_name}** category\n' + emoji_info = f'There are **{len(category_emojis)}** emojis in the **{category_name}** category' if emoji_choice.animated: msg.append(f' {emoji_info}') else: @@ -45,7 +45,7 @@ class EmojiCount(commands.Cog): return embed, msg @staticmethod - def generate_invalid_embed(ctx: commands.Context) -> Tuple[discord.Embed, str]: + def generate_invalid_embed(emojis: list) -> Tuple[discord.Embed, List[str]]: """Generates error embed.""" embed = discord.Embed( color=Colours.soft_red, @@ -54,15 +54,15 @@ class EmojiCount(commands.Cog): msg = [] emoji_dict = defaultdict(list) - for emoji in ctx.guild.emojis: + for emoji in emojis: emoji_dict[emoji.name.split("_")[0]].append(emoji) error_comp = ', '.join(emoji_dict) msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg - @commands.command(name="emojis_count", aliases=["ec"]) - async def emojis_count(self, ctx: commands.Context, *, category_query: str = None) -> None: + @commands.command(name="emoji_count", aliases=["ec", "emojis"]) + async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) @@ -78,9 +78,9 @@ class EmojiCount(commands.Cog): emoji_dict[emoji_category].append(emoji) - if len(emoji_dict) == 0: + if not emoji_dict: log.trace("Invalid name provided by the user") - embed, msg = self.generate_invalid_embed(ctx) + embed, msg = self.generate_invalid_embed(ctx.guild.emojis) else: embed, msg = self.embed_builder(emoji_dict) await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed) -- cgit v1.2.3 From 6c9162deecebb21f3925276a474d2e295877a665 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Fri, 9 Oct 2020 23:35:40 +0530 Subject: blog link shows the link itself --- bot/exts/evergreen/githubinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index f52454ba..b66512b3 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -57,9 +57,9 @@ class GithubInfo(commands.Cog): # Forming blog link if user_data['blog'].startswith("http"): # Blog link is complete - blog = f"[Direct link]({user_data['blog']})" + blog = user_data['blog'] elif user_data['blog']: # Blog exists but the link is not complete - blog = f"[Direct link](https://{user_data['blog']})" + blog = f"https://{user_data['blog']}" else: blog = "No blog link available" -- cgit v1.2.3 From a7c0531b20f6f9bd8bbc9c4436a6cb4e52336e0a Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Fri, 9 Oct 2020 23:50:25 +0530 Subject: organisation support --- bot/exts/evergreen/githubinfo.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index b66512b3..8183dd0c 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -73,20 +73,24 @@ class GithubInfo(commands.Cog): embed.set_thumbnail(url=user_data['avatar_url']) embed.set_footer(text="Account created at") - embed.add_field(name="Followers", value=f"""[{user_data['followers']}] - ({user_data['html_url']}?tab=followers)""") - embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Following", value=f"""[{user_data['following']}] + if user_data['type'] == "User": + + embed.add_field(name="Followers", value=f"""[{user_data['followers']}] + ({user_data['html_url']}?tab=followers)""") + embed.add_field(name="\u200b", value="\u200b") + embed.add_field(name="Following", value=f"""[{user_data['following']}] ({user_data['html_url']}?tab=following)""") embed.add_field(name="Public repos", value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)") embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]({user_data['html_url']}?tab=stars)") - embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", - value=orgs_to_add if orgs else "No organizations") - embed.add_field(name="\u200b", value="\u200b") + if user_data['type'] == "User": + embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]({user_data['html_url']}?tab=stars)") + + embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", + value=orgs_to_add if orgs else "No organizations") + embed.add_field(name="\u200b", value="\u200b") embed.add_field(name="Blog", value=blog) await ctx.send(embed=embed) -- cgit v1.2.3 From 7c6328a6dafa370cab7bcadba454953e50d82741 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Sat, 10 Oct 2020 12:00:59 +0530 Subject: cmd resets if username not given; blog renamed to website; replaced starred repo with gists --- bot/exts/evergreen/githubinfo.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 8183dd0c..b0e7c9f0 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -3,7 +3,6 @@ import random from datetime import datetime from typing import Optional -import aiohttp import discord from discord.ext import commands from discord.ext.commands.cooldowns import BucketType @@ -20,24 +19,22 @@ class GithubInfo(commands.Cog): self.bot = bot @staticmethod - async def fetch_data(url: str) -> dict: + async def fetch_data(self, url: str) -> dict: """Retrieve data as a dictionary.""" - async with aiohttp.ClientSession() as cs: - async with cs.get(url) as r: - return await r.json() + async with self.bot.http_session.get(url) as r: + return await r.json() @commands.command(name='github', aliases=['gh']) - @commands.cooldown(1, 5, BucketType.user) + @commands.cooldown(1, 60, BucketType.user) async def get_github_info(self, ctx: commands.Context, username: Optional[str]) -> None: """ Fetches a user's GitHub information. Username is optional and sends the help command if not specified. - - Usage: .gh [username] """ if username is None: await ctx.invoke(self.bot.get_command('help'), 'github') + ctx.command.reset_cooldown(ctx) return async with ctx.typing(): @@ -53,7 +50,7 @@ class GithubInfo(commands.Cog): orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] orgs_to_add = ' | '.join(orgs) - starred_data = await self.fetch_data(user_data['starred_url']) + gists_data = await self.fetch_data(f"https://api.github.com/users/{username}/gists") # Forming blog link if user_data['blog'].startswith("http"): # Blog link is complete @@ -61,7 +58,7 @@ class GithubInfo(commands.Cog): elif user_data['blog']: # Blog exists but the link is not complete blog = f"https://{user_data['blog']}" else: - blog = "No blog link available" + blog = "No website link available" embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", @@ -86,12 +83,12 @@ class GithubInfo(commands.Cog): embed.add_field(name="\u200b", value="\u200b") if user_data['type'] == "User": - embed.add_field(name="Starred repos", value=f"[{len(starred_data)}]({user_data['html_url']}?tab=stars)") + embed.add_field(name="Gists", value=f"[{len(gists_data)}](https://gist.github.com/{username})") embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", value=orgs_to_add if orgs else "No organizations") embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Blog", value=blog) + embed.add_field(name="Website", value=blog) await ctx.send(embed=embed) -- cgit v1.2.3 From f460a67d339df53163bb1fdc6a8d250a7d498c04 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Sat, 10 Oct 2020 23:44:38 +0530 Subject: followers md fix --- bot/exts/evergreen/githubinfo.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index b0e7c9f0..c514d200 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -18,7 +18,6 @@ class GithubInfo(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @staticmethod async def fetch_data(self, url: str) -> dict: """Retrieve data as a dictionary.""" async with self.bot.http_session.get(url) as r: @@ -43,6 +42,7 @@ class GithubInfo(commands.Cog): # User_data will not have a message key if the user exists if user_data.get('message') is not None: await ctx.send(embed=discord.Embed(title=random.choice(NEGATIVE_REPLIES), + description=f"The profile for `{username}` was not found.", colour=discord.Colour.red())) return @@ -72,11 +72,11 @@ class GithubInfo(commands.Cog): if user_data['type'] == "User": - embed.add_field(name="Followers", value=f"""[{user_data['followers']}] - ({user_data['html_url']}?tab=followers)""") + embed.add_field(name="Followers", + value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)") embed.add_field(name="\u200b", value="\u200b") - embed.add_field(name="Following", value=f"""[{user_data['following']}] - ({user_data['html_url']}?tab=following)""") + embed.add_field(name="Following", + value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)") embed.add_field(name="Public repos", value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)") -- cgit v1.2.3 From d2e2143a2c1345f93a494314b0a9cc954d1ca957 Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Sun, 11 Oct 2020 00:29:13 +0530 Subject: reduced num of api calls by 1 --- bot/exts/evergreen/githubinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index c514d200..19ec382b 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -50,7 +50,7 @@ class GithubInfo(commands.Cog): orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] orgs_to_add = ' | '.join(orgs) - gists_data = await self.fetch_data(f"https://api.github.com/users/{username}/gists") + gists = user_data['public_gists'] # Forming blog link if user_data['blog'].startswith("http"): # Blog link is complete @@ -83,7 +83,7 @@ class GithubInfo(commands.Cog): embed.add_field(name="\u200b", value="\u200b") if user_data['type'] == "User": - embed.add_field(name="Gists", value=f"[{len(gists_data)}](https://gist.github.com/{username})") + embed.add_field(name="Gists", value=f"[{len(gists)}](https://gist.github.com/{username})") embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", value=orgs_to_add if orgs else "No organizations") -- cgit v1.2.3 From d8b51b804b4d309dafc7ba6aaea7be62c64681be Mon Sep 17 00:00:00 2001 From: Lakshya Shastri Date: Sun, 11 Oct 2020 00:46:47 +0530 Subject: gists count fix --- bot/exts/evergreen/githubinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/githubinfo.py b/bot/exts/evergreen/githubinfo.py index 19ec382b..2e38e3ab 100644 --- a/bot/exts/evergreen/githubinfo.py +++ b/bot/exts/evergreen/githubinfo.py @@ -83,7 +83,7 @@ class GithubInfo(commands.Cog): embed.add_field(name="\u200b", value="\u200b") if user_data['type'] == "User": - embed.add_field(name="Gists", value=f"[{len(gists)}](https://gist.github.com/{username})") + embed.add_field(name="Gists", value=f"[{gists}](https://gist.github.com/{username})") embed.add_field(name=f"Organization{'s' if len(orgs)!=1 else ''}", value=orgs_to_add if orgs else "No organizations") -- cgit v1.2.3 From d1a5a420755945ec7f197c92f90a17ac331390e9 Mon Sep 17 00:00:00 2001 From: Anubhav <57266248+Anubhav1603@users.noreply.github.com> Date: Sun, 11 Oct 2020 18:18:00 +0530 Subject: Update bot/exts/evergreen/emoji_count.py Co-authored-by: Rohan Reddy Alleti --- bot/exts/evergreen/emoji_count.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 0c18c9ee..328ceaad 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -37,7 +37,10 @@ class EmojiCount(commands.Cog): else: for category_name, category_emojis in emoji.items(): emoji_choice = random.choice(category_emojis) - emoji_info = f'There are **{len(category_emojis)}** emojis in the **{category_name}** category' + emoji_info = ( + f"There {'is' if len(category_emojis) == 1 else 'are'} **{len(category_emojis)}** " + f"{'emoji' if len(category_emojis) == 1 else 'emojis'} in the **{category_name}** category" + ) if emoji_choice.animated: msg.append(f' {emoji_info}') else: -- cgit v1.2.3 From 8b06a8efe31bf05ed635f14f5dd37f104fb5fdd3 Mon Sep 17 00:00:00 2001 From: Anubhav <57266248+Anubhav1603@users.noreply.github.com> Date: Sun, 11 Oct 2020 18:18:15 +0530 Subject: Update bot/exts/evergreen/emoji_count.py Co-authored-by: Rohan Reddy Alleti --- bot/exts/evergreen/emoji_count.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 328ceaad..1e0cc54d 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -31,7 +31,10 @@ class EmojiCount(commands.Cog): if len(emoji) == 1: for category_name, category_emojis in emoji.items(): - msg.append(f"There is **{len(category_emojis)}** emoji in the **{category_name}** category") + msg.append( + f"There {'is' if len(category_emojis)==1 else 'are'} **{len(category_emojis)}** " + f"{'emoji' if len(category_emojis)==1 else 'emojis'} in the **{category_name}** category" + ) embed.set_thumbnail(url=random.choice(category_emojis).url) else: -- cgit v1.2.3 From 971bb3fe17d34a30cc9492e46d3992735c3cf276 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Sun, 11 Oct 2020 19:01:25 +0530 Subject: changed return --- bot/exts/evergreen/trivia_quiz.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 8dceceac..4dbb1c6a 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -121,8 +121,9 @@ class TriviaQuiz(commands.Cog): # A function to check whether user input is the correct answer(close to the right answer) def check(m: discord.Message) -> bool: - ratio = fuzz.ratio(answer.lower(), m.content.lower()) - return ratio > 85 and m.channel == ctx.channel + return ( + m.channel == ctx.channel and fuzz.ratio(answer.lower(), m.content.lower()) > 85 + ) try: msg = await self.bot.wait_for('message', check=check, timeout=10) -- cgit v1.2.3 From 78fcf48aebdfb2c53f99cb0c77759bd57d23fa40 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Mon, 12 Oct 2020 06:38:18 +0000 Subject: remove duplicates in winner list .riddle --- bot/exts/easter/easter_riddle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 8977534f..513215d1 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -67,7 +67,7 @@ class EasterRiddle(commands.Cog): await asyncio.sleep(TIMELIMIT) if self.winners: - win_list = " ".join(self.winners) + win_list = " ".join(set(self.winners)) # remove duplicated users if any content = f"Well done {win_list} for getting it right!" else: content = "Nobody got it right..." -- cgit v1.2.3 From 3c02761087e98cede89f330470002248e03d0d0b Mon Sep 17 00:00:00 2001 From: Daniel Brown Date: Mon, 12 Oct 2020 13:42:22 -0500 Subject: Adding #voice_chat to the Seasonal Bot white list Since SeasonalBot is able to be used in the off-topic channels, it makes to have it work in the voice_chat channel as well. --- bot/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index e113428e..1909cb86 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -75,6 +75,7 @@ class Channels(NamedTuple): show_your_projects = int(environ.get("CHANNEL_SHOW_YOUR_PROJECTS", 303934982764625920)) show_your_projects_discussion = 360148304664723466 hacktoberfest_2020 = 760857070781071431 + voice_chat = 412357430186344448 class Client(NamedTuple): @@ -209,6 +210,7 @@ WHITELISTED_CHANNELS = ( Channels.off_topic_0, Channels.off_topic_1, Channels.off_topic_2, + Channels.voice_chat, ) # Bot replies -- cgit v1.2.3 From 32fdc750cf35290256fd9a439f168eabb2743e72 Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Tue, 13 Oct 2020 13:37:16 +0530 Subject: improved readbility of code by adding line break --- bot/exts/evergreen/trivia_quiz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/trivia_quiz.py b/bot/exts/evergreen/trivia_quiz.py index 4dbb1c6a..fe692c2a 100644 --- a/bot/exts/evergreen/trivia_quiz.py +++ b/bot/exts/evergreen/trivia_quiz.py @@ -122,7 +122,8 @@ class TriviaQuiz(commands.Cog): # A function to check whether user input is the correct answer(close to the right answer) def check(m: discord.Message) -> bool: return ( - m.channel == ctx.channel and fuzz.ratio(answer.lower(), m.content.lower()) > 85 + m.channel == ctx.channel + and fuzz.ratio(answer.lower(), m.content.lower()) > 85 ) try: -- cgit v1.2.3 From 5824a34ff9d788d152ff53945e7ad17d9b44a7ae Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Tue, 13 Oct 2020 16:01:08 +0530 Subject: improved readbility of code by breaking one liner into if else --- bot/exts/evergreen/emoji_count.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 1e0cc54d..180e7132 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -31,19 +31,19 @@ class EmojiCount(commands.Cog): if len(emoji) == 1: for category_name, category_emojis in emoji.items(): - msg.append( - f"There {'is' if len(category_emojis)==1 else 'are'} **{len(category_emojis)}** " - f"{'emoji' if len(category_emojis)==1 else 'emojis'} in the **{category_name}** category" - ) + if len(category_emojis) == 1: + msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category") + else: + msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category") embed.set_thumbnail(url=random.choice(category_emojis).url) else: for category_name, category_emojis in emoji.items(): emoji_choice = random.choice(category_emojis) - emoji_info = ( - f"There {'is' if len(category_emojis) == 1 else 'are'} **{len(category_emojis)}** " - f"{'emoji' if len(category_emojis) == 1 else 'emojis'} in the **{category_name}** category" - ) + if len(category_emojis) > 1: + emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category" + else: + emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category" if emoji_choice.animated: msg.append(f' {emoji_info}') else: -- cgit v1.2.3 From 0ab3fb29ffdeb7c0b7b24bf4a62392428a8c344a Mon Sep 17 00:00:00 2001 From: Anubhav1603 Date: Wed, 14 Oct 2020 20:34:56 +0530 Subject: removed _ from command name --- bot/exts/evergreen/emoji_count.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji_count.py index 180e7132..cc43e9ab 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji_count.py @@ -67,7 +67,7 @@ class EmojiCount(commands.Cog): msg.append(f"These are the valid categories\n```{error_comp}```") return embed, msg - @commands.command(name="emoji_count", aliases=["ec", "emojis"]) + @commands.command(name="emojicount", aliases=["ec", "emojis"]) async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: """Returns embed with emoji category and info given by the user.""" emoji_dict = defaultdict(list) -- cgit v1.2.3 From be75e6c4d1d65b9b5710ec5fe08bf519a1cdda14 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 15 Oct 2020 07:58:19 +0800 Subject: update self.winners methods for a set() --- bot/exts/easter/easter_riddle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/easter/easter_riddle.py b/bot/exts/easter/easter_riddle.py index 513215d1..3c612eb1 100644 --- a/bot/exts/easter/easter_riddle.py +++ b/bot/exts/easter/easter_riddle.py @@ -22,7 +22,7 @@ class EasterRiddle(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - self.winners = [] + self.winners = set() self.correct = "" self.current_channel = None @@ -67,7 +67,7 @@ class EasterRiddle(commands.Cog): await asyncio.sleep(TIMELIMIT) if self.winners: - win_list = " ".join(set(self.winners)) # remove duplicated users if any + win_list = " ".join(self.winners) content = f"Well done {win_list} for getting it right!" else: content = "Nobody got it right..." @@ -79,7 +79,7 @@ class EasterRiddle(commands.Cog): await ctx.send(content, embed=answer_embed) - self.winners = [] + self.winners.clear() self.current_channel = None @commands.Cog.listener() @@ -92,7 +92,7 @@ class EasterRiddle(commands.Cog): return if message.content.lower() == self.correct.lower(): - self.winners.append(message.author.mention) + self.winners.add(message.author.mention) def setup(bot: commands.Bot) -> None: -- cgit v1.2.3 From 91717584325bfc7321cbeeba8908b7cf897ce668 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Thu, 15 Oct 2020 02:10:16 +0000 Subject: remove redundant set() in issues.py --- bot/exts/evergreen/issues.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/exts/evergreen/issues.py b/bot/exts/evergreen/issues.py index 5a5c82e7..97ee6a12 100644 --- a/bot/exts/evergreen/issues.py +++ b/bot/exts/evergreen/issues.py @@ -38,7 +38,7 @@ class Issues(commands.Cog): ) -> None: """Command to retrieve issue(s) from a GitHub repository.""" links = [] - numbers = set(numbers) + 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') @@ -53,8 +53,7 @@ class Issues(commands.Cog): await ctx.send(embed=embed) return - for number in set(numbers): - # Convert from list to set to remove duplicates, if any. + 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" -- cgit v1.2.3 From 42316a7a57de91a04fdd1edf145cfb81294aafb7 Mon Sep 17 00:00:00 2001 From: Daniel Brown Date: Thu, 15 Oct 2020 09:47:18 -0500 Subject: Added Core Dev Sprint to whitelist For the sake of entertainment of the Core Devs, I have added the SeasonalBot functionality to the Core Dev Sprint channels. --- bot/constants.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 1909cb86..2466da02 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -76,6 +76,23 @@ class Channels(NamedTuple): show_your_projects_discussion = 360148304664723466 hacktoberfest_2020 = 760857070781071431 voice_chat = 412357430186344448 + + # Core Dev Sprint channels + announcements = 755958119963557958 + information = 753338352136224798 + organisers = 753340132639375420 + general = 753340631538991305 + social1_cheese_shop = 758779754789863514 + social2_pet_shop = 758780951978573824 + escape_room = 761031075942105109 + stdlib = 758553316732698634 + asyncio = 762904152438472714 + typing = 762904690341838888 + discussion_capi = 758553358587527218 + discussion_triage = 758553458365300746 + discussion_design = 758553492662255616 + discussion_mentor = 758553536623280159 + documentation = 761038271127093278 class Client(NamedTuple): @@ -211,6 +228,23 @@ WHITELISTED_CHANNELS = ( Channels.off_topic_1, Channels.off_topic_2, Channels.voice_chat, + + # Core Dev Sprint Channels + Channels.announcements, + Channels.information, + Channels.organisers, + Channels.general, + Channels.social1_cheese_shop, + Channels.social2_pet_shop, + Channels.escape_room, + Channels.stdlib, + Channels.asyncio, + Channels.typing, + Channels.discussion_capi, + Channels.discussion_triage, + Channels.discussion_design, + Channels.discussion_mentor, + Channels.documentation, ) # Bot replies -- cgit v1.2.3 From 273a5170a42c119d74c337532e3ff8fe91a6f34c Mon Sep 17 00:00:00 2001 From: Daniel Brown Date: Thu, 15 Oct 2020 10:32:09 -0500 Subject: Corrected Trailing Whitespace Removed a trailing white space that caused a build failure. --- bot/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 2466da02..c5eed1f8 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -76,7 +76,7 @@ class Channels(NamedTuple): show_your_projects_discussion = 360148304664723466 hacktoberfest_2020 = 760857070781071431 voice_chat = 412357430186344448 - + # Core Dev Sprint channels announcements = 755958119963557958 information = 753338352136224798 @@ -228,7 +228,7 @@ WHITELISTED_CHANNELS = ( Channels.off_topic_1, Channels.off_topic_2, Channels.voice_chat, - + # Core Dev Sprint Channels Channels.announcements, Channels.information, -- cgit v1.2.3 From 725d3982128ae7da549b2a9aa1bfa5194393ba42 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 15 Oct 2020 20:17:49 +0200 Subject: Constants: rename conflicting channel There were two attributes named 'announcements' on the Channels class. --- bot/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index c5eed1f8..2056d542 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -78,7 +78,7 @@ class Channels(NamedTuple): voice_chat = 412357430186344448 # Core Dev Sprint channels - announcements = 755958119963557958 + sprint_announcements = 755958119963557958 information = 753338352136224798 organisers = 753340132639375420 general = 753340631538991305 @@ -230,7 +230,7 @@ WHITELISTED_CHANNELS = ( Channels.voice_chat, # Core Dev Sprint Channels - Channels.announcements, + Channels.sprint_announcements, Channels.information, Channels.organisers, Channels.general, -- cgit v1.2.3 From e67f4a063eb7c9926ed295a9c99394487d5ca633 Mon Sep 17 00:00:00 2001 From: kwzrd Date: Thu, 15 Oct 2020 20:28:38 +0200 Subject: Constants: prefix sprint channel names These are channels created for the CPython Core Dev Sprint event. --- bot/constants.py | 56 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 2056d542..f1f34886 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -79,20 +79,20 @@ class Channels(NamedTuple): # Core Dev Sprint channels sprint_announcements = 755958119963557958 - information = 753338352136224798 - organisers = 753340132639375420 - general = 753340631538991305 - social1_cheese_shop = 758779754789863514 - social2_pet_shop = 758780951978573824 - escape_room = 761031075942105109 - stdlib = 758553316732698634 - asyncio = 762904152438472714 - typing = 762904690341838888 - discussion_capi = 758553358587527218 - discussion_triage = 758553458365300746 - discussion_design = 758553492662255616 - discussion_mentor = 758553536623280159 - documentation = 761038271127093278 + sprint_information = 753338352136224798 + sprint_organisers = 753340132639375420 + sprint_general = 753340631538991305 + sprint_social1_cheese_shop = 758779754789863514 + sprint_social2_pet_shop = 758780951978573824 + sprint_escape_room = 761031075942105109 + sprint_stdlib = 758553316732698634 + sprint_asyncio = 762904152438472714 + sprint_typing = 762904690341838888 + sprint_discussion_capi = 758553358587527218 + sprint_discussion_triage = 758553458365300746 + sprint_discussion_design = 758553492662255616 + sprint_discussion_mentor = 758553536623280159 + sprint_documentation = 761038271127093278 class Client(NamedTuple): @@ -231,20 +231,20 @@ WHITELISTED_CHANNELS = ( # Core Dev Sprint Channels Channels.sprint_announcements, - Channels.information, - Channels.organisers, - Channels.general, - Channels.social1_cheese_shop, - Channels.social2_pet_shop, - Channels.escape_room, - Channels.stdlib, - Channels.asyncio, - Channels.typing, - Channels.discussion_capi, - Channels.discussion_triage, - Channels.discussion_design, - Channels.discussion_mentor, - Channels.documentation, + Channels.sprint_information, + Channels.sprint_organisers, + Channels.sprint_general, + Channels.sprint_social1_cheese_shop, + Channels.sprint_social2_pet_shop, + Channels.sprint_escape_room, + Channels.sprint_stdlib, + Channels.sprint_asyncio, + Channels.sprint_typing, + Channels.sprint_discussion_capi, + Channels.sprint_discussion_triage, + Channels.sprint_discussion_design, + Channels.sprint_discussion_mentor, + Channels.sprint_documentation, ) # Bot replies -- cgit v1.2.3 From 21c174768a570b09d948e98b7a377323597a7735 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 02:52:04 +0000 Subject: display in-review, accepted separately --- bot/exts/halloween/hacktoberstats.py | 70 +++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index e77a71f1..dae70e3d 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -1,8 +1,8 @@ import json import logging import re -from collections import Counter -from datetime import datetime +# from collections import Counter +from datetime import datetime, timedelta from pathlib import Path from typing import List, Tuple, Union @@ -32,6 +32,8 @@ GITHUB_NONEXISTENT_USER_MESSAGE = ( # using repo topics API during preview period requires an accept header GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} +REVIEW_DAYS = 14 + class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -187,9 +189,11 @@ class HacktoberStats(commands.Cog): def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") - pr_stats = self._summarize_prs(prs) + prs_dict = self._categorize_prs(prs) + accepted = prs_dict['accepted'] + in_review = prs_dict['in_review'] - n = pr_stats['n_prs'] + n = len(accepted) + len(in_review) if n >= PRS_FOR_SHIRT: shirtstr = f"**{github_username} has earned a T-shirt or a tree!**" elif n == PRS_FOR_SHIRT - 1: @@ -201,7 +205,7 @@ class HacktoberStats(commands.Cog): title=f"{github_username}'s Hacktoberfest", color=discord.Color(0x9c4af7), description=( - f"{github_username} has made {n} " + f"{github_username} has made {n} eligible" f"{HacktoberStats._contributionator(n)} in " f"October\n\n" f"{shirtstr}\n\n" @@ -215,8 +219,12 @@ class HacktoberStats(commands.Cog): icon_url="https://avatars1.githubusercontent.com/u/35706162?s=200&v=4" ) stats_embed.add_field( - name="Top 5 Repositories:", - value=self._build_top5str(pr_stats) + name="In Review", + value=self._build_prs_string(in_review, github_username) + ) + stats_embed.add_field( + name="Accepted", + value=self._build_prs_string(accepted, github_username) ) logging.info(f"Hacktoberfest PR built for GitHub user '{github_username}'") @@ -341,38 +349,42 @@ class HacktoberStats(commands.Cog): return re.findall(exp, in_url)[0] @staticmethod - def _summarize_prs(prs: List[dict]) -> dict: + def _categorize_prs(prs: List[dict]) -> dict: """ - Generate statistics from an input list of PR dictionaries, as output by get_october_prs. + Categorize PRs into 'in_review' and 'accepted'. - Return a dictionary containing: - { - "n_prs": int - "top5": [(repo_shortname, ncontributions), ...] - } + PRs created less than 14 days ago are 'in_review', PRs that are not + are 'accepted' (after 14 days review period). """ - contributed_repos = [pr["repo_shortname"] for pr in prs] - return {"n_prs": len(prs), "top5": Counter(contributed_repos).most_common(5)} + now = datetime.now() + in_review = [] + accepted = [] + for pr in prs: + if (pr['created_at'] + timedelta(REVIEW_DAYS)) < now: + in_review.append(pr) + else: + accepted.append(pr) + + out_dict = { + "in_review": in_review, + "accepted": accepted + } + return out_dict @staticmethod - def _build_top5str(stats: List[tuple]) -> str: + def _build_prs_string(prs: List[tuple], user: str) -> str: """ - Build a string from the Top 5 contributions that is compatible with a discord.Embed field. - - Top 5 contributions should be a list of tuples, as output in the stats dictionary by - _summarize_prs + Builds a discord embed compatible string for a list of PRs. - String is of the form: - n contribution(s) to [shortname](url) - ... + Repository name with the link to pull requests authored by 'user' for + each PR. """ base_url = "https://www.github.com/" - contributionstrs = [] - for repo in stats['top5']: - n = repo[1] - contributionstrs.append(f"{n} {HacktoberStats._contributionator(n)} to [{repo[0]}]({base_url}{repo[0]})") + str_list = [] + for pr in prs: + str_list.append(f"[{pr['repo_shortname']}]({base_url}{pr['repo_shortname']}/pulls/{user})") - return "\n".join(contributionstrs) + return "\n".join(str_list) @staticmethod def _contributionator(n: int) -> str: -- cgit v1.2.3 From 835b6ab8f0364a5e6cc550a1a8878d78549d4d26 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 04:40:32 +0000 Subject: add n prs count and max number --- bot/exts/halloween/hacktoberstats.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index dae70e3d..755888d7 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -1,7 +1,7 @@ import json import logging import re -# from collections import Counter +from collections import Counter from datetime import datetime, timedelta from pathlib import Path from typing import List, Tuple, Union @@ -193,9 +193,9 @@ class HacktoberStats(commands.Cog): accepted = prs_dict['accepted'] in_review = prs_dict['in_review'] - n = len(accepted) + len(in_review) + n = len(accepted) + len(in_review) # total number of PRs if n >= PRS_FOR_SHIRT: - shirtstr = f"**{github_username} has earned a T-shirt or a tree!**" + shirtstr = f"**{github_username} is eligible for a T-shirt or a tree!**" elif n == PRS_FOR_SHIRT - 1: shirtstr = f"**{github_username} is 1 PR away from a T-shirt or a tree!**" else: @@ -205,7 +205,7 @@ class HacktoberStats(commands.Cog): title=f"{github_username}'s Hacktoberfest", color=discord.Color(0x9c4af7), description=( - f"{github_username} has made {n} eligible" + f"{github_username} has made {n} valid " f"{HacktoberStats._contributionator(n)} in " f"October\n\n" f"{shirtstr}\n\n" @@ -381,8 +381,18 @@ class HacktoberStats(commands.Cog): """ base_url = "https://www.github.com/" str_list = [] - for pr in prs: - str_list.append(f"[{pr['repo_shortname']}]({base_url}{pr['repo_shortname']}/pulls/{user})") + repo_list = [pr["repo_shortname"] for pr in prs] + prs_list = Counter(repo_list).most_common(5) # get first 5 counted PRs + more = len(prs) - sum(i[1] for i in prs_list) + + for pr in prs_list: + # for example: https://www.github.com/python-discord/bot/pulls/octocat + # will display pull requests authored by octocat. + # pr[1] is the number of PRs to the repo + string = f"[{pr[0]}]({base_url}{pr[0]}/pulls/{user}) ({pr[1]})" + str_list.append(string) + if more: + str_list.append(f"...and {more} more") return "\n".join(str_list) -- cgit v1.2.3 From ad8a59a0e38d6e2a35552b7300a0880c739ab218 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 05:20:38 +0000 Subject: update calling static methods --- bot/exts/halloween/hacktoberstats.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 755888d7..ebce9c87 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -18,6 +18,7 @@ log = logging.getLogger(__name__) CURRENT_YEAR = datetime.now().year # Used to construct GH API query PRS_FOR_SHIRT = 4 # Minimum number of PRs before a shirt is awarded +REVIEW_DAYS = 14 # number of days needed after PR can be mature HACKTOBER_WHITELIST = WHITELISTED_CHANNELS + (Channels.hacktoberfest_2020,) REQUEST_HEADERS = {"User-Agent": "Python Discord Hacktoberbot"} @@ -32,8 +33,6 @@ GITHUB_NONEXISTENT_USER_MESSAGE = ( # using repo topics API during preview period requires an accept header GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} -REVIEW_DAYS = 14 - class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -55,7 +54,7 @@ class HacktoberStats(commands.Cog): get that user's contributions """ if not github_username: - author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) + author_id, author_mention = self._author_mention_from_context(ctx) if str(author_id) in self.linked_accounts.keys(): github_username = self.linked_accounts[author_id]["github_username"] @@ -86,7 +85,7 @@ class HacktoberStats(commands.Cog): } } """ - author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) + author_id, author_mention = self._author_mention_from_context(ctx) if github_username: if str(author_id) in self.linked_accounts.keys(): old_username = self.linked_accounts[author_id]["github_username"] @@ -111,7 +110,7 @@ class HacktoberStats(commands.Cog): @override_in_channel(HACKTOBER_WHITELIST) async def unlink_user(self, ctx: commands.Context) -> None: """Remove the invoking user's account link from the log.""" - author_id, author_mention = HacktoberStats._author_mention_from_context(ctx) + author_id, author_mention = self._author_mention_from_context(ctx) stored_user = self.linked_accounts.pop(author_id, None) if stored_user: @@ -206,7 +205,7 @@ class HacktoberStats(commands.Cog): color=discord.Color(0x9c4af7), description=( f"{github_username} has made {n} valid " - f"{HacktoberStats._contributionator(n)} in " + f"{self._contributionator(n)} in " f"October\n\n" f"{shirtstr}\n\n" ) -- cgit v1.2.3 From 2e049eec4bd7d18a317c0453600045f707a3db2d Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 05:24:11 +0000 Subject: handle no PR for a section --- bot/exts/halloween/hacktoberstats.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index ebce9c87..6843ded2 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -217,13 +217,17 @@ class HacktoberStats(commands.Cog): url="https://hacktoberfest.digitalocean.com", icon_url="https://avatars1.githubusercontent.com/u/35706162?s=200&v=4" ) + + # this will handle when no PRs in_review or accepted + review_str = self._build_prs_string(in_review, github_username) or "None" + accepted_str = self._build_prs_string(accepted, github_username) or "None" stats_embed.add_field( name="In Review", - value=self._build_prs_string(in_review, github_username) + value=review_str ) stats_embed.add_field( name="Accepted", - value=self._build_prs_string(accepted, github_username) + value=accepted_str ) logging.info(f"Hacktoberfest PR built for GitHub user '{github_username}'") -- cgit v1.2.3 From 608025d3e16846cc9192ef02d77bff466810f06a Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 08:05:33 +0000 Subject: minor design changes --- bot/exts/halloween/hacktoberstats.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 6843ded2..218505fb 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -222,11 +222,11 @@ class HacktoberStats(commands.Cog): review_str = self._build_prs_string(in_review, github_username) or "None" accepted_str = self._build_prs_string(accepted, github_username) or "None" stats_embed.add_field( - name="In Review", + name=":clock1: In Review", value=review_str ) stats_embed.add_field( - name="Accepted", + name=":tada: Accepted", value=accepted_str ) @@ -392,7 +392,7 @@ class HacktoberStats(commands.Cog): # for example: https://www.github.com/python-discord/bot/pulls/octocat # will display pull requests authored by octocat. # pr[1] is the number of PRs to the repo - string = f"[{pr[0]}]({base_url}{pr[0]}/pulls/{user}) ({pr[1]})" + string = f"{pr[1]} to [{pr[0]}]({base_url}{pr[0]}/pulls/{user})" str_list.append(string) if more: str_list.append(f"...and {more} more") -- cgit v1.2.3 From 747893309206ea397585eea27413a4f6b6bd0ff5 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 16 Oct 2020 09:59:37 +0000 Subject: fix in_review and accepted PRs swapped --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 218505fb..dbe83ad1 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -363,7 +363,7 @@ class HacktoberStats(commands.Cog): in_review = [] accepted = [] for pr in prs: - if (pr['created_at'] + timedelta(REVIEW_DAYS)) < now: + if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) else: accepted.append(pr) -- cgit v1.2.3 From e2dc935bef17e1cb8b361e30c052dba9ef76f995 Mon Sep 17 00:00:00 2001 From: Will Da Silva Date: Fri, 16 Oct 2020 15:04:49 -0400 Subject: Comply with TMDB API ToS --- bot/exts/evergreen/movie.py | 5 ++++- bot/exts/halloween/scarymovie.py | 3 ++- bot/exts/valentines/movie_generator.py | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/movie.py b/bot/exts/evergreen/movie.py index 93aeef30..340a5724 100644 --- a/bot/exts/evergreen/movie.py +++ b/bot/exts/evergreen/movie.py @@ -190,7 +190,10 @@ class Movie(Cog): async def get_embed(self, name: str) -> Embed: """Return embed of random movies. Uses name in title.""" - return Embed(title=f'Random {name} Movies').set_footer(text='Powered by TMDB (themoviedb.org)') + embed = Embed(title=f"Random {name} Movies") + embed.set_footer(text="This product uses the TMDb API but is not endorsed or certified by TMDb.") + embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") + return embed def setup(bot: Bot) -> None: diff --git a/bot/exts/halloween/scarymovie.py b/bot/exts/halloween/scarymovie.py index c80e0298..0807eca6 100644 --- a/bot/exts/halloween/scarymovie.py +++ b/bot/exts/halloween/scarymovie.py @@ -121,7 +121,8 @@ class ScaryMovie(commands.Cog): if value: embed.add_field(name=name, value=value) - embed.set_footer(text='powered by themoviedb.org') + embed.set_footer(text="This product uses the TMDb API but is not endorsed or certified by TMDb.") + embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") return embed diff --git a/bot/exts/valentines/movie_generator.py b/bot/exts/valentines/movie_generator.py index 0843175a..4df9e0d5 100644 --- a/bot/exts/valentines/movie_generator.py +++ b/bot/exts/valentines/movie_generator.py @@ -48,6 +48,8 @@ class RomanceMovieFinder(commands.Cog): embed.set_image(url=f"http://image.tmdb.org/t/p/w200/{selected_movie['poster_path']}") embed.add_field(name="Release date :clock1:", value=selected_movie["release_date"]) embed.add_field(name="Rating :star2:", value=selected_movie["vote_average"]) + embed.set_footer(text="This product uses the TMDb API but is not endorsed or certified by TMDb.") + embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") await ctx.send(embed=embed) except KeyError: warning_message = "A KeyError was raised while fetching information on the movie. The API service" \ -- cgit v1.2.3 From 2eb5da7c93886fde7d2979065006cb295cadd05e Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Sat, 17 Oct 2020 07:38:32 +0000 Subject: almost done, needs testing --- bot/exts/halloween/hacktoberstats.py | 118 +++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index dbe83ad1..b0b64be9 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -188,9 +188,7 @@ class HacktoberStats(commands.Cog): def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") - prs_dict = self._categorize_prs(prs) - accepted = prs_dict['accepted'] - in_review = prs_dict['in_review'] + in_review, accepted = self._categorize_prs(prs) n = len(accepted) + len(in_review) # total number of PRs if n >= PRS_FOR_SHIRT: @@ -238,7 +236,7 @@ class HacktoberStats(commands.Cog): """ Query GitHub's API for PRs created during the month of October by github_username. - PRs with an 'invalid' or 'spam' label are ignored + PRs with an 'invalid' or 'spam' label are ignored unless it is merged or approved For PRs created after October 3rd, they have to be in a repository that has a 'hacktoberfest' topic, unless the PR is labelled 'hacktoberfest-accepted' for it @@ -247,17 +245,17 @@ class HacktoberStats(commands.Cog): If PRs are found, return a list of dicts with basic PR information For each PR: - { + { "repo_url": str "repo_shortname": str (e.g. "python-discord/seasonalbot") "created_at": datetime.datetime - } + "number": int + } Otherwise, return None """ - logging.info(f"Generating Hacktoberfest PR query for GitHub user: '{github_username}'") + logging.info(f"Fetching Hacktoberfest Stats for GitHub user: '{github_username}'") base_url = "https://api.github.com/search/issues?q=" - not_labels = ("invalid", "spam") action_type = "pr" is_query = "public" not_query = "draft" @@ -265,8 +263,6 @@ class HacktoberStats(commands.Cog): per_page = "300" query_url = ( f"{base_url}" - f"-label:{not_labels[0]}" - f"+-label:{not_labels[1]}" f"+type:{action_type}" f"+is:{is_query}" f"+author:{github_username}" @@ -276,10 +272,7 @@ class HacktoberStats(commands.Cog): ) logging.debug(f"GitHub query URL generated: {query_url}") - async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=REQUEST_HEADERS) as resp: - jsonresp = await resp.json() - + jsonresp = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) if "message" in jsonresp.keys(): # One of the parameters is invalid, short circuit for now api_message = jsonresp["errors"][0]["message"] @@ -307,28 +300,31 @@ class HacktoberStats(commands.Cog): "created_at": datetime.strptime( item["created_at"], r"%Y-%m-%dT%H:%M:%SZ" ), + "number": item["number"] } + # if the PR has 'invalid' or 'spam' labels, the PR must be + # either merged or approved for it to be included + if HacktoberStats._has_label(item, ["invalid", "spam"]): + if not HacktoberStats._is_accepted(item): + continue + # PRs before oct 3 no need to check for topics - # continue the loop if 'hacktoberfest-accepted' is labeled then + # continue the loop if 'hacktoberfest-accepted' is labelled then # there is no need to check for its topics - if (itemdict["created_at"] < oct3): + if itemdict["created_at"] < oct3: outlist.append(itemdict) continue - if not ("labels" in item.keys()): # if PR has no labels - continue - # checking whether "hacktoberfest-accepted" is one of the PR's labels - if any(label["name"].casefold() == "hacktoberfest-accepted" for label in item["labels"]): + + # checking PR's labels for "hacktoberfest-accepted" + if HacktoberStats._has_label(item, "hacktoberfest-accepted"): outlist.append(itemdict) continue # fetch topics for the pr repo topics_query_url = f"https://api.github.com/repos/{shortname}/topics" logging.debug(f"Fetching repo topics for {shortname} with url: {topics_query_url}") - async with aiohttp.ClientSession() as session: - async with session.get(topics_query_url, headers=GITHUB_TOPICS_ACCEPT_HEADER) as resp: - jsonresp2 = await resp.json() - + jsonresp2 = await HacktoberStats._fetch_url(topics_query_url, GITHUB_TOPICS_ACCEPT_HEADER) if not ("names" in jsonresp2.keys()): logging.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") @@ -338,6 +334,65 @@ class HacktoberStats(commands.Cog): outlist.append(itemdict) return outlist + @staticmethod + async def _fetch_url(url: str, headers: dict) -> dict: + """Retrieve API response from URL.""" + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=headers) as resp: + jsonresp = await resp.json() + return jsonresp + + @staticmethod + def _has_label(pr: dict, labels: Union[List[str], str]) -> bool: + """ + Check if a PR has label 'labels'. + + 'labels' can be a string or a list of strings, if it's a list of strings + it will return true if any of the labels match. + """ + if not pr.get("labels"): # if PR has no labels + return False + if (isinstance(labels, str)) and (any(label["name"].casefold() == labels for label in pr["labels"])): + return True + for item in labels: + if any(label["name"].casefold() == item for label in pr["labels"]): + return True + return False + + @staticmethod + async def _is_accepted(pr: dict) -> bool: + """Check if a PR is merged, approved, or labelled hacktoberfest-accepted.""" + # checking for merge status + query_url = f"https://api.github.com/repos/{pr['repo_shortname']}/pulls" + query_url += pr["number"] + jsonresp = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) + + if "message" in jsonresp.keys(): + logging.error( + f"Error fetching PR stats for #{pr['number']} in repo {pr['repo_shortname']}:\n" + f"{jsonresp['message']}" + ) + return False + if ("merged" in jsonresp.keys()) and (jsonresp["merged"] == "true"): + return True + + # checking for the label, using `jsonresp` which has the label information + if HacktoberStats._has_label(jsonresp, "hacktoberfest-accepted"): + return True + + # checking approval + query_url += "/reviews" + jsonresp2 = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) + if "message" in jsonresp2.keys(): + logging.error( + f"Error fetching PR reviews for #{pr['number']} in repo {pr['repo_shortname']}:\n" + f"{jsonresp2['message']}" + ) + return False + if len(jsonresp2) == 0: # if PR has no reviews + return False + return any(item['status'] == "APPROVED" for item in jsonresp2) + @staticmethod def _get_shortname(in_url: str) -> str: """ @@ -352,12 +407,15 @@ class HacktoberStats(commands.Cog): return re.findall(exp, in_url)[0] @staticmethod - def _categorize_prs(prs: List[dict]) -> dict: + def _categorize_prs(prs: List[dict]) -> tuple: """ - Categorize PRs into 'in_review' and 'accepted'. + Categorize PRs into 'in_review' and 'accepted' and returns as a tuple. PRs created less than 14 days ago are 'in_review', PRs that are not are 'accepted' (after 14 days review period). + + PRs that are accepted must either be merged, approved, or labelled + 'hacktoberfest-accepted. """ now = datetime.now() in_review = [] @@ -365,14 +423,10 @@ class HacktoberStats(commands.Cog): for pr in prs: if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) - else: + elif HacktoberStats._is_accepted(pr): accepted.append(pr) - out_dict = { - "in_review": in_review, - "accepted": accepted - } - return out_dict + return in_review, accepted @staticmethod def _build_prs_string(prs: List[tuple], user: str) -> str: -- cgit v1.2.3 From 41a0b02d891930927cd475a01df4b4219cdb5ec3 Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Sat, 17 Oct 2020 17:52:36 +0800 Subject: async fixes --- bot/exts/halloween/hacktoberstats.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index b0b64be9..a05ea2f9 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -180,15 +180,15 @@ class HacktoberStats(commands.Cog): prs = await self.get_october_prs(github_username) if prs: - stats_embed = self.build_embed(github_username, prs) + stats_embed = await self.build_embed(github_username, prs) await ctx.send('Here are some stats!', embed=stats_embed) else: await ctx.send(f"No valid October GitHub contributions found for '{github_username}'") - def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: + async def build_embed(self, github_username: str, prs: List[dict]) -> discord.Embed: """Return a stats embed built from github_username's PRs.""" logging.info(f"Building Hacktoberfest embed for GitHub user: '{github_username}'") - in_review, accepted = self._categorize_prs(prs) + in_review, accepted = await self._categorize_prs(prs) n = len(accepted) + len(in_review) # total number of PRs if n >= PRS_FOR_SHIRT: @@ -306,7 +306,7 @@ class HacktoberStats(commands.Cog): # if the PR has 'invalid' or 'spam' labels, the PR must be # either merged or approved for it to be included if HacktoberStats._has_label(item, ["invalid", "spam"]): - if not HacktoberStats._is_accepted(item): + if not await HacktoberStats._is_accepted(item): continue # PRs before oct 3 no need to check for topics @@ -363,8 +363,8 @@ class HacktoberStats(commands.Cog): async def _is_accepted(pr: dict) -> bool: """Check if a PR is merged, approved, or labelled hacktoberfest-accepted.""" # checking for merge status - query_url = f"https://api.github.com/repos/{pr['repo_shortname']}/pulls" - query_url += pr["number"] + query_url = f"https://api.github.com/repos/{pr['repo_shortname']}/pulls/" + query_url += str(pr["number"]) jsonresp = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) if "message" in jsonresp.keys(): @@ -407,7 +407,7 @@ class HacktoberStats(commands.Cog): return re.findall(exp, in_url)[0] @staticmethod - def _categorize_prs(prs: List[dict]) -> tuple: + async def _categorize_prs(prs: List[dict]) -> tuple: """ Categorize PRs into 'in_review' and 'accepted' and returns as a tuple. @@ -423,7 +423,7 @@ class HacktoberStats(commands.Cog): for pr in prs: if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) - elif HacktoberStats._is_accepted(pr): + elif await HacktoberStats._is_accepted(pr): accepted.append(pr) return in_review, accepted -- cgit v1.2.3 From 87f6969d0a3190b0b95846c7bbb808054ba17ead Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Sun, 18 Oct 2020 05:17:46 +0000 Subject: fix some bugs and allow topics caching --- bot/exts/halloween/hacktoberstats.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index a05ea2f9..94bfe138 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -22,17 +22,17 @@ REVIEW_DAYS = 14 # number of days needed after PR can be mature HACKTOBER_WHITELIST = WHITELISTED_CHANNELS + (Channels.hacktoberfest_2020,) REQUEST_HEADERS = {"User-Agent": "Python Discord Hacktoberbot"} +# using repo topics API during preview period requires an accept header +GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} if GITHUB_TOKEN := Tokens.github: REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" + GITHUB_TOPICS_ACCEPT_HEADER["Authorization"] = f"token {GITHUB_TOKEN}" GITHUB_NONEXISTENT_USER_MESSAGE = ( "The listed users cannot be searched either because the users do not exist " "or you do not have permission to view the users." ) -# using repo topics API during preview period requires an accept header -GITHUB_TOPICS_ACCEPT_HEADER = {"Accept": "application/vnd.github.mercy-preview+json"} - class HacktoberStats(commands.Cog): """Hacktoberfest statistics Cog.""" @@ -292,6 +292,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") outlist = [] # list of pr information dicts that will get returned oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) + hackto_topics = {} # cache whether each repo has the appropriate topic (bool values) for item in jsonresp["items"]: shortname = HacktoberStats._get_shortname(item["repository_url"]) itemdict = { @@ -321,16 +322,23 @@ class HacktoberStats(commands.Cog): outlist.append(itemdict) continue + # no need to query github if repo topics are fetched before already + if shortname in hackto_topics.keys(): + if hackto_topics[shortname]: + outlist.append(itemdict) + continue # fetch topics for the pr repo topics_query_url = f"https://api.github.com/repos/{shortname}/topics" logging.debug(f"Fetching repo topics for {shortname} with url: {topics_query_url}") jsonresp2 = await HacktoberStats._fetch_url(topics_query_url, GITHUB_TOPICS_ACCEPT_HEADER) - if not ("names" in jsonresp2.keys()): + if jsonresp2.get("names") is None: logging.error(f"Error fetching topics for {shortname}: {jsonresp2['message']}") + return # PRs after oct 3 that doesn't have 'hacktoberfest-accepted' label # must be in repo with 'hacktoberfest' topic if "hacktoberfest" in jsonresp2["names"]: + hackto_topics[shortname] = True # cache result in the dict for later use if needed outlist.append(itemdict) return outlist @@ -373,7 +381,7 @@ class HacktoberStats(commands.Cog): f"{jsonresp['message']}" ) return False - if ("merged" in jsonresp.keys()) and (jsonresp["merged"] == "true"): + if ("merged" in jsonresp.keys()) and jsonresp["merged"]: return True # checking for the label, using `jsonresp` which has the label information @@ -383,15 +391,23 @@ class HacktoberStats(commands.Cog): # checking approval query_url += "/reviews" jsonresp2 = await HacktoberStats._fetch_url(query_url, REQUEST_HEADERS) - if "message" in jsonresp2.keys(): + if isinstance(jsonresp2, dict): + # if API request is unsuccessful it will be a dict with the error in 'message' logging.error( f"Error fetching PR reviews for #{pr['number']} in repo {pr['repo_shortname']}:\n" f"{jsonresp2['message']}" ) return False + # if it is successful it will be a list instead of a dict if len(jsonresp2) == 0: # if PR has no reviews return False - return any(item['status'] == "APPROVED" for item in jsonresp2) + + # loop through reviews and check for approval + for item in jsonresp2: + if "status" in item.keys(): + if item['status'] == "APPROVED": + return True + return False @staticmethod def _get_shortname(in_url: str) -> str: -- cgit v1.2.3 From 0813c581a1edc741ea09747940e80d893afa04c8 Mon Sep 17 00:00:00 2001 From: Gustav Odinger Date: Sun, 18 Oct 2020 17:38:11 +0200 Subject: Change error msg for roll to use correct prefix - Previously used `!` as the prefix, while `.` is the correct one - Now imports prefix from bot.constants, so it'll always be up to date --- bot/exts/evergreen/fun.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 231e6d54..101725da 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -10,7 +10,7 @@ from discord.ext import commands from discord.ext.commands import BadArgument, Bot, Cog, Context, MessageConverter, clean_content from bot import utils -from bot.constants import Colours, Emojis +from bot.constants import Client, Colours, Emojis log = logging.getLogger(__name__) @@ -70,7 +70,7 @@ class Fun(Cog): dice = " ".join(self._get_random_die() for _ in range(num_rolls)) await ctx.send(dice) else: - raise BadArgument("`!roll` only supports between 1 and 6 rolls.") + raise BadArgument(f"`{Client.prefix}roll` only supports between 1 and 6 rolls.") @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) async def uwu_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: -- cgit v1.2.3 -- cgit v1.2.3 From e374391631d1c2698bc777f5dc3b2076ed0d0eca Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Wed, 21 Oct 2020 11:11:48 +0000 Subject: fix keyerror --- bot/exts/halloween/hacktoberstats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 94bfe138..c872e241 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -307,7 +307,7 @@ class HacktoberStats(commands.Cog): # if the PR has 'invalid' or 'spam' labels, the PR must be # either merged or approved for it to be included if HacktoberStats._has_label(item, ["invalid", "spam"]): - if not await HacktoberStats._is_accepted(item): + if not await HacktoberStats._is_accepted(itemdict): continue # PRs before oct 3 no need to check for topics -- cgit v1.2.3 From e79fa1f5fe782e494b8120e4ce97361609c9413b Mon Sep 17 00:00:00 2001 From: Dennis Pham Date: Thu, 22 Oct 2020 10:50:27 -0400 Subject: Update Python Discord badge to 100k members. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fc04cbb..1486283f 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # SeasonalBot [![Build Status](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Seasonal%20Bot%20(Mainline))](https://dev.azure.com/python-discord/Python%20Discord/_build/latest?definitionId=3) -[![Discord](https://img.shields.io/static/v1?label=Python%20Discord&logo=discord&message=%3E60k%20members&color=%237289DA&logoColor=white)](https://discord.gg/2B963hn) +[![Discord](https://img.shields.io/static/v1?label=Python%20Discord&logo=discord&message=%3E100k%20members&color=%237289DA&logoColor=white)](https://discord.gg/2B963hn) A Discord bot for the Python Discord community which changes with the seasons, and provides useful event features. -- cgit v1.2.3 From 93168514ef59009377aa091161d78bfb2a1aa8ca Mon Sep 17 00:00:00 2001 From: Hedy Li Date: Fri, 23 Oct 2020 08:30:25 +0000 Subject: fix time and date specific things --- bot/exts/halloween/hacktoberstats.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index 94bfe138..0b75ca91 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -259,7 +259,7 @@ class HacktoberStats(commands.Cog): action_type = "pr" is_query = "public" not_query = "draft" - date_range = f"{CURRENT_YEAR}-10-01T00:00:00%2B14:00..{CURRENT_YEAR}-11-01T00:00:00-11:00" + date_range = f"{CURRENT_YEAR}-09-30T10:00Z..{CURRENT_YEAR}-11-01T12:00Z" per_page = "300" query_url = ( f"{base_url}" @@ -291,7 +291,7 @@ class HacktoberStats(commands.Cog): logging.info(f"Found {len(jsonresp['items'])} Hacktoberfest PRs for GitHub user: '{github_username}'") outlist = [] # list of pr information dicts that will get returned - oct3 = datetime(int(CURRENT_YEAR), 10, 3, 0, 0, 0) + oct3 = datetime(int(CURRENT_YEAR), 10, 3, 23, 59, 59, tzinfo=None) hackto_topics = {} # cache whether each repo has the appropriate topic (bool values) for item in jsonresp["items"]: shortname = HacktoberStats._get_shortname(item["repository_url"]) @@ -434,12 +434,13 @@ class HacktoberStats(commands.Cog): 'hacktoberfest-accepted. """ now = datetime.now() + oct3 = datetime(CURRENT_YEAR, 10, 3, 23, 59, 59, tzinfo=None) in_review = [] accepted = [] for pr in prs: if (pr['created_at'] + timedelta(REVIEW_DAYS)) > now: in_review.append(pr) - elif await HacktoberStats._is_accepted(pr): + elif (pr['created_at'] <= oct3) or await HacktoberStats._is_accepted(pr): accepted.append(pr) return in_review, accepted -- cgit v1.2.3