Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Implement voice keepalive feature and enhance command validations
Browse files
app.py
CHANGED
@@ -15,6 +15,7 @@ if os.path.exists("assets") is False:
|
|
15 |
"not-lain/assets", "lofi.mp3", repo_type="dataset", local_dir="assets"
|
16 |
)
|
17 |
|
|
|
18 |
|
19 |
# Bot configuration
|
20 |
intents = discord.Intents.default()
|
@@ -26,12 +27,30 @@ class MusicBot:
|
|
26 |
def __init__(self):
|
27 |
self.is_playing = False
|
28 |
self.voice_client = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
async def join_voice(self, ctx):
|
31 |
if ctx.author.voice:
|
32 |
channel = ctx.author.voice.channel
|
33 |
if self.voice_client is None:
|
34 |
self.voice_client = await channel.connect()
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
else:
|
36 |
await self.voice_client.move_to(channel)
|
37 |
else:
|
@@ -41,14 +60,10 @@ class MusicBot:
|
|
41 |
if not self.is_playing:
|
42 |
self.is_playing = True
|
43 |
try:
|
44 |
-
audio_source = discord.FFmpegPCMAudio(
|
45 |
|
46 |
def after_playing(e):
|
47 |
self.is_playing = False
|
48 |
-
# test loop by default
|
49 |
-
if e:
|
50 |
-
print(f"Playback error: {e}")
|
51 |
-
asyncio.run_coroutine_threadsafe(self.play_next(ctx), bot.loop)
|
52 |
|
53 |
self.voice_client.play(audio_source, after=after_playing)
|
54 |
except Exception as e:
|
@@ -56,6 +71,35 @@ class MusicBot:
|
|
56 |
await ctx.send("Error playing the song.")
|
57 |
self.is_playing = False
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
music_bot = MusicBot()
|
61 |
|
@@ -76,7 +120,7 @@ async def on_ready():
|
|
76 |
@bot.tree.command(name="play", description="Play the sample music")
|
77 |
async def play(interaction: discord.Interaction):
|
78 |
await interaction.response.defer()
|
79 |
-
ctx = await
|
80 |
await music_bot.join_voice(ctx)
|
81 |
|
82 |
if not music_bot.is_playing:
|
@@ -86,27 +130,70 @@ async def play(interaction: discord.Interaction):
|
|
86 |
await interaction.followup.send("Already playing!")
|
87 |
|
88 |
|
89 |
-
# Replace the existing skip command with this version
|
90 |
@bot.tree.command(name="skip", description="Skip the current song")
|
91 |
async def skip(interaction: discord.Interaction):
|
92 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
music_bot.voice_client.stop()
|
94 |
await interaction.response.send_message("Skipped current song!")
|
95 |
else:
|
96 |
await interaction.response.send_message("No song is currently playing!")
|
97 |
|
98 |
|
99 |
-
# Replace the existing leave command with this version
|
100 |
@bot.tree.command(name="leave", description="Disconnect bot from voice channel")
|
101 |
async def leave(interaction: discord.Interaction):
|
102 |
-
if
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
|
111 |
|
112 |
def run_discord_bot():
|
@@ -118,6 +205,7 @@ with gr.Blocks() as iface:
|
|
118 |
gr.Markdown("# Discord Music Bot Control Panel")
|
119 |
gr.Markdown("Bot is running in background")
|
120 |
|
|
|
121 |
if __name__ == "__main__":
|
122 |
# Start the Discord bot in a separate thread
|
123 |
bot_thread = threading.Thread(target=run_discord_bot, daemon=True)
|
|
|
15 |
"not-lain/assets", "lofi.mp3", repo_type="dataset", local_dir="assets"
|
16 |
)
|
17 |
|
18 |
+
song = "assets/lofi.mp3"
|
19 |
|
20 |
# Bot configuration
|
21 |
intents = discord.Intents.default()
|
|
|
27 |
def __init__(self):
|
28 |
self.is_playing = False
|
29 |
self.voice_client = None
|
30 |
+
self.keepalive_task = None
|
31 |
+
self.last_context = None
|
32 |
+
|
33 |
+
async def voice_keepalive(self, voice_client):
|
34 |
+
"""Keeps the voice connection alive by periodically playing audio"""
|
35 |
+
print("Starting voice keepalive task")
|
36 |
+
while True:
|
37 |
+
if voice_client.is_connected() and not self.is_playing:
|
38 |
+
await self.play_next(self.last_context)
|
39 |
+
await asyncio.sleep(15)
|
40 |
+
else:
|
41 |
+
await asyncio.sleep(1)
|
42 |
|
43 |
async def join_voice(self, ctx):
|
44 |
if ctx.author.voice:
|
45 |
channel = ctx.author.voice.channel
|
46 |
if self.voice_client is None:
|
47 |
self.voice_client = await channel.connect()
|
48 |
+
self.last_context = ctx
|
49 |
+
if self.keepalive_task:
|
50 |
+
self.keepalive_task.cancel()
|
51 |
+
self.keepalive_task = asyncio.create_task(
|
52 |
+
self.voice_keepalive(self.voice_client)
|
53 |
+
)
|
54 |
else:
|
55 |
await self.voice_client.move_to(channel)
|
56 |
else:
|
|
|
60 |
if not self.is_playing:
|
61 |
self.is_playing = True
|
62 |
try:
|
63 |
+
audio_source = discord.FFmpegPCMAudio(song)
|
64 |
|
65 |
def after_playing(e):
|
66 |
self.is_playing = False
|
|
|
|
|
|
|
|
|
67 |
|
68 |
self.voice_client.play(audio_source, after=after_playing)
|
69 |
except Exception as e:
|
|
|
71 |
await ctx.send("Error playing the song.")
|
72 |
self.is_playing = False
|
73 |
|
74 |
+
async def stop_playing(self, ctx):
|
75 |
+
try:
|
76 |
+
if self.keepalive_task:
|
77 |
+
self.keepalive_task.cancel()
|
78 |
+
self.keepalive_task = None
|
79 |
+
|
80 |
+
if self.voice_client:
|
81 |
+
if self.voice_client.is_playing():
|
82 |
+
self.voice_client.stop()
|
83 |
+
|
84 |
+
self.is_playing = False
|
85 |
+
self.last_context = None
|
86 |
+
|
87 |
+
if self.voice_client.is_connected():
|
88 |
+
await self.voice_client.disconnect(force=False)
|
89 |
+
|
90 |
+
self.voice_client = None
|
91 |
+
return True
|
92 |
+
|
93 |
+
return False
|
94 |
+
|
95 |
+
except Exception as e:
|
96 |
+
print(f"Error during cleanup: {e}")
|
97 |
+
self.is_playing = False
|
98 |
+
self.voice_client = None
|
99 |
+
self.last_context = None
|
100 |
+
self.keepalive_task = None
|
101 |
+
return False
|
102 |
+
|
103 |
|
104 |
music_bot = MusicBot()
|
105 |
|
|
|
120 |
@bot.tree.command(name="play", description="Play the sample music")
|
121 |
async def play(interaction: discord.Interaction):
|
122 |
await interaction.response.defer()
|
123 |
+
ctx = await bot.get_context(interaction)
|
124 |
await music_bot.join_voice(ctx)
|
125 |
|
126 |
if not music_bot.is_playing:
|
|
|
130 |
await interaction.followup.send("Already playing!")
|
131 |
|
132 |
|
|
|
133 |
@bot.tree.command(name="skip", description="Skip the current song")
|
134 |
async def skip(interaction: discord.Interaction):
|
135 |
+
# Check if user is in a voice channel
|
136 |
+
if not interaction.user.voice:
|
137 |
+
await interaction.response.send_message(
|
138 |
+
"You must be in a voice channel to use this command!"
|
139 |
+
)
|
140 |
+
return
|
141 |
+
|
142 |
+
# Check if bot is in a voice channel
|
143 |
+
if not music_bot.voice_client:
|
144 |
+
await interaction.response.send_message("No song is currently playing!")
|
145 |
+
return
|
146 |
+
|
147 |
+
# Check if user is in the same channel as the bot
|
148 |
+
if interaction.user.voice.channel != music_bot.voice_client.channel:
|
149 |
+
await interaction.response.send_message(
|
150 |
+
"You must be in the same voice channel as the bot!"
|
151 |
+
)
|
152 |
+
return
|
153 |
+
|
154 |
+
if music_bot.voice_client and music_bot.is_playing:
|
155 |
+
music_bot.is_playing = False # Reset playing state
|
156 |
music_bot.voice_client.stop()
|
157 |
await interaction.response.send_message("Skipped current song!")
|
158 |
else:
|
159 |
await interaction.response.send_message("No song is currently playing!")
|
160 |
|
161 |
|
|
|
162 |
@bot.tree.command(name="leave", description="Disconnect bot from voice channel")
|
163 |
async def leave(interaction: discord.Interaction):
|
164 |
+
# Check if user is in a voice channel
|
165 |
+
if not interaction.user.voice:
|
166 |
+
await interaction.response.send_message(
|
167 |
+
"You must be in a voice channel to use this command!"
|
168 |
+
)
|
169 |
+
return
|
170 |
+
|
171 |
+
# Check if bot is in a voice channel
|
172 |
+
if not music_bot.voice_client:
|
173 |
+
await interaction.response.send_message("I'm not in any voice channel!")
|
174 |
+
return
|
175 |
+
|
176 |
+
# Check if user is in the same channel as the bot
|
177 |
+
if interaction.user.voice.channel != music_bot.voice_client.channel:
|
178 |
+
await interaction.response.send_message(
|
179 |
+
"You must be in the same voice channel as the bot!"
|
180 |
+
)
|
181 |
+
return
|
182 |
+
|
183 |
+
await interaction.response.defer()
|
184 |
+
ctx = await bot.get_context(interaction)
|
185 |
+
|
186 |
+
try:
|
187 |
+
success = await music_bot.stop_playing(ctx)
|
188 |
+
if success:
|
189 |
+
await interaction.followup.send("Successfully disconnected! 👋")
|
190 |
+
else:
|
191 |
+
await interaction.followup.send(
|
192 |
+
"Failed to disconnect properly. Please try again."
|
193 |
+
)
|
194 |
+
except Exception as e:
|
195 |
+
print(f"Error during leave command: {e}")
|
196 |
+
await interaction.followup.send("An error occurred while trying to disconnect.")
|
197 |
|
198 |
|
199 |
def run_discord_bot():
|
|
|
205 |
gr.Markdown("# Discord Music Bot Control Panel")
|
206 |
gr.Markdown("Bot is running in background")
|
207 |
|
208 |
+
|
209 |
if __name__ == "__main__":
|
210 |
# Start the Discord bot in a separate thread
|
211 |
bot_thread = threading.Thread(target=run_discord_bot, daemon=True)
|