1+ using AutoCommand . Handler ;
2+ using Courier . Configuration ;
3+ using Courier . Extensions ;
4+ using Courier . Models ;
5+ using Courier . Utilities ;
6+ using Discord ;
7+ using Discord . WebSocket ;
8+ using Microsoft . Extensions . Options ;
9+ using Serilog ;
10+
11+ namespace Courier . Commands ;
12+
13+ public class AddCommand ( IOptionsMonitor < FeedOptions > optionsMonitor , DiscordSocketClient client ) : ICommandHandler
14+ {
15+ public async Task HandleAsync (
16+ ILogger logger ,
17+ SocketSlashCommand command ,
18+ CancellationToken cancellationToken = new ( ) )
19+ {
20+ var commandOptions = command . Data . Options ;
21+ var interval = command . Data . Options
22+ . Where ( option => option . Name == "interval" )
23+ . Select ( option => option . Value )
24+ . Cast < long > ( )
25+ . FirstOrDefault ( Feed . DefaultInterval ) ;
26+ var channel = await command . Data . Options
27+ . Where ( option => option . Name == "channel" )
28+ . Select ( option => option . Value )
29+ . OfType < ITextChannel > ( )
30+ . ToAsyncEnumerable ( )
31+ . FirstOrDefaultAwaitAsync ( async textChannel =>
32+ {
33+ var user = await textChannel . GetUserAsync (
34+ client . CurrentUser . Id ,
35+ options : new RequestOptions { CancelToken = cancellationToken } ) ;
36+ var permissions = user ? . GetPermissions ( textChannel ) ;
37+
38+ return permissions is { SendMessages : true , EmbedLinks : true } ;
39+ } , cancellationToken ) ;
40+
41+ if ( ! Validate < string > ( commandOptions . First ( option => option . Name == "name" ) , out var name ) ||
42+ name is null )
43+ {
44+ await command . RespondEphemeralAsync (
45+ Resources . AddCommandNameOptionRequired ,
46+ cancellationToken : cancellationToken ) ;
47+ return ;
48+ }
49+
50+ if ( ! Validate < Uri > ( commandOptions . First ( option => option . Name == "uri" ) , out var uri ) ||
51+ uri is null )
52+ {
53+ await command . RespondEphemeralAsync (
54+ Resources . AddCommandUriOptionRequired ,
55+ cancellationToken : cancellationToken ) ;
56+ return ;
57+ }
58+
59+ if ( channel is null )
60+ {
61+ await command . RespondEphemeralAsync (
62+ Resources . AddCommandChannelOptionRequired ,
63+ cancellationToken : cancellationToken ) ;
64+ return ;
65+ }
66+
67+ var feeds = new HashSet < Feed > ( optionsMonitor . CurrentValue . Feeds ) ;
68+ var newFeed = new Feed
69+ {
70+ Name = name ,
71+ Uri = uri . AbsoluteUri ,
72+ ChannelId = channel . Id ,
73+ Interval = interval
74+ } ;
75+
76+ if ( ! feeds . Add ( newFeed ) )
77+ {
78+ await command . RespondEphemeralAsync (
79+ Resources . AddCommandFeedAlreadyExists ,
80+ cancellationToken : cancellationToken ) ;
81+ return ;
82+ }
83+
84+ await JsonWriter . UpdateFeedsAsync ( feeds , optionsMonitor . CurrentValue . FilePath , cancellationToken ) ;
85+ await command . RespondEphemeralAsync (
86+ Resources . AddCommandFeedAdded ,
87+ cancellationToken : cancellationToken ) ;
88+ }
89+
90+ public string CommandName => "add" ;
91+
92+ private static bool Validate < T > ( SocketSlashCommandDataOption option , out T ? value ) where T : class
93+ {
94+ switch ( option . Value )
95+ {
96+ case string s when typeof ( T ) == typeof ( string ) :
97+ value = Convert . ChangeType ( s , typeof ( T ) ) as T ;
98+ return ! string . IsNullOrWhiteSpace ( s ) ;
99+ case string u when typeof ( T ) == typeof ( Uri ) :
100+ u = u . Trim ( ) ;
101+ u = u . StartsWith ( "https://" ) || u . StartsWith ( "http://" ) ? u : "https://" + u ;
102+ var isValid = Uri . TryCreate ( u , UriKind . Absolute , out var uri ) ;
103+ value = Convert . ChangeType ( uri , typeof ( T ) ) as T ;
104+ return isValid && ( uri ? . Scheme == Uri . UriSchemeHttp || uri ? . Scheme == Uri . UriSchemeHttps ) ;
105+ case long l when typeof ( T ) == typeof ( long ) :
106+ value = Convert . ChangeType ( l , typeof ( T ) ) as T ;
107+ return l > 0 ;
108+ }
109+
110+ value = null ;
111+ return false ;
112+ }
113+ }
0 commit comments