77import discord
88import pytz
99from discord import NotFound , app_commands , Guild
10- from discord .errors import DiscordServerError , HTTPException
10+ from discord .errors import DiscordException
1111from discord .ext import commands , tasks
1212
13- from utilities .global_vars import bot , wall_e_config
14-
15- from wall_e_models .models import Level , UserPoint , UpdatedUser , ProfileBucketInProgress
16-
1713from utilities .embed import embed , COLOUR_MAPPING , WallEColour
1814from utilities .file_uploading import start_file_uploading
15+ from utilities .global_vars import bot , wall_e_config
1916from utilities .paginate import paginate_embed
2017from utilities .setup_logger import Loggers
21-
22- BUGGY_USER = [234501345749630976 ]
18+ from wall_e_models .models import Level , UserPoint , UpdatedUser , ProfileBucketInProgress
2319
2420
2521class Leveling (commands .Cog ):
@@ -53,6 +49,7 @@ def __init__(self):
5349 self .council_channel = None
5450 self .levelling_website_avatar_channel = None
5551 self .bucket_update_in_progress = False
52+ self .NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER = 15
5653 self .ensure_xp_roles_exist_and_have_right_users .start ()
5754 self .process_leveling_profile_data_for_lurkers .start ()
5855 self .process_outdated_profile_pics .start ()
@@ -67,7 +64,8 @@ async def upload_debug_logs(self):
6764 while self .guild is None :
6865 await asyncio .sleep (2 )
6966 await start_file_uploading (
70- self .logger , self .guild , bot , wall_e_config , self .debug_log_file_absolute_path , "leveling_debug"
67+ self .logger , self .guild , bot , wall_e_config , self .debug_log_file_absolute_path ,
68+ "leveling_debug"
7169 )
7270
7371 @commands .Cog .listener (name = "on_ready" )
@@ -84,7 +82,8 @@ async def upload_error_logs(self):
8482 while self .guild is None :
8583 await asyncio .sleep (2 )
8684 await start_file_uploading (
87- self .logger , self .guild , bot , wall_e_config , self .error_log_file_absolute_path , "leveling_error"
85+ self .logger , self .guild , bot , wall_e_config , self .error_log_file_absolute_path ,
86+ "leveling_error"
8887 )
8988
9089 @commands .Cog .listener (name = "on_ready" )
@@ -558,7 +557,7 @@ async def process_leveling_profile_data_for_lurkers(self):
558557 f"[Leveling process_leveling_profile_data_for_lurkers()] { user_ids_to_update } "
559558 f"potential updates retrieved for bucket { entry .bucket_number_completed } "
560559 )
561- await self ._update_users (self .process_lurkers_logger , user_ids_to_update )
560+ await self ._update_users_with_given_ids (self .process_lurkers_logger , user_ids_to_update )
562561 await ProfileBucketInProgress .async_save (entry )
563562
564563 async def _set_bucket_numbers (self , logger ):
@@ -671,37 +670,44 @@ async def _get_current_bucket_number(self) -> ProfileBucketInProgress:
671670 entry .bucket_number_completed = 1
672671 return entry
673672
674- async def _update_users (self , logger , updated_user_ids ):
673+ async def _update_users_with_given_ids (self , logger , updated_user_ids ):
675674 """
676675 iterates through the given list of user_ids and updates them
677676 :param updated_user_ids:
678677 :return:
679678 """
680679 total_number_of_updates_needed = len (updated_user_ids )
681680 for index , user_id in enumerate (updated_user_ids ):
681+ if self .user_points [user_id ].being_processed :
682+ continue
683+ self .user_points [user_id ].being_processed = True
684+ successful = False
682685 logger .debug (
683- f"[Leveling _update_users()] attempting to get updated user_point profile data for member { user_id } "
684- f"{ index + 1 } /{ total_number_of_updates_needed } "
686+ f"[Leveling _update_users_with_given_ids()] attempt "
687+ f"{ self .user_points [user_id ].leveling_update_attempt } to get updated user_point profile data for "
688+ f"member { user_id } { index + 1 } /{ total_number_of_updates_needed } "
685689 )
686- member = None
687- try :
688- member = await self .guild .fetch_member (user_id )
689- except (NotFound , DiscordServerError ) as e :
690- try :
691- logger .info (
692- f"[Leveling _update_users()] got following error when fetching guild member { user_id } \n { e } "
693- )
694- member = await bot .fetch_user (user_id )
695- except DiscordServerError as e :
696- logger .error (
697- f"[Leveling _update_users()] got the following error when fetching member { user_id } \n { e } ."
698- )
699- except HTTPException as e :
700- logger .warn (
701- f"[Leveling _update_users()] got the following error when fetching member { user_id } \n { e } ."
690+ while (
691+ not successful and
692+ self .user_points [user_id ].leveling_update_attempt < self .NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER
693+ ):
694+ self .user_points [user_id ].leveling_update_attempt += 1
695+ random_number_milliseconds = random .randint (0 , 1000 ) / 1000
696+ sleep_seconds = math .pow (
697+ 2 , self .user_points [user_id ].leveling_update_attempt + random_number_milliseconds
698+ )
699+ logger .debug (
700+ f"[Leveling _update_users_with_given_ids()] Sleeping for { sleep_seconds } before attempt "
701+ f"{ self .user_points [user_id ].leveling_update_attempt } when trying to update user_point profile "
702+ f"data for member { user_id } ."
702703 )
703- if member :
704- await self ._update_member_profile_data (logger , member , user_id , index , total_number_of_updates_needed )
704+ await asyncio .sleep (sleep_seconds )
705+ member = await self .get_user (logger , user_id )
706+ if member :
707+ successful = await self ._update_member_profile_data (
708+ logger , member , user_id , index , total_number_of_updates_needed
709+ )
710+ self .user_points [user_id ].being_processed = False
705711
706712 @tasks .loop (seconds = 2 )
707713 async def process_leveling_profile_data_for_active_users (self ):
@@ -715,21 +721,65 @@ async def process_leveling_profile_data_for_active_users(self):
715721 updated_user_logs = await UpdatedUser .get_updated_user_logs ()
716722 total_number_of_updates_needed = len (updated_user_logs )
717723 for index , update_user in enumerate (updated_user_logs ):
718- updated_user_log_id = update_user [0 ] # noqa: F841
719724 updated_user_id = update_user [1 ]
725+ if self .user_points [updated_user_id ].being_processed :
726+ continue
727+ self .user_points [updated_user_id ].being_processed = True
728+ updated_user_log_id = update_user [0 ] # noqa: F841
729+ successful = False
720730 self .logger .debug (
721- f"[Leveling process_leveling_profile_data_for_active_users()] attempting to get updated "
722- f"user_point profile data for member { updated_user_id } "
723- f"{ index + 1 } /{ total_number_of_updates_needed } "
731+ f"[Leveling process_leveling_profile_data_for_active_users()] attempt "
732+ f"{ self . user_points [ updated_user_id ]. leveling_update_attempt } to get data for user in UpdatedUser "
733+ f" with id { updated_user_id } { index + 1 } /{ total_number_of_updates_needed } "
724734 )
735+ while (
736+ not successful and
737+ self .user_points [updated_user_id ].leveling_update_attempt <
738+ self .NUMBER_OF_RETRIEVAL_ATTEMPTS_PER_USER
739+ ):
740+ self .user_points [updated_user_id ].leveling_update_attempt += 1
741+ random_number_milliseconds = random .randint (0 , 1000 ) / 1000
742+ sleep_seconds = math .pow (
743+ 2 , self .user_points [updated_user_id ].leveling_update_attempt + random_number_milliseconds
744+ )
745+ self .logger .debug (
746+ f"[Leveling process_leveling_profile_data_for_active_users()] Sleeping for { sleep_seconds } before"
747+ f" attempt { self .user_points [updated_user_id ].leveling_update_attempt } when trying to get data"
748+ f" for user in UpdatedUser with id { updated_user_id } ."
749+ )
750+ await asyncio .sleep (sleep_seconds )
751+ member = await self .get_user (self .logger , updated_user_id )
752+ if member :
753+ # cannot call _update_users_with_given_ids like process_outdated_profile_pics and
754+ # process_leveling_profile_data_for_lurkers because this task needs to be able to specify a
755+ # updated_user_log_id that will be deleted update_leveling_profile_info when the user is processed
756+ successful = await self ._update_member_profile_data (
757+ self .logger , member , updated_user_id , index , total_number_of_updates_needed ,
758+ updated_user_log_id = updated_user_log_id
759+ )
760+ self .user_points [updated_user_id ].being_processed = False
761+
762+ async def get_user (self , logger , user_id ):
763+ user = None
764+ try :
765+ user = await self .guild .fetch_member (user_id )
766+ except NotFound as e :
725767 try :
726- member = await self .guild .fetch_member (updated_user_id )
727- except NotFound :
728- member = await bot .fetch_user (updated_user_id )
729- await self ._update_member_profile_data (
730- self .logger , member , updated_user_id , index , total_number_of_updates_needed ,
731- updated_user_log_id = updated_user_log_id
768+ logger .info (
769+ f"[Leveling get_user()] unable to find guild member { user_id } \n { e } "
770+ )
771+ user = await bot .fetch_user (user_id )
772+ except DiscordException as e :
773+ logger .error (
774+ f"[Leveling get_user()] got the following error when fetching discord user "
775+ f"{ user_id } \n { e } ."
776+ )
777+ except DiscordException as e :
778+ logger .error (
779+ f"[Leveling get_user()] got the following error when fetching guild member "
780+ f"{ user_id } \n { e } ."
732781 )
782+ return user
733783
734784 @tasks .loop (seconds = 5 )
735785 async def process_outdated_profile_pics (self ):
@@ -747,7 +797,7 @@ async def process_outdated_profile_pics(self):
747797 f"[Leveling process_outdated_profile_pics()] { number_of_users_to_update } users with outdated CDN links"
748798 f" to update"
749799 )
750- await self ._update_users (self .update_outdated_profile_pics_logger , user_ids_to_update )
800+ await self ._update_users_with_given_ids (self .update_outdated_profile_pics_logger , user_ids_to_update )
751801 self .process_outdated_profile_pics_in_progress = False
752802
753803 async def _update_member_profile_data (self , logger , member , updated_user_id , index ,
@@ -763,62 +813,38 @@ async def _update_member_profile_data(self, logger, member, updated_user_id, ind
763813 current loop
764814 :param updated_user_log_id: the ID of the UpdatedUser record to delete if this function was called as a
765815 result of member_update_listener's recording any detected changed in UpdatedUser
766- :return:
816+ :return: True if member was successfully processed
767817 """
768- if member :
769- if member .id in BUGGY_USER :
770- return
771- try :
772- if self .user_points [member .id ].leveling_update_attempt >= 5 :
773- log_level = logger .error if updated_user_log_id is None else logger .debug
774- # wil print out this log line at ERROR only if its not part of the code that attempts to update
775- # active users returned by UpdatedUser.get_updated_user_logs()
776- # I am doing this only cause if you happen to have a backlog of users to work through, you will
777- # mistakenly get alerted about attempts to update the user info not being successful when in
778- # actuality what is happening is just the first process was all that was necessary and the
779- # rest of superfluous but I don't want to update the database to take in just 1 log for each
780- # user for the times when the task is active and therefore one log for each update would
781- # actually be useful
782- log_level (
783- f"[Leveling _update_member_profile_data()] "
784- f"attempt { self .user_points [member .id ].leveling_update_attempt } to update the member profile"
785- f" data in the database for member { member } with id [{ member .id } ], expiry_date of "
786- f"[{ self .user_points [member .id ].discord_avatar_link_expiry_date .pst } ] and a CDN link of "
787- f"<{ self .user_points [member .id ].leveling_message_avatar_url } > "
788- f"{ index + 1 } /{ total_number_of_updates_needed } "
789- )
790- else :
791- # leveling_update_attempt is reset to 0 in update_leveling_profile_info if member is successfully
792- # updated THIS time
793- logger .debug (
794- f"[Leveling _update_member_profile_data()] attempt"
795- f" { self .user_points [member .id ].leveling_update_attempt } to"
796- f" update the member profile data in the database for member { member } with"
797- f" id [{ updated_user_id } ] { index + 1 } /{ total_number_of_updates_needed } "
798- )
799- user_updated = await self .user_points [member .id ].update_leveling_profile_info (
800- logger , self .guild .id , member , self .levelling_website_avatar_channel ,
801- updated_user_log_id = updated_user_log_id
802- )
803- if user_updated :
804- logger .debug (
805- f"[Leveling _update_member_profile_data()] updated the member profile data"
806- f" in the database for member { member } with id [{ updated_user_id } ] "
807- f"{ index + 1 } /{ total_number_of_updates_needed } "
808- )
809- except Exception as e :
810- logger .error (
811- f"[Leveling _update_member_profile_data()] unable to update the member profile"
812- f" data in the database for member { member } with id [{ updated_user_id } ]"
813- f" { index + 1 } /{ total_number_of_updates_needed } due to error:\n { e } "
818+ user_updated = False
819+ try :
820+ logger .debug (
821+ f"[Leveling _update_member_profile_data()] "
822+ f"attempt { self .user_points [member .id ].leveling_update_attempt } to update the member profile"
823+ f"data in the database for member { member } with id [{ member .id } ], "
824+ f"updated_user_log_id = { updated_user_log_id } , expiry_date of "
825+ f"[{ self .user_points [member .id ].discord_avatar_link_expiry_date .pst } ] and a CDN link of "
826+ f"<{ self .user_points [member .id ].leveling_message_avatar_url } > "
827+ f"{ index + 1 } /{ total_number_of_updates_needed } "
828+ )
829+ # leveling_update_attempt is reset to 0 in update_leveling_profile_info if member is successfully
830+ # updated THIS time
831+ user_updated = await self .user_points [member .id ].update_leveling_profile_info (
832+ logger , self .guild .id , member , self .levelling_website_avatar_channel ,
833+ updated_user_log_id = updated_user_log_id
834+ )
835+ if user_updated :
836+ logger .debug (
837+ f"[Leveling _update_member_profile_data()] updated the member profile data"
838+ f" in the database for member { member } with id [{ updated_user_id } ] "
839+ f"{ index + 1 } /{ total_number_of_updates_needed } "
814840 )
815- else :
816- logger .warn (
817- f"[Leveling _update_member_profile_data()] attempt "
818- f"{ self . user_points [ member . id ]. leveling_update_attempt } : unable to update the member profile data "
819- f" in the database for member { updated_user_id } { index + 1 } /{ total_number_of_updates_needed } "
841+ except Exception as e :
842+ logger .error (
843+ f"[Leveling _update_member_profile_data()] unable to update the member profile "
844+ f" data in the database for member { member } with id [ { updated_user_id } ] and updated_user_log_id "
845+ f" = { updated_user_log_id } { index + 1 } /{ total_number_of_updates_needed } due to error: \n { e } "
820846 )
821- await self . user_points [ member . id ]. async_save ()
847+ return user_updated
822848
823849 @app_commands .command (name = "reset_attempts" )
824850 @app_commands .checks .has_any_role ("Bot_manager" )
0 commit comments