11package net .discordjug .javabot .systems .help .commands ;
22
3+ import java .util .List ;
4+
5+ import org .jetbrains .annotations .NotNull ;
6+
7+ import net .discordjug .javabot .annotations .AutoDetectableComponentHandler ;
38import net .discordjug .javabot .data .config .BotConfig ;
49import net .discordjug .javabot .data .h2db .DbActions ;
510import net .discordjug .javabot .systems .help .HelpManager ;
611import net .discordjug .javabot .systems .help .dao .HelpAccountRepository ;
712import net .discordjug .javabot .systems .help .dao .HelpTransactionRepository ;
813import net .discordjug .javabot .systems .user_preferences .UserPreferenceService ;
14+ import net .discordjug .javabot .util .ExceptionLogger ;
915import net .discordjug .javabot .util .Responses ;
1016import net .dv8tion .jda .api .entities .channel .ChannelType ;
1117import net .dv8tion .jda .api .entities .channel .concrete .ThreadChannel ;
18+ import net .dv8tion .jda .api .entities .channel .unions .MessageChannelUnion ;
19+ import net .dv8tion .jda .api .events .interaction .ModalInteractionEvent ;
1220import net .dv8tion .jda .api .events .interaction .command .SlashCommandInteractionEvent ;
13- import net .dv8tion .jda .api .interactions .commands .CommandInteraction ;
21+ import net .dv8tion .jda .api .interactions .Interaction ;
22+ import net .dv8tion .jda .api .interactions .callbacks .IReplyCallback ;
1423import net .dv8tion .jda .api .interactions .commands .OptionMapping ;
1524import net .dv8tion .jda .api .interactions .commands .OptionType ;
1625import net .dv8tion .jda .api .interactions .commands .build .Commands ;
17-
18- import org .jetbrains .annotations .NotNull ;
26+ import net .dv8tion .jda .api .interactions .components .ActionRow ;
27+ import net .dv8tion .jda .api .interactions .components .text .TextInput ;
28+ import net .dv8tion .jda .api .interactions .components .text .TextInputStyle ;
29+ import net .dv8tion .jda .api .interactions .modals .Modal ;
30+ import net .dv8tion .jda .api .interactions .modals .ModalMapping ;
1931import xyz .dynxsty .dih4jda .interactions .commands .application .SlashCommand ;
32+ import xyz .dynxsty .dih4jda .interactions .components .ModalHandler ;
33+ import xyz .dynxsty .dih4jda .util .ComponentIdBuilder ;
2034
2135/**
2236 * A simple command that can be used inside reserved help channels to
2337 * immediately unreserve them, instead of waiting for a timeout.
2438 */
25- public class UnreserveCommand extends SlashCommand {
39+ @ AutoDetectableComponentHandler (UnreserveCommand .UNRESERVE_ID )
40+ public class UnreserveCommand extends SlashCommand implements ModalHandler {
41+ private static final String UNRESERVE_ID = "unreserve" ;
42+ private static final String REASON_ID = "reason" ;
2643 private final BotConfig botConfig ;
2744 private final DbActions dbActions ;
2845 private final HelpAccountRepository helpAccountRepository ;
@@ -43,40 +60,75 @@ public UnreserveCommand(BotConfig botConfig, DbActions dbActions, HelpTransactio
4360 this .helpAccountRepository = helpAccountRepository ;
4461 this .helpTransactionRepository = helpTransactionRepository ;
4562 this .preferenceService = preferenceService ;
46- setCommandData (Commands .slash ("unreserve" , "Unreserves this post marking your question/issue as resolved." )
63+ setCommandData (Commands .slash (UNRESERVE_ID , "Unreserves this post marking your question/issue as resolved." )
4764 .setGuildOnly (true )
48- .addOption (OptionType .STRING , "reason" , "The reason why you're unreserving this channel" , false )
65+ .addOption (OptionType .STRING , REASON_ID , "The reason why you're unreserving this channel" , false )
4966 );
5067 }
5168
5269 @ Override
5370 public void execute (@ NotNull SlashCommandInteractionEvent event ) {
71+ String reason = event .getOption (REASON_ID , null , OptionMapping ::getAsString );
72+ onCloseRequest (event , event , event .getChannel (), reason , ()->{
73+ TextInput reasonInput = TextInput
74+ .create (REASON_ID , "Reason" , TextInputStyle .SHORT )
75+ .setRequiredRange (11 , 100 )
76+ .setRequired (true )
77+ .setPlaceholder ("Please enter the reason you are closing this post here" )
78+ .build ();
79+ Modal modal = Modal
80+ .create (ComponentIdBuilder .build (UNRESERVE_ID ), "Close post" )
81+ .addComponents (ActionRow .of (
82+ reasonInput ))
83+ .build ();
84+ event .replyModal (modal ).queue ();
85+ });
86+ }
87+
88+ @ Override
89+ public void handleModal (ModalInteractionEvent event , List <ModalMapping > values ) {
90+ values
91+ .stream ()
92+ .filter (mapping -> REASON_ID .equals (mapping .getId ()))
93+ .map (mapping -> mapping .getAsString ())
94+ .findAny ()
95+ .ifPresentOrElse (reason -> {
96+ onCloseRequest (event , event , event .getChannel (), reason , ()->{
97+ Responses .error (event , "An error occured - The reason field is missing." ).queue ();
98+ ExceptionLogger .capture (new IllegalStateException ("A reason was expected but not present" ), getClass ().getName ());
99+ });
100+ }, () -> Responses .warning (event , "A reason must be provided" ).queue ());
101+
102+ }
103+
104+ private void onCloseRequest (Interaction interaction , IReplyCallback replyCallback , MessageChannelUnion channel , String reason , Runnable noReasonHandler ) {
105+ ChannelType channelType = channel .getType ();
54106 // check whether the channel type is either text or thread (possible forum post?)
55- if (event . getChannelType () != ChannelType .TEXT && event . getChannelType () != ChannelType .GUILD_PUBLIC_THREAD ) {
56- replyInvalidChannel (event );
107+ if (channelType != ChannelType .TEXT && channelType != ChannelType .GUILD_PUBLIC_THREAD ) {
108+ replyInvalidChannel (replyCallback );
57109 return ;
58110 }
59- ThreadChannel postThread = event . getChannel () .asThreadChannel ();
111+ ThreadChannel postThread = channel .asThreadChannel ();
60112 if (postThread .getParentChannel ().getType () != ChannelType .FORUM ) {
61- replyInvalidChannel (event );
113+ replyInvalidChannel (replyCallback );
114+ return ;
62115 }
63116 HelpManager manager = new HelpManager (postThread , dbActions , botConfig , helpAccountRepository , helpTransactionRepository , preferenceService );
64- if (manager .isForumEligibleToBeUnreserved (event .getInteraction ())) {
65- String reason = event .getOption ("reason" , null , OptionMapping ::getAsString );
66- if (event .getUser ().getIdLong () != postThread .getOwnerIdLong () && reason == null ) {
67- Responses .warning (event , "Could not close this post" , "Closing a post of another user requires a reason to be set." ).queue ();
117+ if (manager .isForumEligibleToBeUnreserved (interaction )) {
118+ if (replyCallback .getUser ().getIdLong () != postThread .getOwnerIdLong () && reason == null ) {
119+ noReasonHandler .run ();
68120 return ;
69121 }
70- manager .close (event ,
71- event .getUser ().getIdLong () == manager .getPostThread ().getOwnerIdLong (),
122+ manager .close (replyCallback ,
123+ replyCallback .getUser ().getIdLong () == manager .getPostThread ().getOwnerIdLong (),
72124 reason );
73125 } else {
74- Responses .warning (event , "Could not close this post" , "You're not allowed to close this post." ).queue ();
126+ Responses .warning (replyCallback , "Could not close this post" , "You're not allowed to close this post." ).queue ();
75127 }
76128 }
77129
78- private void replyInvalidChannel (CommandInteraction interaction ) {
79- Responses .warning (interaction , "Invalid Channel" ,
130+ private void replyInvalidChannel (IReplyCallback replyCallback ) {
131+ Responses .warning (replyCallback , "Invalid Channel" ,
80132 "This command may only be used in either the text-channel-based help system, or in our new forum help system." )
81133 .queue ();
82134 }
0 commit comments