11using Application . Interfaces . Services ;
22using Domain . Common ;
33using Domain . Eventing ;
4- using Microsoft . Extensions . Configuration ;
4+ using Domain . Events ;
5+ using Infrastructure . Services ;
56using Microsoft . Extensions . DependencyInjection ;
67using NetCord . Rest ;
7- using NetCord . Services ;
88using NetCord . Services . ApplicationCommands ;
99using NetCord . Services . Commands ;
10+ using NetCord . Services . ComponentInteractions ;
1011using YoutubeExplode ;
1112using YoutubeExplode . Common ;
1213using Constants = Domain . Common . Constants ;
1314
1415namespace Infrastructure . Commands ;
1516
16- public class NetCordCommand ( IServiceProvider serviceProvider , IConfiguration configuration )
17+ public class NetCordCommand ( IServiceProvider serviceProvider , IMusicQueueService queue )
1718 : ApplicationCommandModule < ApplicationCommandContext >
1819{
19- [ SlashCommand ( "play" , "Play a track from SoundCloud " ) ]
20+ [ SlashCommand ( "play" , "Play a track from Youtube or a radio station " ) ]
2021 public async Task PingAsync ( [ CommandParameter ( Remainder = true ) ] string command )
2122 {
2223 if ( await NotInVoiceChannel ( ) )
2324 {
2425 return ;
2526 }
27+
2628 // `AddApplicationCommands()` registers services as singleton,
2729 // so scope is needed to resolve scoped services.
2830 using var scope = serviceProvider . CreateScope ( ) ;
2931 var youtubeClient = scope . ServiceProvider . GetRequiredService < YoutubeClient > ( ) ;
3032 var radioSourceService = scope . ServiceProvider . GetRequiredService < IRadioSourceService > ( ) ;
31-
33+
3234 var message = CreateMessage < InteractionMessageProperties > ( "Select a track to play:" ) ;
3335
3436 switch ( command )
@@ -69,9 +71,8 @@ public async Task Stop()
6971 {
7072 return ;
7173 }
72- using var scope = serviceProvider . CreateScope ( ) ;
73- var eventDispatcher = scope . ServiceProvider . GetRequiredService < IEventDispatcher > ( ) ;
74- eventDispatcher . Dispatch ( new EventType . Stop ( ) ) ;
74+
75+ DispatchEvent ( new EventType . Stop ( ) ) ;
7576 var message = CreateMessage < InteractionMessageProperties > ( "Stopping playback and clearing the queue." ) ;
7677 await RespondAsync ( InteractionCallback . Message ( message ) ) ;
7778 }
@@ -83,13 +84,47 @@ public async Task Skip()
8384 {
8485 return ;
8586 }
86- using var scope = serviceProvider . CreateScope ( ) ;
87- var eventDispatcher = scope . ServiceProvider . GetRequiredService < IEventDispatcher > ( ) ;
88- eventDispatcher . Dispatch ( new EventType . Skip ( ) ) ;
87+
88+ DispatchEvent ( new EventType . Skip ( ) ) ;
8989 var message = CreateMessage < InteractionMessageProperties > ( "Skipping the current track." ) ;
9090 await RespondAsync ( InteractionCallback . Message ( message ) ) ;
9191 }
9292
93+ [ SlashCommand ( "playlist" , "Show the current playlist" ) ]
94+ public async Task Playlist ( )
95+ {
96+ if ( await NotInVoiceChannel ( ) )
97+ {
98+ return ;
99+ }
100+
101+
102+ if ( queue . Count == 0 )
103+ await RespondAsync ( InteractionCallback . Message ( "No songs in queue." ) ) ;
104+ else
105+ {
106+ using var scope = serviceProvider . CreateScope ( ) ;
107+ var youtubeService = scope . ServiceProvider . GetRequiredKeyedService < IStreamService > ( nameof ( YoutubeService ) ) ;
108+ var songs = queue . GetAllRequests ( ) . Select ( async r =>
109+ {
110+ var title = await youtubeService . GetVideoTitleAsync (
111+ ( r . ContextAsObject as StringMenuInteractionContext ) ? . SelectedValues [ 0 ] , CancellationToken . None ) ;
112+ return title ;
113+ }
114+ ) . ToList ( ) ;
115+
116+ var titles = await Task . WhenAll ( songs ) ;
117+
118+ var response = "Queues: " + Environment . NewLine + string . Join ( Environment . NewLine ,
119+ titles . Select ( ( title , index ) =>
120+ {
121+ var isPlayingNowMsg = index == 0 ? "(Playing now)" : "" ;
122+ return $ "{ index + 1 } . { title } { isPlayingNowMsg } ";
123+ } ) ) ;
124+ await RespondAsync ( InteractionCallback . Message ( response ) ) ;
125+ }
126+ }
127+
93128 private static T CreateMessage < T > ( string message ) where T : IMessageProperties , new ( )
94129 {
95130 return new ( )
@@ -113,7 +148,7 @@ private static IEnumerable<IMessageComponentProperties> CreateComponent<T>(T sou
113148 }
114149 ] ;
115150 }
116-
151+
117152 private async Task < bool > NotInVoiceChannel ( )
118153 {
119154 if ( ! Context . Guild ! . VoiceStates . TryGetValue ( Context . User . Id , out _ ) )
@@ -127,5 +162,13 @@ private async Task<bool> NotInVoiceChannel()
127162 return false ;
128163 }
129164
165+ private void DispatchEvent < TEvent > ( TEvent @event ) where TEvent : IEvent
166+ {
167+ using var scope = serviceProvider . CreateScope ( ) ;
168+ var eventDispatcher = scope . ServiceProvider . GetRequiredService < IEventDispatcher > ( ) ;
169+
170+ eventDispatcher . Dispatch ( @event ) ;
171+ }
172+
130173 private record ComponentModel ( string Title , string Url , string ? Description = null ) ;
131174}
0 commit comments