@@ -15,7 +15,7 @@ internal static class DebugCommands
1515 [ Description ( "Evaluate C# code!" ) ]
1616 public static async Task DebugEvalCommandAsync ( SlashCommandContext ctx ,
1717 [ SlashAutoCompleteProvider ( typeof ( DebugHistoryAutoCompleteProvider ) ) ]
18- [ Parameter ( "code" ) , Description ( "The code to evaluate." ) ] string code )
18+ [ Parameter ( "code" ) , Description ( "The code to evaluate." ) ] string codeOrHistoryEntryId )
1919 {
2020 CancellationToken cancellationToken = default ;
2121
@@ -25,23 +25,15 @@ await ctx.RespondAsync(new DiscordInteractionResponseBuilder(builder)
2525 . AsEphemeral ( ephemeral : ctx . Interaction . ShouldUseEphemeralResponse ( false ) ) ) ;
2626 var msg = await ctx . GetResponseAsync ( ) ;
2727
28+ string code = await ResolveInputAsync ( codeOrHistoryEntryId , EvaluatorInputType . CSharp ) ;
29+
2830 if ( Setup . Eval . RestrictedTerms . Any ( code . Contains ) )
2931 {
3032 await ctx . EditResponseAsync ( new DiscordMessageBuilder ( ) . WithContent ( "You can't do that." ) ) ;
3133 return ;
3234 }
3335
34- var currentHistoryEntries = await Setup . Storage . Redis . HashGetAllAsync ( "evalHistory" ) ;
35- var matchingEntry = currentHistoryEntries . FirstOrDefault ( x => x . Value == code ) ;
36- if ( matchingEntry == default )
37- {
38- await Setup . Storage . Redis . HashSetAsync ( "evalHistory" , DateTime . UtcNow . ToString ( ) , code ) ;
39- }
40- else
41- {
42- await Setup . Storage . Redis . HashDeleteAsync ( "evalHistory" , matchingEntry . Name ) ;
43- await Setup . Storage . Redis . HashSetAsync ( "evalHistory" , DateTime . UtcNow . ToString ( ) , matchingEntry . Value ) ;
44- }
36+ await StoreHistoryEntryAsync ( code , EvaluatorInputType . CSharp ) ;
4537
4638 try
4739 {
@@ -112,7 +104,7 @@ [new DiscordButtonComponent(DiscordButtonStyle.Danger, "button-callback-eval-can
112104 public static async Task DebugShellCommandAsync ( SlashCommandContext ctx ,
113105 [ SlashAutoCompleteProvider ( typeof ( DebugHistoryAutoCompleteProvider ) ) ]
114106 [ Parameter ( "command" ) , Description ( "The command to run, including any arguments." ) ]
115- string command )
107+ string commandOrHistoryEntryId )
116108 {
117109 await ctx . RespondAsync ( new DiscordInteractionResponseBuilder ( )
118110 . WithContent ( "Working on it..." )
@@ -122,6 +114,8 @@ [new DiscordButtonComponent(DiscordButtonStyle.Danger, "button-callback-eval-can
122114 . AsEphemeral ( ephemeral : ctx . Interaction . ShouldUseEphemeralResponse ( false ) )
123115 ) ;
124116
117+ var command = await ResolveInputAsync ( commandOrHistoryEntryId , EvaluatorInputType . Shell ) ;
118+
125119 if ( Setup . Eval . RestrictedTerms . Any ( command . Contains ) )
126120 if ( ! Setup . State . Discord . Client . CurrentApplication . Owners . Contains ( ctx . User ) )
127121 {
@@ -138,17 +132,7 @@ [new DiscordButtonComponent(DiscordButtonStyle.Danger, "button-callback-eval-can
138132 return ;
139133 }
140134
141- var currentHistoryEntries = await Setup . Storage . Redis . HashGetAllAsync ( "shellHistory" ) ;
142- var matchingEntry = currentHistoryEntries . FirstOrDefault ( x => x . Value == command ) ;
143- if ( matchingEntry == default )
144- {
145- await Setup . Storage . Redis . HashSetAsync ( "shellHistory" , DateTime . UtcNow . ToString ( ) , command ) ;
146- }
147- else
148- {
149- await Setup . Storage . Redis . HashDeleteAsync ( "shellHistory" , matchingEntry . Name ) ;
150- await Setup . Storage . Redis . HashSetAsync ( "shellHistory" , DateTime . UtcNow . ToString ( ) , matchingEntry . Value ) ;
151- }
135+ await StoreHistoryEntryAsync ( command , EvaluatorInputType . Shell ) ;
152136
153137 var msg = await ctx . GetResponseAsync ( ) ;
154138 Setup . State . Caches . CancellationTokens . Add ( msg . Id , new CancellationTokenSource ( ) ) ;
@@ -751,7 +735,7 @@ await ctx.RespondAsync(new DiscordInteractionResponseBuilder()
751735
752736 #endregion commands
753737
754- #region '/debug shell' utilities
738+ #region eval and shell utilities
755739
756740 private static async Task < ShellCommandResult > RunShellCommandAsync ( string command , CancellationToken cancellationToken )
757741 {
@@ -840,7 +824,60 @@ internal ShellCommandResult(int exitCode, string output)
840824 internal string Error { get ; private set ; }
841825 }
842826
843- #endregion '/debug shell' utilities
827+ private static async Task < string > ResolveInputAsync ( string input , EvaluatorInputType inputType )
828+ {
829+ if ( ! Setup . Constants . RegularExpressions . DiscordIdPattern . IsMatch ( input ) )
830+ return input ;
831+
832+ string redisHashName ;
833+ if ( inputType == EvaluatorInputType . CSharp )
834+ redisHashName = "evalHistory" ;
835+ else if ( inputType == EvaluatorInputType . Shell )
836+ redisHashName = "shellHistory" ;
837+ else
838+ throw new ArgumentException ( "Invalid input type. Unable to read from history." ) ;
839+
840+ Dictionary < ulong , string > historyEntries = ( await Setup . Storage . Redis . HashGetAllAsync ( redisHashName ) )
841+ . ToDictionary ( x => Convert . ToUInt64 ( x . Name ) , x => x . Value . ToString ( ) ) ;
842+
843+ if ( historyEntries . TryGetValue ( Convert . ToUInt64 ( input ) , out string historyEntry ) )
844+ return historyEntry ;
845+ else
846+ return input ;
847+ }
848+
849+ private static async Task StoreHistoryEntryAsync ( string input , EvaluatorInputType inputType )
850+ {
851+ string redisHashName ;
852+ if ( inputType == EvaluatorInputType . CSharp )
853+ redisHashName = "evalHistory" ;
854+ else if ( inputType == EvaluatorInputType . Shell )
855+ redisHashName = "shellHistory" ;
856+ else
857+ throw new ArgumentException ( "Invalid input type. Unable to store history entry." ) ;
858+
859+ HashEntry [ ] currentHistoryEntries = await Setup . Storage . Redis . HashGetAllAsync ( redisHashName ) ;
860+ var matchingEntry = currentHistoryEntries . FirstOrDefault ( x => x . Value == input ) ;
861+ if ( matchingEntry == default )
862+ {
863+ await Setup . Storage . Redis . HashSetAsync ( redisHashName , DSharpPlus . Utilities . CreateSnowflake ( DateTime . UtcNow ) , input ) ;
864+ }
865+ else
866+ {
867+ await Setup . Storage . Redis . HashDeleteAsync ( redisHashName , matchingEntry . Name ) ;
868+ await Setup . Storage . Redis . HashSetAsync ( redisHashName , DSharpPlus . Utilities . CreateSnowflake ( DateTime . UtcNow ) , matchingEntry . Value ) ;
869+ }
870+ }
871+
872+ #endregion eval and shell utilities
873+
874+ #region enums
875+ private enum EvaluatorInputType
876+ {
877+ CSharp ,
878+ Shell
879+ }
880+ #endregion enums
844881
845882 #region choice/autocomplete providers
846883
@@ -922,8 +959,8 @@ public async ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync
922959 else
923960 return default ;
924961 return historyEntries . Where ( x => x . Value . ToString ( ) . Contains ( focusedOption . Value . ToString ( ) , StringComparison . OrdinalIgnoreCase ) )
925- . OrderByDescending ( x => Convert . ToDateTime ( x . Name . ToString ( ) ) )
926- . Select ( x => new DiscordAutoCompleteChoice ( x . Value . ToString ( ) , x . Value . ToString ( ) ) )
962+ . OrderByDescending ( x => x . Name )
963+ . Select ( x => new DiscordAutoCompleteChoice ( x . Value . ToString ( ) . Truncate ( 100 ) , x . Name . ToString ( ) ) )
927964 . Take ( 25 ) . ToList ( ) ;
928965 }
929966 return default ;
0 commit comments