diff options
author | 2018-11-27 21:01:18 +0100 | |
---|---|---|
committer | 2018-11-27 21:01:18 +0100 | |
commit | 70d2170a0a6594561d59c7d080c4280f1ebcd70b (patch) | |
tree | 3eb52bc45b91f3de1cc51a0cb2e710685009dd4d /bot/cogs/hacktober/candy_collection.py | |
parent | Merge pull request #69 from python-discord/issue/68-with-typing (diff) |
Allows you to create Seasons. (#64)
* Allows you to create Season objects which change the bots behavior. For example, a season can determine things like the avatar, the nickname, and which cogs are loaded. Season automatically changes according to the date range you specify when you create it.
* removing some hungarian notation.
* Automatic season changes will now always happen at a minute past midnight, no matter when the bot started.
* catching dunders in the glob.
* Refine Season Creation behaviour and structure.
* Added channel and role constants, refactored roles into NamedTuples, added role check decorators from the main bot, and added role checks for the season change feature. Yes this is duplicate code from our main bot, but it will just have to be like that for now until we get a bot core running.
* replacing the or with an xor and switching out the assert for a UserWarning
* New lockfile
* changing discord.py to discord-py to prevent pip bug from putting two of them in our lockfile
* fixing flake errors
* flake8
* Cleaned everything up, but I seem to have introduced some sort of infinite load loop? o.O
* Fixing up all bugs in the halloween cogs. This should be ready for merge now.
* Add avatar_path baseclass method for consistency.
While making it simpler to add avatar urls in new season extensions, it also allows the avatar resource path to be changed in a single place if needed in future.
* Avoid shadowing builtin `object`.
* Add debug mode, refine bot user editing on season load.
The changing of a bot's username and avatar is heavily ratelimited. While testing, restarting the bot and changing seasons is required, and hitting these limits are typical. Instead, when in debug mode, the avatar isn't set and only the nickname is changed to prevent unnecessary account edit requests.
In the case that the rate limit is hit when not in debug mode, there's an added fallback to use the nickname instead.
* Add cancel load_seasons task on SeasonManager un/reload.
Previously the load_seasons task was loaded and looping forever. Even if the cog was unloaded for some reason, it would still be running. On loading the SeasonManager again, it would create a new load_seasons task, while the old one still existed.
Adding the cancellation allows the task to end when the cog is unloaded or reloaded, and will help assist with live code changes during development at a later time where it's possible to reload this extension (perhaps when the pending bot core is implemented).
* get_season_class helper, season class attribs, fix admin id
Changes `get_season`'s date check to not initialise unwanted classes (to avoid needless loading of tasks which would otherwise cause unexpected behaviour). To do this, defining attributes of season classes have been moved from `__init__` as an instance variable, to the class variable level. This also results in `__init__` not needing to be defined for the `SeasonBase` class, and `super().__init__()` not needing to be called in individual season classes, making things cleaner/simpler for them.
Adds a helper function for retrieving a season class and combines two unnecessarily separate if statements. Credits to @MarkKoz for the suggestions.
Reverts the admin ID mistakenly changed in a previous commit.
* Update bot/seasons/halloween/hacktoberstats.py
Co-Authored-By: heavysaturn <[email protected]>
* Update bot/seasons/halloween/halloween_facts.py
Co-Authored-By: heavysaturn <[email protected]>
* No more property in halloweenfacts
* Changed all aliases to tuples
* Made tokens a seperate namedtuple
* Update bot/seasons/halloween/spookyavatar.py
Co-Authored-By: heavysaturn <[email protected]>
Diffstat (limited to 'bot/cogs/hacktober/candy_collection.py')
-rw-r--r-- | bot/cogs/hacktober/candy_collection.py | 229 |
1 files changed, 0 insertions, 229 deletions
diff --git a/bot/cogs/hacktober/candy_collection.py b/bot/cogs/hacktober/candy_collection.py deleted file mode 100644 index f5f17abb..00000000 --- a/bot/cogs/hacktober/candy_collection.py +++ /dev/null @@ -1,229 +0,0 @@ -import functools -import json -import os -import random - -import discord -from discord.ext import commands - -from bot.constants import HACKTOBER_CHANNEL_ID - -json_location = os.path.join("bot", "resources", "halloween", "candy_collection.json") - -# chance is 1 in x range, so 1 in 20 range would give 5% chance (for add candy) -ADD_CANDY_REACTION_CHANCE = 20 # 5% -ADD_CANDY_EXISTING_REACTION_CHANCE = 10 # 10% -ADD_SKULL_REACTION_CHANCE = 50 # 2% -ADD_SKULL_EXISTING_REACTION_CHANCE = 20 # 5% - - -class CandyCollection: - def __init__(self, bot): - self.bot = bot - with open(json_location) as candy: - self.candy_json = json.load(candy) - self.msg_reacted = self.candy_json['msg_reacted'] - self.get_candyinfo = dict() - for userinfo in self.candy_json['records']: - userid = userinfo['userid'] - self.get_candyinfo[userid] = userinfo - - async def on_message(self, message): - """ - Randomly adds candy or skull to certain messages - """ - - # make sure its a human message - if message.author.bot: - return - # ensure it's hacktober channel - if message.channel.id != HACKTOBER_CHANNEL_ID: - return - - # do random check for skull first as it has the lower chance - if random.randint(1, ADD_SKULL_REACTION_CHANCE) == 1: - d = {"reaction": '\N{SKULL}', "msg_id": message.id, "won": False} - self.msg_reacted.append(d) - return await message.add_reaction('\N{SKULL}') - # check for the candy chance next - if random.randint(1, ADD_CANDY_REACTION_CHANCE) == 1: - d = {"reaction": '\N{CANDY}', "msg_id": message.id, "won": False} - self.msg_reacted.append(d) - return await message.add_reaction('\N{CANDY}') - - async def on_reaction_add(self, reaction, user): - """ - Add/remove candies from a person if the reaction satisfies criteria - """ - - message = reaction.message - # check to ensure the reactor is human - if user.bot: - return - # check to ensure it is in correct channel - if message.channel.id != HACKTOBER_CHANNEL_ID: - return - - # if its not a candy or skull, and it is one of 10 most recent messages, - # proceed to add a skull/candy with higher chance - if str(reaction.emoji) not in ('\N{SKULL}', '\N{CANDY}'): - if message.id in await self.ten_recent_msg(): - await self.reacted_msg_chance(message) - return - - for react in self.msg_reacted: - # check to see if the message id of a message we added a - # reaction to is in json file, and if nobody has won/claimed it yet - if react['msg_id'] == message.id and react['won'] is False: - react['user_reacted'] = user.id - react['won'] = True - try: - # if they have record/candies in json already it will do this - user_records = self.get_candyinfo[user.id] - if str(reaction.emoji) == '\N{CANDY}': - user_records['record'] += 1 - if str(reaction.emoji) == '\N{SKULL}': - if user_records['record'] <= 3: - user_records['record'] = 0 - lost = 'all of your' - else: - lost = random.randint(1, 3) - user_records['record'] -= lost - await self.send_spook_msg(message.author, message.channel, lost) - - except KeyError: - # otherwise it will raise KeyError so we need to add them to file - if str(reaction.emoji) == '\N{CANDY}': - print('ok') - d = {"userid": user.id, "record": 1} - self.candy_json['records'].append(d) - await self.remove_reactions(reaction) - - async def reacted_msg_chance(self, message): - """ - Randomly add a skull or candy to a message if there is a reaction there already - (higher probability) - """ - - if random.randint(1, ADD_SKULL_EXISTING_REACTION_CHANCE) == 1: - d = {"reaction": '\N{SKULL}', "msg_id": message.id, "won": False} - self.msg_reacted.append(d) - return await message.add_reaction('\N{SKULL}') - - if random.randint(1, ADD_CANDY_EXISTING_REACTION_CHANCE) == 1: - d = {"reaction": '\N{CANDY}', "msg_id": message.id, "won": False} - self.msg_reacted.append(d) - return await message.add_reaction('\N{CANDY}') - - async def ten_recent_msg(self): - """Get the last 10 messages sent in the channel""" - ten_recent = [] - recent_msg = max(message.id for message - in self.bot._connection._messages - if message.channel.id == self.HACKTOBER_CHANNEL_ID) - - channel = await self.hacktober_channel() - ten_recent.append(recent_msg.id) - - for i in range(9): - o = discord.Object(id=recent_msg.id + i) - msg = await next(channel.history(limit=1, before=o)) - ten_recent.append(msg.id) - - return ten_recent - - async def get_message(self, msg_id): - """ - Get the message from it's ID. - """ - - try: - o = discord.Object(id=msg_id + 1) - # Use history rather than get_message due to - # poor ratelimit (50/1s vs 1/1s) - msg = await next(self.hacktober_channel.history(limit=1, before=o)) - - if msg.id != msg_id: - return None - - return msg - - except Exception: - return None - - async def hacktober_channel(self): - """ - Get #hacktoberbot channel from it's id - """ - return self.bot.get_channel(id=HACKTOBER_CHANNEL_ID) - - async def remove_reactions(self, reaction): - """ - Remove all candy/skull reactions - """ - - try: - async for user in reaction.users(): - await reaction.message.remove_reaction(reaction.emoji, user) - - except discord.HTTPException: - pass - - async def send_spook_msg(self, author, channel, candies): - """ - Send a spooky message - """ - e = discord.Embed(colour=author.colour) - e.set_author(name="Ghosts and Ghouls and Jack o' lanterns at night; " - f"I took {candies} candies and quickly took flight.") - await channel.send(embed=e) - - def save_to_json(self): - """ - Save json to the file. - """ - with open(json_location, 'w') as outfile: - json.dump(self.candy_json, outfile) - - @commands.command() - async def candy(self, ctx): - """ - Get the candy leaderboard and save to json when this is called - """ - - # use run_in_executor to prevent blocking - thing = functools.partial(self.save_to_json) - await self.bot.loop.run_in_executor(None, thing) - - emoji = ( - '\N{FIRST PLACE MEDAL}', - '\N{SECOND PLACE MEDAL}', - '\N{THIRD PLACE MEDAL}', - '\N{SPORTS MEDAL}', - '\N{SPORTS MEDAL}' - ) - - top_sorted = sorted(self.candy_json['records'], key=lambda k: k.get('record', 0), reverse=True) - top_five = top_sorted[:5] - - usersid = [] - records = [] - for record in top_five: - usersid.append(record['userid']) - records.append(record['record']) - - value = '\n'.join(f'{emoji[index]} <@{usersid[index]}>: {records[index]}' - for index in range(0, len(usersid))) or 'No Candies' - - e = discord.Embed(colour=discord.Colour.blurple()) - e.add_field(name="Top Candy Records", value=value, inline=False) - e.add_field(name='\u200b', - value=f"Candies will randomly appear on messages sent. " - f"\nHit the candy when it appears as fast as possible to get the candy! " - f"\nBut beware the ghosts...", - inline=False) - await ctx.send(embed=e) - - -def setup(bot): - bot.add_cog(CandyCollection(bot)) |