@@ -2218,7 +2218,18 @@ async def find(
22182218 try :
22192219 await thread .wait_until_ready ()
22202220 except asyncio .CancelledError :
2221- logger .warning ("Thread for %s cancelled." , recipient )
2221+ # Improve logging: include username and user ID when possible
2222+ try :
2223+ if recipient is not None :
2224+ label = f"{ recipient } ({ recipient .id } )"
2225+ elif recipient_id is not None :
2226+ user = await self .bot .get_or_fetch_user (recipient_id )
2227+ label = f"{ user } ({ recipient_id } )" if user else f"User ({ recipient_id } )"
2228+ else :
2229+ label = "Unknown User"
2230+ except Exception :
2231+ label = f"User ({ recipient_id } )" if recipient_id is not None else "Unknown User"
2232+ logger .warning ("Thread for %s cancelled." , label )
22222233 return thread
22232234 else :
22242235 # If the thread is snoozed (channel is None), return it for restoration
@@ -2335,7 +2346,12 @@ async def create(
23352346 try :
23362347 await thread .wait_until_ready ()
23372348 except asyncio .CancelledError :
2338- logger .warning ("Thread for %s cancelled, abort creating." , recipient )
2349+ # Improve logging to include username and ID
2350+ try :
2351+ label = f"{ recipient } ({ recipient .id } )"
2352+ except Exception :
2353+ label = f"User ({ getattr (recipient , 'id' , 'unknown' )} )"
2354+ logger .warning ("Thread for %s cancelled, abort creating." , label )
23392355 return thread
23402356 else :
23412357 if thread .channel and self .bot .get_channel (thread .channel .id ):
@@ -2350,7 +2366,18 @@ async def create(
23502366
23512367 self .cache [recipient .id ] = thread
23522368
2353- if (message or not manual_trigger ) and self .bot .config ["confirm_thread_creation" ]:
2369+ # Determine if the advanced thread-creation menu is enabled; if so and the user
2370+ # initiated via DM, we defer confirmation until AFTER the user selects an option.
2371+ adv_menu_enabled = self .bot .config .get ("thread_creation_menu_enabled" ) and bool (
2372+ self .bot .config .get ("thread_creation_menu_options" )
2373+ )
2374+ user_initiated_dm = (creator is None or creator == recipient ) and manual_trigger
2375+
2376+ if (
2377+ (message or not manual_trigger )
2378+ and self .bot .config ["confirm_thread_creation" ]
2379+ and not (adv_menu_enabled and user_initiated_dm )
2380+ ):
23542381 if not manual_trigger :
23552382 destination = recipient
23562383 else :
@@ -2416,7 +2443,6 @@ async def create(
24162443 submenus = self .bot .config .get ("thread_creation_menu_submenus" ) or {}
24172444
24182445 # Minimal inline view implementation (avoid importing plugin code)
2419- import discord
24202446
24212447 thread .ready = False # not ready yet
24222448
@@ -2459,6 +2485,69 @@ async def callback(self, interaction: discord.Interaction):
24592485 # Fallback to provided category (from outer scope) or main category
24602486 fallback_category = category or self .outer_thread .bot .main_category
24612487 use_category = sel_category or fallback_category
2488+ # If confirmation is enabled, prompt now (after option selection)
2489+ try :
2490+ if self .outer_thread .bot .config .get ("confirm_thread_creation" ):
2491+ dest = message .channel if manual_trigger else recipient
2492+ view = ConfirmThreadCreationView ()
2493+ view .add_item (
2494+ AcceptButton (
2495+ "accept-thread-creation" ,
2496+ self .outer_thread .bot .config ["confirm_thread_creation_accept" ],
2497+ )
2498+ )
2499+ view .add_item (
2500+ DenyButton (
2501+ "deny-thread-creation" ,
2502+ self .outer_thread .bot .config ["confirm_thread_creation_deny" ],
2503+ )
2504+ )
2505+ confirm = await dest .send (
2506+ embed = discord .Embed (
2507+ title = self .outer_thread .bot .config ["confirm_thread_creation_title" ],
2508+ description = self .outer_thread .bot .config ["confirm_thread_response" ],
2509+ color = self .outer_thread .bot .main_color ,
2510+ ),
2511+ view = view ,
2512+ )
2513+ await view .wait ()
2514+ if view .value is None :
2515+ # Timed out
2516+ self .outer_thread .cancelled = True
2517+ try :
2518+ await dest .send (
2519+ embed = discord .Embed (
2520+ title = self .outer_thread .bot .config ["thread_cancelled" ],
2521+ description = "Timed out" ,
2522+ color = self .outer_thread .bot .error_color ,
2523+ )
2524+ )
2525+ await confirm .edit (view = None )
2526+ except Exception :
2527+ pass
2528+ elif view .value is False :
2529+ self .outer_thread .cancelled = True
2530+ try :
2531+ await dest .send (
2532+ embed = discord .Embed (
2533+ title = self .outer_thread .bot .config ["thread_cancelled" ],
2534+ color = self .outer_thread .bot .error_color ,
2535+ )
2536+ )
2537+ except Exception :
2538+ pass
2539+ if self .outer_thread .cancelled :
2540+ # Clear pending/menu state and cache
2541+ try :
2542+ setattr (self .outer_thread , "_pending_menu" , False )
2543+ self .outer_thread .manager .cache .pop (self .outer_thread .id , None )
2544+ except Exception :
2545+ pass
2546+ return
2547+ except Exception :
2548+ # If confirm step fails, proceed to create thread to avoid dead-ends
2549+ logger .warning ("Confirm step failed after menu selection; continuing." )
2550+
24622551 self .outer_thread .bot .loop .create_task (
24632552 self .outer_thread .setup (
24642553 creator = creator ,
@@ -2555,6 +2644,8 @@ async def on_timeout(self):
25552644 self .outer_thread .manager .cache .pop (self .outer_thread .id , None )
25562645 except Exception :
25572646 pass
2647+ # Clear pending menu flag so a new message can recreate a fresh thread
2648+ setattr (self .outer_thread , "_pending_menu" , False )
25582649 self .outer_thread .cancelled = True
25592650 else :
25602651 try :
@@ -2563,6 +2654,18 @@ async def on_timeout(self):
25632654 )
25642655 except Exception :
25652656 pass
2657+ # Allow subsequent messages to trigger a new menu/thread by clearing state
2658+ setattr (self .outer_thread , "_pending_menu" , False )
2659+ try :
2660+ self .outer_thread .manager .cache .pop (self .outer_thread .id , None )
2661+ except Exception :
2662+ pass
2663+ self .outer_thread .cancelled = True
2664+ # Ensure view is stopped to release any internal tasks
2665+ try :
2666+ self .stop ()
2667+ except Exception :
2668+ pass
25662669
25672670 # Send DM prompt
25682671 try :
@@ -2571,6 +2674,11 @@ async def on_timeout(self):
25712674 menu_msg = await recipient .send (embed = embed , view = menu_view )
25722675 # mark thread as pending menu selection
25732676 thread ._pending_menu = True
2677+ # Explicitly attach the message to the view for safety in callbacks
2678+ try :
2679+ menu_view .message = menu_msg
2680+ except Exception :
2681+ pass
25742682 except Exception :
25752683 logger .warning (
25762684 "Failed to send thread-creation menu DM, falling back to immediate thread creation."
0 commit comments