modified: app.py
This commit is contained in:
53
app.py
53
app.py
@@ -142,6 +142,15 @@ def get_guild_music(guild_id: int) -> GuildMusic:
|
||||
music_states[guild_id] = st
|
||||
return st
|
||||
|
||||
async def maybe_defer(ctx: commands.Context):
|
||||
"""Defer interaction responses to avoid 'interaction failed' on long ops (>3s)."""
|
||||
try:
|
||||
inter = getattr(ctx, 'interaction', None)
|
||||
if inter and not inter.response.is_done():
|
||||
await inter.response.defer(thinking=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def ytdlp_opts() -> dict:
|
||||
opts = {
|
||||
'format': 'bestaudio/best',
|
||||
@@ -227,20 +236,23 @@ async def start_player_loop(ctx: commands.Context):
|
||||
|
||||
@bot.hybrid_command(name='join', description='Join your voice channel')
|
||||
async def cmd_join(ctx):
|
||||
await maybe_defer(ctx)
|
||||
vc = await ensure_voice(ctx)
|
||||
if vc:
|
||||
await ctx.reply(f"✅ Joined {vc.channel.mention}")
|
||||
await _safe_send(ctx, f"✅ Joined {vc.channel.mention}")
|
||||
|
||||
@bot.hybrid_command(name='leave', description='Leave the voice channel')
|
||||
async def cmd_leave(ctx):
|
||||
await maybe_defer(ctx)
|
||||
if ctx.voice_client and ctx.voice_client.is_connected():
|
||||
await ctx.voice_client.disconnect()
|
||||
await ctx.reply("👋 Disconnected.")
|
||||
await _safe_send(ctx, "👋 Disconnected.")
|
||||
else:
|
||||
await ctx.reply("ℹ️ Not connected.")
|
||||
await _safe_send(ctx, "ℹ️ Not connected.")
|
||||
|
||||
@bot.hybrid_command(name='play', description='Play a YouTube URL or search term')
|
||||
async def cmd_play(ctx, *, query: str):
|
||||
await maybe_defer(ctx)
|
||||
vc = await ensure_voice(ctx)
|
||||
if not vc:
|
||||
return
|
||||
@@ -248,21 +260,23 @@ async def cmd_play(ctx, *, query: str):
|
||||
song = await yt_extract(query)
|
||||
state = get_guild_music(ctx.guild.id)
|
||||
await state.queue.put(song)
|
||||
await ctx.reply(f"▶️ Queued: **{song.title}**")
|
||||
await _safe_send(ctx, f"▶️ Queued: **{song.title}**")
|
||||
await start_player_loop(ctx)
|
||||
except Exception as e:
|
||||
await ctx.reply(f"❌ Failed to add: {e}")
|
||||
await _safe_send(ctx, f"❌ Failed to add: {e}")
|
||||
|
||||
@bot.hybrid_command(name='skip', description='Skip the current track')
|
||||
async def cmd_skip(ctx):
|
||||
await maybe_defer(ctx)
|
||||
if ctx.voice_client and ctx.voice_client.is_playing():
|
||||
ctx.voice_client.stop()
|
||||
await ctx.reply("⏭️ Skipped.")
|
||||
await _safe_send(ctx, "⏭️ Skipped.")
|
||||
else:
|
||||
await ctx.reply("ℹ️ Nothing is playing.")
|
||||
await _safe_send(ctx, "ℹ️ Nothing is playing.")
|
||||
|
||||
@bot.hybrid_command(name='stop', description='Stop playback and clear the queue')
|
||||
async def cmd_stop(ctx):
|
||||
await maybe_defer(ctx)
|
||||
state = get_guild_music(ctx.guild.id)
|
||||
# Clear queue
|
||||
try:
|
||||
@@ -272,38 +286,42 @@ async def cmd_stop(ctx):
|
||||
pass
|
||||
if ctx.voice_client and ctx.voice_client.is_playing():
|
||||
ctx.voice_client.stop()
|
||||
await ctx.reply("⏹️ Stopped and cleared queue.")
|
||||
await _safe_send(ctx, "⏹️ Stopped and cleared queue.")
|
||||
|
||||
@bot.hybrid_command(name='pause', description='Pause playback')
|
||||
async def cmd_pause(ctx):
|
||||
await maybe_defer(ctx)
|
||||
if ctx.voice_client and ctx.voice_client.is_playing():
|
||||
ctx.voice_client.pause()
|
||||
await ctx.reply("⏸️ Paused.")
|
||||
await _safe_send(ctx, "⏸️ Paused.")
|
||||
else:
|
||||
await ctx.reply("ℹ️ Nothing is playing.")
|
||||
await _safe_send(ctx, "ℹ️ Nothing is playing.")
|
||||
|
||||
@bot.hybrid_command(name='resume', description='Resume playback')
|
||||
async def cmd_resume(ctx):
|
||||
await maybe_defer(ctx)
|
||||
if ctx.voice_client and ctx.voice_client.is_paused():
|
||||
ctx.voice_client.resume()
|
||||
await ctx.reply("▶️ Resumed.")
|
||||
await _safe_send(ctx, "▶️ Resumed.")
|
||||
else:
|
||||
await ctx.reply("ℹ️ Nothing is paused.")
|
||||
await _safe_send(ctx, "ℹ️ Nothing is paused.")
|
||||
|
||||
@bot.hybrid_command(name='np', description='Show the currently playing track')
|
||||
async def cmd_nowplaying(ctx):
|
||||
await maybe_defer(ctx)
|
||||
state = get_guild_music(ctx.guild.id)
|
||||
if state.current:
|
||||
dur = f" ({state.current.duration//60}:{state.current.duration%60:02d})" if state.current.duration else ""
|
||||
await ctx.reply(f"🎶 Now playing: **{state.current.title}**{dur}\n{state.current.webpage_url}")
|
||||
await _safe_send(ctx, f"🎶 Now playing: **{state.current.title}**{dur}\n{state.current.webpage_url}")
|
||||
else:
|
||||
await ctx.reply("ℹ️ Nothing is playing.")
|
||||
await _safe_send(ctx, "ℹ️ Nothing is playing.")
|
||||
|
||||
@bot.hybrid_command(name='queue', description='Show queued tracks')
|
||||
async def cmd_queue(ctx):
|
||||
await maybe_defer(ctx)
|
||||
state = get_guild_music(ctx.guild.id)
|
||||
if state.queue.empty():
|
||||
await ctx.reply("🗒️ Queue is empty.")
|
||||
await _safe_send(ctx, "🗒️ Queue is empty.")
|
||||
return
|
||||
# Snapshot queue without draining
|
||||
items: List[Song] = []
|
||||
@@ -320,10 +338,11 @@ async def cmd_queue(ctx):
|
||||
if more > 0:
|
||||
desc += f"\n...and {more} more"
|
||||
embed = discord.Embed(title="🎼 Queue", description=desc, color=discord.Color.purple())
|
||||
await ctx.reply(embed=embed)
|
||||
await _safe_send(ctx, embed=embed)
|
||||
|
||||
@bot.hybrid_command(name='voicediag', description='Diagnose voice playback environment')
|
||||
async def cmd_voicediag(ctx):
|
||||
await maybe_defer(ctx)
|
||||
details = []
|
||||
details.append(f"discord.py: {discord.__version__}")
|
||||
# PyNaCl check
|
||||
@@ -353,7 +372,7 @@ async def cmd_voicediag(ctx):
|
||||
details.append(f"cookie.txt present: {os.path.exists(COOKIE_FILE)} at {COOKIE_FILE}")
|
||||
|
||||
embed = discord.Embed(title='Voice Diagnostics', description='\n'.join(details), color=discord.Color.dark_grey())
|
||||
await ctx.reply(embed=embed)
|
||||
await _safe_send(ctx, embed=embed)
|
||||
|
||||
@bot.event
|
||||
async def on_guild_join(guild):
|
||||
|
||||
Reference in New Issue
Block a user