From 96225e9958ca896c4a705eb632db4b86d39bc53b Mon Sep 17 00:00:00 2001 From: Marko259 Date: Sat, 14 Sep 2024 13:44:20 +0200 Subject: [PATCH] Added rate limit check, new roles API + restrict event spam --- cogs/events.py | 12 ++++++++++-- cogs/roles.py | 25 ++++++++++++------------- helpers/error.py | 35 +++++++++++++++++++++++++++++++++++ helpers/roles.py | 20 ++++++-------------- 4 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 helpers/error.py diff --git a/cogs/events.py b/cogs/events.py index d446011..1843f10 100644 --- a/cogs/events.py +++ b/cogs/events.py @@ -8,6 +8,7 @@ from helpers.message import embed, event_description, get_image from helpers.database import db_connection from helpers.event import Event +from helpers.error import Error class EventsCog(commands.Cog): # @@ -105,7 +106,7 @@ async def post_events(self): text = f'{role.mention}\n:clock2: **{event.name}** is starting in two hours!' message = await channel.send(text, embed=msg) - if DEBUG == False: await message.publish() + if DEBUG == False: await Error.crosspost(message=message) event.mark_as_published() await self.store_message_expire(message, event._get_expire_datetime()) @@ -225,6 +226,10 @@ async def fetch_api(self): None ) + is_recurring = new_event.get('parent_id') + if is_recurring is not None: + continue + # Publish the newly scheduled event in channel channel = self.bot.get_channel(EVENTS_CHANNEL) e = self.events[new_event.get('id')] @@ -239,7 +244,7 @@ async def fetch_api(self): text = f':calendar_spiral: A new event has been scheduled.' message = await channel.send(text, embed=msg) - if DEBUG == False: await message.publish() + if DEBUG == False: await Error.crosspost(message=message) await self.store_message_expire(message, e._get_expire_datetime()) @@ -264,6 +269,9 @@ async def save_data(self): mydb = db_connection() cursor = mydb.cursor() + print(event.id, flush=True) + print(event.url, flush=True) + cursor.execute( "INSERT INTO events (id, name, img, url, description, start_time, end_time, published) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE name = VALUES(name), img = VALUES(img), url = VALUES(url), description = VALUES(description), start_time = VALUES(start_time), end_time = VALUES(end_time), published = VALUES(published)", ( diff --git a/cogs/roles.py b/cogs/roles.py index 735fb79..c9f4b66 100644 --- a/cogs/roles.py +++ b/cogs/roles.py @@ -44,9 +44,7 @@ async def check_roles(self, override=False): training_staff_role = discord.utils.get(guild.roles, id=TRAINING_STAFF_ROLE) - mentors = await Roles.get_mentors(self) - - moderators = await Roles.get_moderators(self) + roles = await Roles.get_roles(self) trainings = await Roles.get_training(self) @@ -58,20 +56,21 @@ async def check_roles(self, override=False): raise ValueError should_be_mentor = False + should_be_training_staff = False belong_to = [] - for mentor in mentors: - if int(mentor['id']) == int(cid[0]): - should_be_mentor = True - for fir in mentor['fir']: - belong_to.append(fir) + for role in roles: + if int(role['id']) == int(cid[0]): + for item in role['roles']: + print(role['roles'][item]) + if role['roles'][item] is not None: + if 'Mentor' in role['roles'][item]: + should_be_mentor = True + belong_to.append(item) + if 'Moderator' in role['roles'][item]: + should_be_training_staff = True - should_be_training_staff = False - - for moderator in moderators: - if int(moderator['id']) == int(cid[0]): - should_be_training_staff = True student_data = {} should_be_student = False diff --git a/helpers/error.py b/helpers/error.py new file mode 100644 index 0000000..cd1953e --- /dev/null +++ b/helpers/error.py @@ -0,0 +1,35 @@ +import asyncio +from discord.errors import HTTPException + + +class Error: + def __init__(self) -> None: + pass + + async def crosspost(message, retries=3): + """ + Safely crossposts a message, with rate limit handling. + :param message: The discord message to crosspost. + :param retries: Number of retries allowed in case of rate limiting. + """ + for attempt in range(retries): + try: + # Try publishing the message + await message.publish() + return # Exit when the message has successfully been published + + except HTTPException as e: + if e.status == 429: + retry_after = e.retry_after if hasattr(e, 'retry_after') else 500 # Default to 500 seconds if not provided + print(f'Rate limit hit! Retrying in {retry_after:.2f} seconds...', flush=True) + await asyncio.sleep(retry_after) # Wait for the rate limit to reset + + else: + #If another HTTP Exception is thrown reraise it + raise + + except Exception as e: + print(f"An Error occurred while trying to publish a message: {e}", flush=True) + break + + print(f"Failed to publish message after {retries} attempts", flush=True) diff --git a/helpers/roles.py b/helpers/roles.py index 79246ef..9363d37 100644 --- a/helpers/roles.py +++ b/helpers/roles.py @@ -14,25 +14,17 @@ def __init__(self): Create a Roles object """ - async def get_mentors(self): + async def get_roles(self): """ Get all mentors from the API """ - request = requests.get(CC_API_URL + '/roles', headers={'Authorization': 'Bearer ' + CC_API_TOKEN, 'Accept': 'application/json'}) - if request.status_code == requests.codes.ok: - feedback = request.json() - return feedback["data"]["mentors"] - else: - return False - - async def get_moderators(self): - """ - Get all moderators from the API - """ - request = requests.get(CC_API_URL + '/roles', headers={'Authorization': 'Bearer ' + CC_API_TOKEN, 'Accept': 'application/json'}) + request = requests.get(CC_API_URL + '/users', headers={'Authorization': 'Bearer ' + CC_API_TOKEN, 'Accept': 'application/json'}, + params={ + 'include[]': 'roles' + }) if request.status_code == requests.codes.ok: feedback = request.json() - return feedback["data"]["moderators"] + return feedback["data"] else: return False