@@ -400,16 +400,106 @@ async def post_to_log_channel(self, embed: discord.Embed = None, file: discord.F
400400 except Exception as e :
401401 log .warning (f"[LOG] Could not send to log channel: { e } " )
402402
403- @app_commands .command (
403+ @tasks .loop (hours = 1 )
404+ async def update_stfc_ranks (self ):
405+ """Periodically check for rank changes and request admin confirmation."""
406+ guild = self .get_guild (GUILD_ID )
407+ if not guild :
408+ log .warning ("[UPDATE] Guild not found" )
409+ return
410+
411+ # Only run every UPDATE_CHECK_HOURS hours
412+ await self ._update_stfc_ranks_impl (guild )
413+
414+ async def _update_stfc_ranks_impl (self , guild : discord .Guild ):
415+ """Implementation of rank update checking."""
416+ log .info ("[UPDATE] Starting periodic rank check" )
417+
418+ players = store .get_all_players ()
419+ log .info (f"[UPDATE] Found { len (players )} players to check" )
420+
421+ for user_id , player_id in players :
422+ member = guild .get_member (user_id )
423+ if not member :
424+ log .debug (f"[UPDATE] Member { user_id } no longer in guild" )
425+ continue
426+
427+ try :
428+ player_data = STFCProScraper .fetch_player_data (player_id )
429+ if not player_data :
430+ log .warning (f"[UPDATE] Could not fetch data for player { player_id } " )
431+ continue
432+
433+ old_data = store .get_player_data (user_id )
434+ if not old_data :
435+ continue
436+
437+ old_rank = old_data [4 ] # rank is 5th column
438+ new_rank = player_data .rank
439+
440+ # Check if rank changed
441+ if old_rank != new_rank :
442+ log .info (f"[UPDATE] Rank change detected for { member .name } : { old_rank } → { new_rank } " )
443+
444+ # Update nickname in case it changed
445+ new_nick = self ._build_nickname (player_data .alliance_tag , player_data .username )
446+ if member .nick != new_nick :
447+ try :
448+ await member .edit (nick = new_nick )
449+ except discord .Forbidden :
450+ log .warning (f"[UPDATE] Could not update nickname for { member .name } " )
451+
452+ # Request confirmation for ANY rank change
453+ confirmation_view = await self ._assign_ranks (member , player_data , request_confirmation = True )
454+
455+ # Update database
456+ store .store_stfc_player (user_id , player_data , old_data [5 ]) # screenshot_url is 6th column
457+
458+ # Post confirmation request if needed
459+ if confirmation_view :
460+ admin_ping = f"<@&{ ADMIN_ROLE_ID } >" if ADMIN_ROLE_ID else "Admins"
461+ alliance_display = f"[{ player_data .alliance_tag } ]" if player_data .alliance_tag else "N/A"
462+ confirm_embed = discord .Embed (
463+ title = "🔔 Rank Change Detected - Confirmation Required" ,
464+ description = f"{ admin_ping } , please confirm this rank change." ,
465+ color = discord .Color .orange (),
466+ )
467+ confirm_embed .add_field (name = "Player" , value = f"{ member .mention } ({ player_data .username } )" , inline = False )
468+ confirm_embed .add_field (name = "Previous Rank" , value = old_rank or "N/A" , inline = True )
469+ confirm_embed .add_field (name = "New Rank" , value = new_rank or "N/A" , inline = True )
470+ confirm_embed .add_field (name = "Alliance" , value = alliance_display , inline = True )
471+ await self .post_to_log_channel (embed = confirm_embed , view = confirmation_view )
472+
473+ except Exception as e :
474+ log .error (f"[UPDATE] Error checking player { player_id } : { e } " )
475+ continue
476+
477+ @update_stfc_ranks .before_loop
478+ async def before_update_stfc_ranks (self ):
479+ """Wait for bot to be ready before starting update loop."""
480+ await self .wait_until_ready ()
481+ # Adjust loop to run at specified intervals
482+ current_hours = UPDATE_CHECK_HOURS
483+ if current_hours > 0 :
484+ self .update_stfc_ranks .change_interval (hours = current_hours )
485+
486+
487+ # ---------------------------------------------------------------------------
488+ # Commands (Registered after bot instantiation)
489+ # ---------------------------------------------------------------------------
490+ def setup_commands (bot : STFCRankBot ):
491+ """Register slash commands with the bot."""
492+
493+ @bot .tree .command (
404494 name = "verify" ,
405- description = "Verify your STFC player account with alliance rank"
495+ description = "Verify your STFC player account with alliance rank" ,
496+ guild = discord .Object (GUILD_ID ),
406497 )
407498 @app_commands .describe (
408- player_url = "Your stfc.pro/stfc.wtf player URL or player ID" ,
499+ player_url = "Your stfc.pro/stfc.wtf/stfc.live player URL or player ID" ,
409500 screenshot = "Screenshot of your player profile (for verification logging)"
410501 )
411502 async def verify_command (
412- self ,
413503 interaction : discord .Interaction ,
414504 player_url : str ,
415505 screenshot : discord .Attachment ,
@@ -446,7 +536,7 @@ async def verify_command(
446536 return
447537
448538 # Update nickname
449- new_nick = self ._build_nickname (player_data .alliance_tag , player_data .username )
539+ new_nick = bot ._build_nickname (player_data .alliance_tag , player_data .username )
450540 try :
451541 await member .edit (nick = new_nick , reason = "STFC Verification" )
452542 log .info (f"[VERIFY] Updated nickname for { member .name } : { new_nick } " )
@@ -465,7 +555,7 @@ async def verify_command(
465555 store .store_stfc_player (member .id , player_data , screenshot_url )
466556
467557 # Assign ranks (base role immediate, higher ranks need confirmation)
468- confirmation_view = await self ._assign_ranks (member , player_data , request_confirmation = False )
558+ confirmation_view = await bot ._assign_ranks (member , player_data , request_confirmation = False )
469559
470560 # Success response
471561 embed = discord .Embed (
@@ -491,7 +581,7 @@ async def verify_command(
491581 log_embed .add_field (name = "Rank" , value = player_data .rank or "N/A" , inline = True )
492582 log_embed .add_field (name = "Server" , value = str (player_data .server ), inline = True )
493583 log_embed .set_image (url = screenshot_url )
494- await self .post_to_log_channel (embed = log_embed )
584+ await bot .post_to_log_channel (embed = log_embed )
495585
496586 # If rank requires confirmation, post confirmation request
497587 if confirmation_view :
@@ -504,90 +594,7 @@ async def verify_command(
504594 confirm_embed .add_field (name = "Player" , value = f"{ member .mention } ({ player_data .username } )" , inline = False )
505595 confirm_embed .add_field (name = "Rank" , value = player_data .rank , inline = True )
506596 confirm_embed .add_field (name = "Alliance" , value = alliance_display , inline = True )
507- await self .post_to_log_channel (embed = confirm_embed , view = confirmation_view )
508-
509- @tasks .loop (hours = 1 )
510- async def update_stfc_ranks (self ):
511- """Periodically check for rank changes and request admin confirmation."""
512- guild = self .get_guild (GUILD_ID )
513- if not guild :
514- log .warning ("[UPDATE] Guild not found" )
515- return
516-
517- # Only run every UPDATE_CHECK_HOURS hours
518- await self ._update_stfc_ranks_impl (guild )
519-
520- async def _update_stfc_ranks_impl (self , guild : discord .Guild ):
521- """Implementation of rank update checking."""
522- log .info ("[UPDATE] Starting periodic rank check" )
523-
524- players = store .get_all_players ()
525- log .info (f"[UPDATE] Found { len (players )} players to check" )
526-
527- for user_id , player_id in players :
528- member = guild .get_member (user_id )
529- if not member :
530- log .debug (f"[UPDATE] Member { user_id } no longer in guild" )
531- continue
532-
533- try :
534- player_data = STFCProScraper .fetch_player_data (player_id )
535- if not player_data :
536- log .warning (f"[UPDATE] Could not fetch data for player { player_id } " )
537- continue
538-
539- old_data = store .get_player_data (user_id )
540- if not old_data :
541- continue
542-
543- old_rank = old_data [4 ] # rank is 5th column
544- new_rank = player_data .rank
545-
546- # Check if rank changed
547- if old_rank != new_rank :
548- log .info (f"[UPDATE] Rank change detected for { member .name } : { old_rank } → { new_rank } " )
549-
550- # Update nickname in case it changed
551- new_nick = self ._build_nickname (player_data .alliance_tag , player_data .username )
552- if member .nick != new_nick :
553- try :
554- await member .edit (nick = new_nick )
555- except discord .Forbidden :
556- log .warning (f"[UPDATE] Could not update nickname for { member .name } " )
557-
558- # Request confirmation for ANY rank change
559- confirmation_view = await self ._assign_ranks (member , player_data , request_confirmation = True )
560-
561- # Update database
562- store .store_stfc_player (user_id , player_data , old_data [5 ]) # screenshot_url is 6th column
563-
564- # Post confirmation request if needed
565- if confirmation_view :
566- admin_ping = f"<@&{ ADMIN_ROLE_ID } >" if ADMIN_ROLE_ID else "Admins"
567- alliance_display = f"[{ player_data .alliance_tag } ]" if player_data .alliance_tag else "N/A"
568- confirm_embed = discord .Embed (
569- title = "🔔 Rank Change Detected - Confirmation Required" ,
570- description = f"{ admin_ping } , please confirm this rank change." ,
571- color = discord .Color .orange (),
572- )
573- confirm_embed .add_field (name = "Player" , value = f"{ member .mention } ({ player_data .username } )" , inline = False )
574- confirm_embed .add_field (name = "Previous Rank" , value = old_rank or "N/A" , inline = True )
575- confirm_embed .add_field (name = "New Rank" , value = new_rank or "N/A" , inline = True )
576- confirm_embed .add_field (name = "Alliance" , value = alliance_display , inline = True )
577- await self .post_to_log_channel (embed = confirm_embed , view = confirmation_view )
578-
579- except Exception as e :
580- log .error (f"[UPDATE] Error checking player { player_id } : { e } " )
581- continue
582-
583- @update_stfc_ranks .before_loop
584- async def before_update_stfc_ranks (self ):
585- """Wait for bot to be ready before starting update loop."""
586- await self .wait_until_ready ()
587- # Adjust loop to run at specified intervals
588- current_hours = UPDATE_CHECK_HOURS
589- if current_hours > 0 :
590- self .update_stfc_ranks .change_interval (hours = current_hours )
597+ await bot .post_to_log_channel (embed = confirm_embed , view = confirmation_view )
591598
592599
593600# ---------------------------------------------------------------------------
@@ -598,4 +605,5 @@ async def before_update_stfc_ranks(self):
598605 os .makedirs ("screenshots" , exist_ok = True )
599606
600607 bot = STFCRankBot ()
608+ setup_commands (bot )
601609 bot .run (DISCORD_TOKEN )
0 commit comments