Skip to content

Commit e583363

Browse files
LulalabyCopilot
andcommitted
fix: harden Lavalink example commands
Also updates the README entries to reflect that the Lavalink sample is current on DisCatSharp 10.7.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 0e74d22 commit e583363

3 files changed

Lines changed: 51 additions & 104 deletions

File tree

Lavalink/Commands/MusicCommands.cs

Lines changed: 49 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -17,45 +17,73 @@ namespace DisCatSharp.Examples.Lavalink.Commands;
1717
/// </summary>
1818
public class MusicCommands : ApplicationCommandsModule
1919
{
20-
/// <summary>
21-
/// Play music asynchronously.
22-
/// </summary>
23-
/// <param name="ctx">Interaction context</param>
24-
/// <param name="query">Search string or Youtube link</param>
25-
[SlashCommand("play", "Play music asynchronously")]
26-
public static async Task PlayAsync(InteractionContext ctx, [Option("query", "Search string or Youtube link")] string query)
20+
private static async Task<(LavalinkGuildPlayer Connection, bool Failed)> TryGetGuildPlayerAsync(BaseContext ctx)
2721
{
22+
if (ctx.Member?.VoiceState?.Channel == null)
23+
{
24+
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
25+
{
26+
IsEphemeral = true,
27+
Content = "You must be connected to a voice channel to use this command!"
28+
});
29+
return (null, true);
30+
}
31+
2832
var lava = ctx.Client.GetLavalink();
29-
var node = lava.ConnectedSessions.Values.First();
30-
var connection = node.GetGuildPlayer(ctx.Member.VoiceState.Guild);
33+
if (!lava.ConnectedSessions.Any())
34+
{
35+
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
36+
{
37+
IsEphemeral = true,
38+
Content = "The Lavalink connection is not established!"
39+
});
40+
return (null, true);
41+
}
3142

43+
var node = lava.ConnectedSessions.Values.First();
44+
var connection = node.GetGuildPlayer(ctx.Guild);
3245
if (connection == null)
3346
{
3447
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
3548
{
3649
IsEphemeral = true,
3750
Content = "The bot is not connected to the voice channel in this guild!"
3851
});
39-
return;
52+
return (null, true);
4053
}
4154

42-
if (ctx.Member.VoiceState == null || ctx.Member.VoiceState.Channel == null || ctx.Member.VoiceState.Channel != connection.Channel)
55+
if (ctx.Member.VoiceState.Channel != connection.Channel)
4356
{
4457
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
4558
{
4659
IsEphemeral = true,
4760
Content = "You must be in the same voice channel as the bot!"
4861
});
49-
return;
62+
return (null, true);
5063
}
5164

65+
return (connection, false);
66+
}
67+
68+
/// <summary>
69+
/// Play music asynchronously.
70+
/// </summary>
71+
/// <param name="ctx">Interaction context</param>
72+
/// <param name="query">Search string or Youtube link</param>
73+
[SlashCommand("play", "Play music asynchronously")]
74+
public static async Task PlayAsync(InteractionContext ctx, [Option("query", "Search string or Youtube link")] string query)
75+
{
76+
var (connection, failed) = await TryGetGuildPlayerAsync(ctx);
77+
if (failed)
78+
return;
79+
5280
LavalinkTrackLoadingResult tracks;
5381

5482
// Check if query is valid url
5583
if (Uri.TryCreate(query, UriKind.Absolute, out var uriResult) &&
5684
(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps))
5785
// Get track from the url
58-
tracks = await connection.LoadTracksAsync(uriResult.AbsolutePath);
86+
tracks = await connection.LoadTracksAsync(uriResult.AbsoluteUri);
5987
else
6088
// Search track in Youtube
6189
tracks = await connection.LoadTracksAsync(query);
@@ -92,29 +120,9 @@ public static async Task PlayAsync(InteractionContext ctx, [Option("query", "Sea
92120
[SlashCommand("pause", "Pause playback")]
93121
public static async Task PauseAsync(InteractionContext ctx)
94122
{
95-
var lava = ctx.Client.GetLavalink();
96-
var node = lava.ConnectedSessions.Values.First();
97-
var connection = node.GetGuildPlayer(ctx.Member.VoiceState.Guild);
98-
99-
if (connection == null)
100-
{
101-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
102-
{
103-
IsEphemeral = true,
104-
Content = "The bot is not connected to the voice channel in this guild!"
105-
});
106-
return;
107-
}
108-
109-
if (ctx.Member.VoiceState == null || ctx.Member.VoiceState.Channel == null || ctx.Member.VoiceState.Channel != connection.Channel)
110-
{
111-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
112-
{
113-
IsEphemeral = true,
114-
Content = "You must be in the same voice channel as the bot!"
115-
});
123+
var (connection, failed) = await TryGetGuildPlayerAsync(ctx);
124+
if (failed)
116125
return;
117-
}
118126

119127
// Pause playback
120128
await connection.PauseAsync();
@@ -132,29 +140,9 @@ public static async Task PauseAsync(InteractionContext ctx)
132140
[SlashCommand("resume", "Resume playback")]
133141
public static async Task ResumeAsync(InteractionContext ctx)
134142
{
135-
var lava = ctx.Client.GetLavalink();
136-
var node = lava.ConnectedSessions.Values.First();
137-
var connection = node.GetGuildPlayer(ctx.Member.VoiceState.Guild);
138-
139-
if (connection == null)
140-
{
141-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
142-
{
143-
IsEphemeral = true,
144-
Content = "The bot is not connected to the voice channel in this guild!"
145-
});
146-
return;
147-
}
148-
149-
if (ctx.Member.VoiceState == null || ctx.Member.VoiceState.Channel == null || ctx.Member.VoiceState.Channel != connection.Channel)
150-
{
151-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
152-
{
153-
IsEphemeral = true,
154-
Content = "You must be in the same voice channel as the bot!"
155-
});
143+
var (connection, failed) = await TryGetGuildPlayerAsync(ctx);
144+
if (failed)
156145
return;
157-
}
158146

159147
// Resume playback
160148
await connection.ResumeAsync();
@@ -172,29 +160,9 @@ public static async Task ResumeAsync(InteractionContext ctx)
172160
[SlashCommand("stop", "Stop playback")]
173161
public static async Task StopAsync(InteractionContext ctx)
174162
{
175-
var lava = ctx.Client.GetLavalink();
176-
var node = lava.ConnectedSessions.Values.First();
177-
var connection = node.GetGuildPlayer(ctx.Member.VoiceState.Guild);
178-
179-
if (connection == null)
180-
{
181-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
182-
{
183-
IsEphemeral = true,
184-
Content = "The bot is not connected to the voice channel in this guild!"
185-
});
163+
var (connection, failed) = await TryGetGuildPlayerAsync(ctx);
164+
if (failed)
186165
return;
187-
}
188-
189-
if (ctx.Member.VoiceState == null || ctx.Member.VoiceState.Channel == null || ctx.Member.VoiceState.Channel != connection.Channel)
190-
{
191-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
192-
{
193-
IsEphemeral = true,
194-
Content = "You must be in the same voice channel as the bot!"
195-
});
196-
return;
197-
}
198166

199167
await connection.StopAsync();
200168

@@ -212,30 +180,9 @@ public static async Task StopAsync(InteractionContext ctx)
212180
public static async Task PlayAsync(ContextMenuContext ctx)
213181
{
214182
var query = ctx.TargetMessage.Content;
215-
216-
var lava = ctx.Client.GetLavalink();
217-
var node = lava.ConnectedSessions.Values.First();
218-
var connection = node.GetGuildPlayer(ctx.Member.VoiceState.Guild);
219-
220-
if (connection == null)
221-
{
222-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
223-
{
224-
IsEphemeral = true,
225-
Content = "The bot is not connected to the voice channel in this guild!"
226-
});
227-
return;
228-
}
229-
230-
if (ctx.Member.VoiceState == null || ctx.Member.VoiceState.Channel == null || ctx.Member.VoiceState.Channel != connection.Channel)
231-
{
232-
await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new()
233-
{
234-
IsEphemeral = true,
235-
Content = "You must be in the same voice channel as the bot!"
236-
});
183+
var (connection, failed) = await TryGetGuildPlayerAsync(ctx);
184+
if (failed)
237185
return;
238-
}
239186

240187
LavalinkTrackLoadingResult tracks;
241188

Lavalink/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ To get the bot up and running, run the following command:
88
dotnet run <someBotTokenHere> <lavalinkHost> <lavalinkPass>
99
```
1010

11-
WARNING: This still runs with Lavalink V1, which is only in DisCatSharp 10.4.1
11+
This sample targets the current `DisCatSharp.Lavalink` package line used by the examples repository (`10.7.0`).

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ You need a compatible IDE to work with it, and .NET 9 SDK.
1313
| **ApplicationCommands** | Yes | Create and use application commands: registration, deferred responses, Components V2 cards, autocomplete, DI-backed tags, role/help cards, and other practical command patterns. |
1414
| **Interactivity** | Yes | Interactivity, components (buttons, select menus), threads, stages, owner-scoped panels, and a practical multi-step task workflow with modal capture and follow-up handling. |
1515
| **VoiceNext** | Yes | Play local audio files and record incoming voice into timeline-aware per-speaker WAV files with the modern `DisCatSharp.Voice` package. |
16-
| **Lavalink** | No | Play audio from YouTube in voice channels. |
16+
| **Lavalink** | Yes | Play audio from YouTube in voice channels with the current `DisCatSharp.Lavalink` package. |
1717
| **Hosting** | Yes | Initialize a bot as a service and surface host-backed runtime status through Components V2 cards. |
1818

1919
### Components V2 feature map

0 commit comments

Comments
 (0)