1+ using System . Text . RegularExpressions ;
2+ using Slack_GPT_Socket . Settings ;
3+ using Slack_GPT_Socket . Utilities . LiteDB ;
4+ using SlackNet . Interaction ;
5+
6+ namespace Slack_GPT_Socket . Command ;
7+
8+ /// <summary>
9+ /// Custom command to list all commands, allows also for adding and removing commands.
10+ /// </summary>
11+ public class CommandsCommandStrategy : ICommandStrategy
12+ {
13+ private readonly IUserCommandDb _userCommandDb ;
14+
15+ public CommandsCommandStrategy ( IUserCommandDb userCommandDb )
16+ {
17+ _userCommandDb = userCommandDb ;
18+ }
19+
20+ public string Command => "commands" ;
21+
22+ /// <summary>
23+ /// Execute commands command
24+ /// </summary>
25+ /// <param name="command"></param>
26+ /// <returns></returns>
27+ public async Task < SlashCommandResponse > Execute ( SlashCommand command )
28+ {
29+ var restOfCommand = command . Text . Substring ( Command . Length ) . Trim ( ) ;
30+ var commandName = restOfCommand . Split ( " " ) [ 0 ] ;
31+ restOfCommand = restOfCommand . Substring ( commandName . Length ) . Trim ( ) ;
32+
33+ switch ( commandName )
34+ {
35+ case "" : // List all commands
36+ return ListCommands ( command ) ;
37+ case "add" : // Add a command
38+ return AddCommand ( command , restOfCommand ) ;
39+ case "remove" : // Remove a command
40+ return RemoveCommand ( command , restOfCommand ) ;
41+ case "help" : // Help for this command
42+ return HelpCommand ( command , restOfCommand ) ;
43+ }
44+
45+ return CommandStrategyUtils . SlashCommandResponse ( "Command not found." ) ;
46+ }
47+
48+ /// <summary>
49+ /// List help for commands prompt or details for a specific command
50+ /// </summary>
51+ /// <param name="command"></param>
52+ /// <param name="commandToHelp"></param>
53+ /// <returns></returns>
54+ private SlashCommandResponse HelpCommand ( SlashCommand command , string ? commandToHelp )
55+ {
56+
57+ if ( string . IsNullOrWhiteSpace ( commandToHelp ) )
58+ {
59+ return CommandStrategyUtils . SlashCommandResponse (
60+ "Usage: /commands [add|remove|help] [command] [prompt] [description] [options: -global]\n " +
61+ "Examples:\n " +
62+ "/commands add -test \" This is a test\" \" This is a test command\" \n " +
63+ "/commands remove -test\n " +
64+ "/commands help\n " +
65+ "/commands help -test\n " +
66+ "/commands" ) ;
67+ }
68+ else
69+ {
70+ var commandHelp = _userCommandDb . FindCommand ( commandToHelp , command . UserId ) ;
71+ return CommandStrategyUtils . SlashCommandResponse (
72+ $ "{ commandHelp . Command } \n " +
73+ $ "\t Prompt: { commandHelp . Prompt } \n " +
74+ $ "\t Description: { commandHelp . Description } \n " +
75+ $ "\t Is Global: { ( commandHelp . UserId == null ) } \n " +
76+ $ "\t As System: { commandHelp . AsSystem } ") ;
77+ }
78+ }
79+
80+ /// <summary>
81+ /// Removes a command
82+ /// </summary>
83+ /// <param name="command"></param>
84+ /// <param name="commandToRemove"></param>
85+ /// <returns></returns>
86+ private SlashCommandResponse RemoveCommand ( SlashCommand command , string commandToRemove )
87+ {
88+ var toRemove = _userCommandDb . FindCommand ( commandToRemove , command . UserId ) ;
89+ if ( toRemove == null ) return CommandStrategyUtils . SlashCommandResponse ( $ "Command { commandToRemove } not found.") ;
90+ _userCommandDb . RemoveCommand ( toRemove ) ;
91+ return CommandStrategyUtils . SlashCommandResponse ( $ "Removed command { commandToRemove } .") ;
92+ }
93+
94+ /// <summary>
95+ /// New command should be in the format "add command prompt description" or "add command prompt"
96+ /// Prompt and Description can have " " in them, so we need to split on " " and then recombine the last two
97+ /// You can escape " " with \ within the prompt and description. Eg: add test "this is a \"test\""
98+ /// </summary>
99+ /// <param name="command"></param>
100+ /// <param name="restOfCommand"></param>
101+ /// <returns></returns>
102+ private SlashCommandResponse AddCommand ( SlashCommand command , string restOfCommand )
103+ {
104+ var commandToAdd = restOfCommand ;
105+ var isGlobal = false ;
106+ var matches = Regex . Matches ( commandToAdd ,
107+ @"[^\s""']+|""([^""\\]*(?:\\.[^""\\]*)*)""|'([^'\\]*(?:\\.[^'\\]*)*)'" ) ;
108+
109+ var splitCommand = matches . Select ( m => m . Value ) . ToArray ( ) ;
110+
111+ // If there is "-global" parameter after prompt or description, then this is a global command.
112+ // There can be more then one option
113+ var options = splitCommand [ 1 ..] . Where ( c => c . StartsWith ( "-" ) ) . ToArray ( ) ;
114+ if ( options . Contains ( "-global" ) ) isGlobal = true ;
115+
116+
117+ GptUserCommand newCommand = new ( )
118+ {
119+ Command = splitCommand [ 0 ] ,
120+ Description = splitCommand . Length > 2
121+ ? splitCommand [ 2 ]
122+ : "" ,
123+ Prompt = splitCommand . Length > 1
124+ ? splitCommand [ 1 ]
125+ : "" ,
126+ UserId = isGlobal
127+ ? null
128+ : command . UserId
129+ } ;
130+ _userCommandDb . AddCommand ( newCommand ) ;
131+ return CommandStrategyUtils . SlashCommandResponse (
132+ $ "Added command { newCommand . Command } described as { newCommand . Description } \n " +
133+ $ "\t Prompt:{ newCommand . Prompt } .\n " +
134+ $ "\t Global: { isGlobal } ") ;
135+ }
136+
137+ /// <summary>
138+ /// List all commands
139+ /// </summary>
140+ /// <param name="command"></param>
141+ /// <returns></returns>
142+ private SlashCommandResponse ListCommands ( SlashCommand command )
143+ {
144+ var commands = _userCommandDb . GetAllCommands ( command . UserId ) ;
145+ var globalCommands = _userCommandDb . GetAllCommands ( ) ;
146+ var allCommands = commands . Concat ( globalCommands ) . ToList ( ) ;
147+
148+ if ( ! allCommands . Any ( ) ) return CommandStrategyUtils . SlashCommandResponse ( "No commands found." ) ;
149+
150+ var commandList = string . Join ( "\n " ,
151+ allCommands . Select ( c => $ "{ c . Command } [{ ( c . UserId == null ? "Global" : "User" ) } ]") ) ;
152+ return CommandStrategyUtils . SlashCommandResponse ( commandList ) ;
153+ }
154+ }
0 commit comments