From c7742a8023b3269f97d3981cbb4f5244e6838645 Mon Sep 17 00:00:00 2001 From: phxntxm Date: Sat, 22 Sep 2018 11:17:11 -0500 Subject: [PATCH] Move tasks into their own methods, to try to gc better --- cogs/birthday.py | 65 ++++++++++++++++---------- cogs/picarto.py | 110 +++++++++++++++++++++---------------------- cogs/raffle.py | 3 +- cogs/spotify.py | 16 ++++++- cogs/twitch.py | 118 +++++++++++++++++++++++------------------------ 5 files changed, 172 insertions(+), 140 deletions(-) diff --git a/cogs/birthday.py b/cogs/birthday.py index 460b255..093996e 100644 --- a/cogs/birthday.py +++ b/cogs/birthday.py @@ -1,35 +1,45 @@ import discord import pendulum import asyncio +import traceback from pendulum.parsing.exceptions import ParserError from discord.ext import commands from . import utils +tzmap = { + 'us-central': pendulum.timezone('US/Central'), + 'eu-central': pendulum.timezone('Europe/Paris'), + 'hongkong': pendulum.timezone('Hongkong'), + +} + + +def sort_birthdays(bds): + # First sort the birthdays based on the comparison of the actual date + bds = sorted(bds, key=lambda x: x['birthday']) + # We want to split this into birthdays after and before todays date + # We can then use this to sort based on "whose is closest" + later_bds = [] + previous_bds = [] + # Loop through each birthday + for bd in bds: + # If it is after or equal to today, insert into our later list + if bd['birthday'].date() >= pendulum.today().date(): + later_bds.append(bd) + # Otherwise, insert into our previous list + else: + previous_bds.append(bd) + # At this point we have 2 lists, in order, one from all of dates before today, and one after + # So all we need to do is put them in order all of "laters" then all of "befores" + return later_bds + previous_bds + + class Birthday: def __init__(self, bot): self.bot = bot - self.bot.loop.create_task(self.birthday_task()) - - def sort_birthdays(self, bds): - # First sort the birthdays based on the comparison of the actual date - bds = sorted(bds, key=lambda x: x['birthday']) - # We want to split this into birthdays after and before todays date - # We can then use this to sort based on "whose is closest" - later_bds = [] - previous_bds = [] - # Loop through each birthday - for bd in bds: - # If it is after or equal to today, insert into our later list - if bd['birthday'].date() >= pendulum.today().date(): - later_bds.append(bd) - # Otherwise, insert into our previous list - else: - previous_bds.append(bd) - # At this point we have 2 lists, in order, one from all of dates before today, and one after - # So all we need to do is put them in order all of "laters" then all of "befores" - return later_bds + previous_bds + self.task = self.bot.loop.create_task(self.birthday_task()) def get_birthdays_for_server(self, server, today=False): bds = self.bot.db.load('birthdays') @@ -46,6 +56,7 @@ class Birthday: continue day = pendulum.parse(bd['birthday']) + # tz = tzmap.get(server.region) # Check if it's today, and we want to only get todays birthdays if (today and day.date() == pendulum.today().date()) or not today: # If so, get the member and add them to the entry @@ -55,13 +66,19 @@ class Birthday: 'member': member }) - return self.sort_birthdays(_entries) + return sort_birthdays(_entries) async def birthday_task(self): while True: - await self.notify_birthdays() - # Every 12 hours, this is not something that needs to happen often - await asyncio.sleep(60 * 60 * 12) + try: + await self.notify_birthdays() + except Exception as error: + with open("error_log", 'a') as f: + traceback.print_tb(error.__traceback__, file=f) + print('{0.__class__.__name__}: {0}'.format(error), file=f) + finally: + # Every 12 hours, this is not something that needs to happen often + await asyncio.sleep(60 * 60 * 12) async def notify_birthdays(self): tfilter = {'birthdays_allowed': True} diff --git a/cogs/picarto.py b/cogs/picarto.py index 0c88f93..02c4e82 100644 --- a/cogs/picarto.py +++ b/cogs/picarto.py @@ -2,20 +2,18 @@ import asyncio import discord import re import traceback -import logging from discord.ext import commands from . import utils -log = logging.getLogger() BASE_URL = 'https://api.picarto.tv/v1' class Picarto: def __init__(self, bot): self.bot = bot - self.bot.loop.create_task(self.check_channels()) + self.task = self.bot.loop.create_task(self.picarto_task()) # noinspection PyAttributeOutsideInit async def get_online_users(self): @@ -69,64 +67,66 @@ class Picarto: channel = re.search("(?<=picarto.tv/)(.*)", channel).group(1) return channel.lower() in [stream['name'].lower() for stream in self.online_channels] - async def check_channels(self): - await self.bot.wait_until_ready() - # This is a loop that runs every 30 seconds, checking if anyone has gone online + async def picarto_task(self): try: + await self.bot.wait_until_ready() while not self.bot.is_closed(): - await self.get_online_users() - picarto = await self.bot.db.actual_load('picarto', table_filter={'notifications_on': 1}) - for data in picarto: - m_id = int(data['member_id']) - url = data['picarto_url'] - # Check if they are online - online = self.channel_online(url) - # If they're currently online, but saved as not then we'll let servers know they are now online - if online and data['live'] == 0: - msg = "{member.display_name} has just gone live!" - self.bot.db.save('picarto', {'live': 1, 'member_id': str(m_id)}) - # Otherwise our notification will say they've gone offline - elif not online and data['live'] == 1: - msg = "{member.display_name} has just gone offline!" - self.bot.db.save('picarto', {'live': 0, 'member_id': str(m_id)}) - else: - continue + await self.check_channels() + except Exception as error: + with open("error_log", 'a') as f: + traceback.print_tb(error.__traceback__, file=f) + print('{0.__class__.__name__}: {0}'.format(error), file=f) + finally: + await asyncio.sleep(30) - embed = await self.channel_embed(url) - # Loop through each server that they are set to notify - for s_id in data['servers']: - server = self.bot.get_guild(int(s_id)) - # If we can't find it, ignore this one - if server is None: - continue - member = server.get_member(m_id) - # If we can't find them in this server, also ignore - if member is None: - continue + async def check_channels(self): + await self.get_online_users() + picarto = await self.bot.db.actual_load('picarto', table_filter={'notifications_on': 1}) + for data in picarto: + m_id = int(data['member_id']) + url = data['picarto_url'] + # Check if they are online + online = self.channel_online(url) + # If they're currently online, but saved as not then we'll let servers know they are now online + if online and data['live'] == 0: + msg = "{member.display_name} has just gone live!" + self.bot.db.save('picarto', {'live': 1, 'member_id': str(m_id)}) + # Otherwise our notification will say they've gone offline + elif not online and data['live'] == 1: + msg = "{member.display_name} has just gone offline!" + self.bot.db.save('picarto', {'live': 0, 'member_id': str(m_id)}) + else: + continue - # Get the notifications settings, get the picarto setting - notifications = self.bot.db.load('server_settings', key=s_id, pluck='notifications') or {} - # Set our default to either the one set, or the default channel of the server - default_channel_id = notifications.get('default') - # If it is has been overriden by picarto notifications setting, use this - channel_id = notifications.get('picarto') or default_channel_id - # Now get the channel - if channel_id: - channel = server.get_channel(int(channel_id)) - else: - continue + embed = await self.channel_embed(url) + # Loop through each server that they are set to notify + for s_id in data['servers']: + server = self.bot.get_guild(int(s_id)) + # If we can't find it, ignore this one + if server is None: + continue + member = server.get_member(m_id) + # If we can't find them in this server, also ignore + if member is None: + continue - # Then just send our message - try: - await channel.send(msg.format(member=member), embed=embed) - except (discord.Forbidden, discord.HTTPException, AttributeError): - pass + # Get the notifications settings, get the picarto setting + notifications = self.bot.db.load('server_settings', key=s_id, pluck='notifications') or {} + # Set our default to either the one set, or the default channel of the server + default_channel_id = notifications.get('default') + # If it is has been overriden by picarto notifications setting, use this + channel_id = notifications.get('picarto') or default_channel_id + # Now get the channel + if channel_id: + channel = server.get_channel(int(channel_id)) + else: + continue - await asyncio.sleep(30) - except Exception as e: - tb = traceback.format_exc() - fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e) - log.error(fmt) + # Then just send our message + try: + await channel.send(msg.format(member=member), embed=embed) + except (discord.Forbidden, discord.HTTPException, AttributeError): + pass @commands.group(invoke_without_command=True) @utils.custom_perms(send_messages=True) diff --git a/cogs/raffle.py b/cogs/raffle.py index b68ebee..37361c4 100644 --- a/cogs/raffle.py +++ b/cogs/raffle.py @@ -23,7 +23,8 @@ class Raffle: with open("error_log", 'a') as f: traceback.print_tb(error.__traceback__, file=f) print('{0.__class__.__name__}: {0}'.format(error), file=f) - await asyncio.sleep(60) + finally: + await asyncio.sleep(60) async def check_raffles(self): # This is used to periodically check the current raffles, and see if they have ended yet diff --git a/cogs/spotify.py b/cogs/spotify.py index e085876..34b748c 100644 --- a/cogs/spotify.py +++ b/cogs/spotify.py @@ -1,5 +1,6 @@ import asyncio import aiohttp +import traceback from discord.ext import commands from base64 import urlsafe_b64encode @@ -20,9 +21,21 @@ class Spotify: urlsafe_b64encode(self._authorization.encode()).decode() ) } - self.bot.loop.create_task(self.api_token_task()) + self.task = self.bot.loop.create_task(self.api_token_task()) async def api_token_task(self): + while True: + try: + delay = await self.get_api_token() + except Exception as error: + with open("error_log", 'a') as f: + traceback.print_tb(error.__traceback__, file=f) + print('{0.__class__.__name__}: {0}'.format(error), file=f) + delay = 2400 + finally: + await asyncio.sleep(delay) + + async def get_api_token(self): url = "https://accounts.spotify.com/api/token" opts = {"grant_type": "client_credentials"} while True: @@ -30,6 +43,7 @@ class Spotify: response = await session.post(url, data=opts) data = await response.json() self._token = data.get("access_token") + return data.get("expires_in") await asyncio.sleep(data.get("expires_in", 2400)) diff --git a/cogs/twitch.py b/cogs/twitch.py index c4b2d60..eedf394 100644 --- a/cogs/twitch.py +++ b/cogs/twitch.py @@ -2,11 +2,9 @@ from discord.ext import commands from . import utils -import aiohttp import asyncio import discord import re -import rethinkdb as r import traceback import logging @@ -23,6 +21,8 @@ class Twitch: self.key = utils.twitch_key self.params = {'client_id': self.key} + self.task = bot.loop.create_task(self.check_channels()) + def _form_embed(self, data): if not data: return None @@ -82,63 +82,65 @@ class Twitch: data = await utils.request(url, payload=self.params) return self._form_embed(data) - async def check_channels(self): + async def twitch_task(self): await self.bot.wait_until_ready() - # This is a loop that runs every 30 seconds, checking if anyone has gone online - try: - while not self.bot.is_closed(): - twitch = await self.bot.db.actual_load('twitch', table_filter={'notifications_on': 1}) - for data in twitch: - m_id = int(data['member_id']) - url = data['twitch_url'] - # Check if they are online by trying to get an displayed embed for this user - embed = await self.online_embed(url) - # If they're currently online, but saved as not then we'll let servers know they are now online - if embed and data['live'] == 0: - msg = "{member.display_name} has just gone live!" - self.bot.db.save('twitch', {'live': 1, 'member_id': str(m_id)}) - # Otherwise our notification will say they've gone offline - elif not embed and data['live'] == 1: - msg = "{member.display_name} has just gone offline!" - embed = await self.offline_embed(url) - self.bot.db.save('twitch', {'live': 0, 'member_id': str(m_id)}) - else: - continue - - # Loop through each server that they are set to notify - for s_id in data['servers']: - server = self.bot.get_guild(int(s_id)) - # If we can't find it, ignore this one - if server is None: - continue - member = server.get_member(m_id) - # If we can't find them in this server, also ignore - if member is None: - continue - - # Get the notifications settings, get the twitch setting - notifications = self.bot.db.load('server_settings', key=s_id, pluck='notifications') or {} - # Set our default to either the one set, or the default channel of the server - default_channel_id = notifications.get('default') - # If it is has been overriden by twitch notifications setting, use this - channel_id = notifications.get('twitch') or default_channel_id - # Now get the channel - if channel_id: - channel = server.get_channel(int(channel_id)) - else: - continue - - # Then just send our message - try: - await channel.send(msg.format(member=member), embed=embed) - except (discord.Forbidden, discord.HTTPException, AttributeError): - pass - + while not self.bot.is_closed(): + try: + await self.check_channels() + except Exception as error: + with open("error_log", 'a') as f: + traceback.print_tb(error.__traceback__, file=f) + print('{0.__class__.__name__}: {0}'.format(error), file=f) + finally: await asyncio.sleep(30) - except Exception as e: - tb = traceback.format_exc() - fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e) - log.error(fmt) + + async def check_channels(self): + twitch = await self.bot.db.actual_load('twitch', table_filter={'notifications_on': 1}) + for data in twitch: + m_id = int(data['member_id']) + url = data['twitch_url'] + # Check if they are online by trying to get an displayed embed for this user + embed = await self.online_embed(url) + # If they're currently online, but saved as not then we'll let servers know they are now online + if embed and data['live'] == 0: + msg = "{member.display_name} has just gone live!" + self.bot.db.save('twitch', {'live': 1, 'member_id': str(m_id)}) + # Otherwise our notification will say they've gone offline + elif not embed and data['live'] == 1: + msg = "{member.display_name} has just gone offline!" + embed = await self.offline_embed(url) + self.bot.db.save('twitch', {'live': 0, 'member_id': str(m_id)}) + else: + continue + + # Loop through each server that they are set to notify + for s_id in data['servers']: + server = self.bot.get_guild(int(s_id)) + # If we can't find it, ignore this one + if server is None: + continue + member = server.get_member(m_id) + # If we can't find them in this server, also ignore + if member is None: + continue + + # Get the notifications settings, get the twitch setting + notifications = self.bot.db.load('server_settings', key=s_id, pluck='notifications') or {} + # Set our default to either the one set, or the default channel of the server + default_channel_id = notifications.get('default') + # If it is has been overriden by twitch notifications setting, use this + channel_id = notifications.get('twitch') or default_channel_id + # Now get the channel + if channel_id: + channel = server.get_channel(int(channel_id)) + else: + continue + + # Then just send our message + try: + await channel.send(msg.format(member=member), embed=embed) + except (discord.Forbidden, discord.HTTPException, AttributeError): + pass @commands.group(invoke_without_command=True) @commands.guild_only() @@ -329,6 +331,4 @@ class Twitch: def setup(bot): - t = Twitch(bot) - bot.loop.create_task(t.check_channels()) bot.add_cog(Twitch(bot))