diff options
author | 2020-10-02 17:08:38 +0200 | |
---|---|---|
committer | 2020-10-02 17:08:38 +0200 | |
commit | 427821dfd5c272bfd8b42fab630661be2df27ee8 (patch) | |
tree | 3b25d4e94caf908201d685c8c3a1298bb4822006 | |
parent | linting smh (diff) | |
parent | Authenticate GitHub API requests for the Hacktoberfest issue finder. (diff) |
Merge branch 'master' into master
-rw-r--r-- | bot/constants.py | 7 | ||||
-rw-r--r-- | bot/exts/halloween/hacktober-issue-finder.py | 12 | ||||
-rw-r--r-- | bot/exts/halloween/hacktoberstats.py | 39 | ||||
-rw-r--r-- | bot/exts/halloween/spookysound.py | 48 | ||||
-rw-r--r-- | bot/exts/halloween/timeleft.py | 32 |
5 files changed, 58 insertions, 80 deletions
diff --git a/bot/constants.py b/bot/constants.py index 935b90e0..7ec8ac27 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -11,7 +11,6 @@ __all__ = ( "Client", "Colours", "Emojis", - "Hacktoberfest", "Icons", "Lovefest", "Month", @@ -75,7 +74,7 @@ class Channels(NamedTuple): python_discussion = 267624335836053506 show_your_projects = int(environ.get("CHANNEL_SHOW_YOUR_PROJECTS", 303934982764625920)) show_your_projects_discussion = 360148304664723466 - hacktoberfest_2019 = 628184417646411776 + hacktoberfest_2020 = 760857070781071431 class Client(NamedTuple): @@ -129,10 +128,6 @@ class Emojis: status_offline = "<:status_offline:470326266537705472>" -class Hacktoberfest(NamedTuple): - voice_id = 514420006474219521 - - class Icons: questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png" bookmark = ( diff --git a/bot/exts/halloween/hacktober-issue-finder.py b/bot/exts/halloween/hacktober-issue-finder.py index b5ad1c4f..78acf391 100644 --- a/bot/exts/halloween/hacktober-issue-finder.py +++ b/bot/exts/halloween/hacktober-issue-finder.py @@ -7,13 +7,19 @@ import aiohttp import discord from discord.ext import commands -from bot.constants import Month +from bot.constants import Month, Tokens from bot.utils.decorators import in_month log = logging.getLogger(__name__) URL = "https://api.github.com/search/issues?per_page=100&q=is:issue+label:hacktoberfest+language:python+state:open" -HEADERS = {"Accept": "application / vnd.github.v3 + json"} + +REQUEST_HEADERS = { + "User-Agent": "Python Discord Hacktoberbot", + "Accept": "application / vnd.github.v3 + json" +} +if GITHUB_TOKEN := Tokens.github: + REQUEST_HEADERS["Authorization"] = f"token {GITHUB_TOKEN}" class HacktoberIssues(commands.Cog): @@ -66,7 +72,7 @@ class HacktoberIssues(commands.Cog): url += f"&page={page}" log.debug(f"making api request to url: {url}") - async with session.get(url, headers=HEADERS) as response: + async with session.get(url, headers=REQUEST_HEADERS) as response: if response.status != 200: log.error(f"expected 200 status (got {response.status}) from the GitHub api.") await ctx.send(f"ERROR: expected 200 status (got {response.status}) from the GitHub api.") diff --git a/bot/exts/halloween/hacktoberstats.py b/bot/exts/halloween/hacktoberstats.py index db5e37f2..ed1755e3 100644 --- a/bot/exts/halloween/hacktoberstats.py +++ b/bot/exts/halloween/hacktoberstats.py @@ -10,7 +10,7 @@ import aiohttp import discord from discord.ext import commands -from bot.constants import Channels, Month, WHITELISTED_CHANNELS +from bot.constants import Channels, Month, Tokens, WHITELISTED_CHANNELS from bot.utils.decorators import in_month, override_in_channel from bot.utils.persist import make_persistent @@ -18,7 +18,16 @@ 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 -HACKTOBER_WHITELIST = WHITELISTED_CHANNELS + (Channels.hacktoberfest_2019,) +HACKTOBER_WHITELIST = WHITELISTED_CHANNELS + (Channels.hacktoberfest_2020,) + +REQUEST_HEADERS = {"User-Agent": "Python Discord Hacktoberbot"} +if GITHUB_TOKEN := Tokens.github: + REQUEST_HEADERS["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." +) class HacktoberStats(commands.Cog): @@ -29,7 +38,7 @@ class HacktoberStats(commands.Cog): self.link_json = make_persistent(Path("bot", "resources", "halloween", "github_links.json")) self.linked_accounts = self.load_linked_users() - @in_month(Month.OCTOBER) + @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @commands.group(name="hacktoberstats", aliases=("hackstats",), invoke_without_command=True) @override_in_channel(HACKTOBER_WHITELIST) async def hacktoberstats_group(self, ctx: commands.Context, github_username: str = None) -> None: @@ -57,7 +66,7 @@ class HacktoberStats(commands.Cog): await self.get_stats(ctx, github_username) - @in_month(Month.OCTOBER) + @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @hacktoberstats_group.command(name="link") @override_in_channel(HACKTOBER_WHITELIST) async def link_user(self, ctx: commands.Context, github_username: str = None) -> None: @@ -92,7 +101,7 @@ class HacktoberStats(commands.Cog): logging.info(f"{author_id} tried to link a GitHub account but didn't provide a username") await ctx.send(f"{author_mention}, a GitHub username is required to link your account") - @in_month(Month.OCTOBER) + @in_month(Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER) @hacktoberstats_group.command(name="unlink") @override_in_channel(HACKTOBER_WHITELIST) async def unlink_user(self, ctx: commands.Context) -> None: @@ -175,11 +184,11 @@ class HacktoberStats(commands.Cog): n = pr_stats['n_prs'] if n >= PRS_FOR_SHIRT: - shirtstr = f"**{github_username} has earned a tshirt!**" + shirtstr = f"**{github_username} has earned a T-shirt or a tree!**" elif n == PRS_FOR_SHIRT - 1: - shirtstr = f"**{github_username} is 1 PR away from a tshirt!**" + shirtstr = f"**{github_username} is 1 PR away from a T-shirt or a tree!**" else: - shirtstr = f"**{github_username} is {PRS_FOR_SHIRT - n} PRs away from a tshirt!**" + shirtstr = f"**{github_username} is {PRS_FOR_SHIRT - n} PRs away from a T-shirt or a tree!**" stats_embed = discord.Embed( title=f"{github_username}'s Hacktoberfest", @@ -196,7 +205,7 @@ class HacktoberStats(commands.Cog): stats_embed.set_author( name="Hacktoberfest", url="https://hacktoberfest.digitalocean.com", - icon_url="https://hacktoberfest.digitalocean.com/pretty_logo.png" + icon_url="https://avatars1.githubusercontent.com/u/35706162?s=200&v=4" ) stats_embed.add_field( name="Top 5 Repositories:", @@ -242,16 +251,22 @@ class HacktoberStats(commands.Cog): f"&per_page={per_page}" ) - headers = {"user-agent": "Discord Python Hacktoberbot"} async with aiohttp.ClientSession() as session: - async with session.get(query_url, headers=headers) as resp: + 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"] - logging.error(f"GitHub API request for '{github_username}' failed with message: {api_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 '{github_username}'") + else: + logging.error(f"GitHub API request for '{github_username}' failed with message: {api_message}") + return + else: if jsonresp["total_count"] == 0: # Short circuit if there aren't any PRs diff --git a/bot/exts/halloween/spookysound.py b/bot/exts/halloween/spookysound.py deleted file mode 100644 index 569a9153..00000000 --- a/bot/exts/halloween/spookysound.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging -import random -from pathlib import Path - -import discord -from discord.ext import commands - -from bot.bot import SeasonalBot -from bot.constants import Hacktoberfest - -log = logging.getLogger(__name__) - - -class SpookySound(commands.Cog): - """A cog that plays a spooky sound in a voice channel on command.""" - - def __init__(self, bot: SeasonalBot): - self.bot = bot - self.sound_files = list(Path("bot/resources/halloween/spookysounds").glob("*.mp3")) - self.channel = None - - @commands.cooldown(rate=1, per=1) - @commands.command(brief="Play a spooky sound, restricted to once per 2 mins") - async def spookysound(self, ctx: commands.Context) -> None: - """ - Connect to the Hacktoberbot voice channel, play a random spooky sound, then disconnect. - - Cannot be used more than once in 2 minutes. - """ - if not self.channel: - await self.bot.wait_until_guild_available() - self.channel = self.bot.get_channel(Hacktoberfest.voice_id) - - await ctx.send("Initiating spooky sound...") - file_path = random.choice(self.sound_files) - src = discord.FFmpegPCMAudio(str(file_path.resolve())) - voice = await self.channel.connect() - voice.play(src, after=lambda e: self.bot.loop.create_task(self.disconnect(voice))) - - @staticmethod - async def disconnect(voice: discord.VoiceClient) -> None: - """Helper method to disconnect a given voice client.""" - await voice.disconnect() - - -def setup(bot: SeasonalBot) -> None: - """Spooky sound Cog load.""" - bot.add_cog(SpookySound(bot)) diff --git a/bot/exts/halloween/timeleft.py b/bot/exts/halloween/timeleft.py index 295acc89..47adb09b 100644 --- a/bot/exts/halloween/timeleft.py +++ b/bot/exts/halloween/timeleft.py @@ -13,20 +13,23 @@ class TimeLeft(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - @staticmethod - def in_october() -> bool: - """Return True if the current month is October.""" - return datetime.utcnow().month == 10 + def in_hacktober(self) -> bool: + """Return True if the current time is within Hacktoberfest.""" + _, end, start = self.load_date() + + now = datetime.utcnow() + + return start <= now <= end @staticmethod - def load_date() -> Tuple[int, datetime, datetime]: + def load_date() -> Tuple[datetime, datetime, datetime]: """Return of a tuple of the current time and the end and start times of the next October.""" now = datetime.utcnow() year = now.year if now.month > 10: year += 1 - end = datetime(year, 11, 1, 11, 59, 59) - start = datetime(year, 10, 1) + end = datetime(year, 11, 1, 12) # November 1st 12:00 (UTC-12:00) + start = datetime(year, 9, 30, 10) # September 30th 10:00 (UTC+14:00) return now, end, start @commands.command() @@ -35,16 +38,23 @@ class TimeLeft(commands.Cog): Calculates the time left until the end of Hacktober. Whilst in October, displays the days, hours and minutes left. - Only displays the days left until the beginning and end whilst in a different month + Only displays the days left until the beginning and end whilst in a different month. + + This factors in that Hacktoberfest starts when it is October anywhere in the world + and ends with the same rules. It treats the start as UTC+14:00 and the end as + UTC-12. """ now, end, start = self.load_date() diff = end - now days, seconds = diff.days, diff.seconds - if self.in_october(): + if self.in_hacktober(): minutes = seconds // 60 hours, minutes = divmod(minutes, 60) - await ctx.send(f"There is currently only {days} days, {hours} hours and {minutes}" - "minutes left until the end of Hacktober.") + + await ctx.send( + f"There are {days} days, {hours} hours and {minutes}" + f" minutes left until the end of Hacktober." + ) else: start_diff = start - now start_days = start_diff.days |