44import lombok .Value ;
55import lombok .experimental .NonFinal ;
66import net .dv8tion .jda .api .entities .IMentionable ;
7+ import net .dv8tion .jda .api .entities .ISnowflake ;
78import net .dv8tion .jda .api .entities .Message ;
89import net .dv8tion .jda .api .entities .Role ;
910import net .dv8tion .jda .api .entities .User ;
1617import net .dv8tion .jda .api .events .interaction .component .EntitySelectInteractionEvent ;
1718import net .dv8tion .jda .api .events .interaction .component .StringSelectInteractionEvent ;
1819import net .dv8tion .jda .api .hooks .ListenerAdapter ;
20+ import net .dv8tion .jda .api .interactions .InteractionHook ;
1921import net .dv8tion .jda .api .interactions .components .buttons .Button ;
2022import net .dv8tion .jda .api .interactions .components .selections .EntitySelectMenu ;
2123import net .dv8tion .jda .api .interactions .components .selections .StringSelectMenu ;
4244import org .comroid .api .tree .UncheckedCloseable ;
4345import org .jetbrains .annotations .NotNull ;
4446
47+ import java .io .BufferedReader ;
4548import java .io .File ;
49+ import java .io .FileInputStream ;
4650import java .io .FileOutputStream ;
51+ import java .io .InputStreamReader ;
4752import java .lang .reflect .Field ;
4853import java .nio .charset .StandardCharsets ;
4954import java .time .Instant ;
5055import java .util .Arrays ;
5156import java .util .List ;
5257import java .util .NoSuchElementException ;
58+ import java .util .Objects ;
5359import java .util .Optional ;
60+ import java .util .concurrent .CompletableFuture ;
5461import java .util .stream .Collectors ;
5562import java .util .stream .IntStream ;
63+ import java .util .stream .Stream ;
5664
5765import static org .comroid .api .Polyfill .*;
5866import static org .comroid .api .text .Markdown .*;
@@ -87,10 +95,46 @@ public T initialize() {
8795 }
8896
8997 public void reload () {
90- if (ftime ().isBefore (timestamp )) return ; // reload is not necessary
98+ reload (false );
99+ }
100+
101+ @ SneakyThrows
102+ public void reload (boolean force ) {
103+ if (!force && ftime ().isBefore (timestamp )) return ; // reload is not necessary
104+
105+ DataNode node ;
106+ try (var fis = new FileInputStream (file ); var isr = new InputStreamReader (fis ); var br = new BufferedReader (isr )) {
107+ var data = br .lines ().collect (Collectors .joining ());
108+ node = Objects .requireNonNull (dataType .getDeserializer (), "No deserializer set for " + dataType ).apply (data );
109+ }
110+ Objects .requireNonNull (node , "No data" );
111+
112+ setSelfAndChildrenRecursive (struct , config , node );
91113 this .timestamp = Instant .now ();
92114 }
93115
116+ private void setSelfAndChildrenRecursive (DataStructure <?> struct , Object it , DataNode data ) {
117+ for (var property : struct .getProperties ()) {
118+ var node = data .get (property .getName ());
119+ var propType = property .getType ();
120+ Object value = null ;
121+ Class <?>[] classes = null ;
122+ if (property .isAnnotationPresent (Adapt .class )) {
123+ classes = property .getAnnotation (Adapt .class ).value ();
124+ JITAssistant .prepare (classes );
125+ value = Arrays .stream (classes )
126+ .flatMap ($ -> Stream .ofNullable (TypeAdapter .CACHE .getOrDefault (property .getType ().getTargetClass (), null )))
127+ .flatMap (adp -> adp .deserialize (context , adp .parseSerialized (node .asString ())).stream ())
128+ .findAny ()
129+ .orElse (null );
130+ }
131+ if (propType .isStandard () || classes != null ) {
132+ if (propType .isStandard () && value == null ) value = propType .parse (node .asString ());
133+ property .setFor (it , uncheckedCast (value ));
134+ } else setSelfAndChildrenRecursive (DataStructure .of (propType .getTargetClass ()), property .getFrom (it ), node );
135+ }
136+ }
137+
94138 @ SneakyThrows
95139 public void save () {
96140 DataNode data = config ;
@@ -138,7 +182,7 @@ private void invokePropertyAdaptersRecursive(Context context, DataNode node, Dat
138182 @ SuppressWarnings ("OptionalOfNullableMisuse" ) var value = Optional .ofNullable (node .get (key ))
139183 .map (n -> n .as (adp .getSerialized ()))
140184 .orElseGet (() -> Annotations .defaultValue (property ));
141- return adp .deserialize (context , uncheckedCast (value ));
185+ return adp .deserialize (context , uncheckedCast (value )). get () ;
142186 }).ifPresent (value -> property .setFor (it , uncheckedCast (value )));
143187 }
144188 }
@@ -156,18 +200,17 @@ default void resend() {
156200
157201 @ Value
158202 @ NonFinal
159- public class Presentation$JDA implements Presentation {
160- InteractionHandler handler = new InteractionHandler ();
161- TextChannel channel ;
203+ public class Presentation$JDA extends ListenerAdapter implements Presentation {
204+ TextChannel channel ;
162205
163206 public Presentation$JDA (TextChannel channel ) {
164207 this .channel = channel ;
165208
166- channel .getJDA ().addEventListener (handler );
209+ channel .getJDA ().addEventListener (this );
167210 }
168211
169212 protected boolean checkOutOfContext (GenericInteractionCreateEvent event ) {
170- return true ;
213+ return false ;
171214 }
172215
173216 @ Override
@@ -193,10 +236,10 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
193236 Debug .log (fullName + " is not null" );
194237
195238 var title = IntStream .range (0 , level ).mapToObj ($ -> "#" ).collect (Collectors .joining ()) + " Config Value " + Code .apply (fullName );
239+ var desc = property .getDescription ();
240+ var current = property .getFrom (it );
196241 var propType = property .getType ();
197242 var propClass = propType .getTargetClass ();
198- var current = property .getFrom (it );
199- var desc = property .getDescription ();
200243 var text = """
201244 %s%s
202245 ```
@@ -218,6 +261,7 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
218261
219262 // set default value
220263 EntitySelectMenu .DefaultValue def ;
264+ if (current instanceof ISnowflake flake ) current = flake .getIdLong ();
221265 if (current != null ) {
222266 var currentId = (long ) current ;
223267 if (Channel .class .isAssignableFrom (propClass )) def = EntitySelectMenu .DefaultValue .channel (currentId );
@@ -266,41 +310,46 @@ private void sendAttributeMessageRecursive(String fullName, DataStructure<?>.Pro
266310
267311 @ Override
268312 public void close () {
269- channel .getJDA ().removeEventListener (handler );
313+ channel .getJDA ().removeEventListener (this );
270314 }
271315
272- private final class InteractionHandler extends ListenerAdapter {
273- @ Override
274- public void onButtonInteraction ( @ NotNull ButtonInteractionEvent event ) {
275- if (checkOutOfContext (event )) return ;
316+ @ Override
317+ public void onButtonInteraction ( @ NotNull ButtonInteractionEvent event ) {
318+ event . deferReply (). submit (). thenCompose ( hook -> {
319+ if (checkOutOfContext (event )) return CompletableFuture . completedFuture ( null ) ;
276320 event .replyModal (Modal .create (event .getComponentId (), event .getComponentId ())
277321 .addActionRow (TextInput .create ("newValue" , "New Value" , TextInputStyle .SHORT )
278322 .setPlaceholder (config .get (event .getComponentId ().split ("\\ ." )).asString ())
279323 .build ())
280324 .build ()).queue ();
281- }
325+ return hook .sendMessage ("Done!" ).setEphemeral (false ).submit ();
326+ }).exceptionally (Debug .exceptionLogger ("Internal error when handling interaction" ));
327+ }
282328
283- @ Override
284- public void onModalInteraction (@ NotNull ModalInteractionEvent event ) {
285- if (checkOutOfContext (event )) return ;
329+ @ Override
330+ public void onModalInteraction (@ NotNull ModalInteractionEvent event ) {
331+ event .deferReply ().submit ().thenCompose (hook -> {
332+ if (checkOutOfContext (event )) return CompletableFuture .completedFuture (null );
286333 var path = event .getModalId ().split ("\\ ." );
287334 var locals = descend (path );
288335 var propType = locals .getFirst ().getType ();
289336
290337 var newValue = event .getValue ("newValue" ).getAsString ();
291338 locals .getFirst ().setFor (locals .getSecond (), uncheckedCast (propType .parse (String .valueOf (newValue ))));
292339 save ();
293- resend ();
294- }
340+ return updateDisplayValue (event .getMessage (), hook , newValue );
341+ }).exceptionally (Debug .exceptionLogger ("Internal error when handling interaction" ));
342+ }
295343
296- @ Override
297- public void onStringSelectInteraction (@ NotNull StringSelectInteractionEvent event ) {
298- if (checkOutOfContext (event )) return ;
344+ @ Override
345+ public void onStringSelectInteraction (@ NotNull StringSelectInteractionEvent event ) {
346+ event .deferReply ().submit ().thenCompose (hook -> {
347+ if (checkOutOfContext (event )) return CompletableFuture .completedFuture (null );
299348 var path = event .getComponentId ().split ("\\ ." );
300349 var locals = descend (path );
301350 var propType = locals .getFirst ().getType ();
302351
303- event .getValues ()
352+ return event .getValues ()
304353 .stream ()
305354 .flatMap (value -> Arrays .stream (propType .getTargetClass ().getFields ())
306355 .filter (Field ::isEnumConstant )
@@ -310,37 +359,52 @@ public void onStringSelectInteraction(@NotNull StringSelectInteractionEvent even
310359 .orElseGet (field ::getName ))))
311360 .findAny ()
312361 .map (ThrowingFunction .sneaky (field -> field .get (null )))
313- .ifPresent (value -> locals .getFirst ().setFor (locals .getSecond (), uncheckedCast (value )));
314- save ();
315- resend ();
316- }
362+ .map (value -> {
363+ locals .getFirst ().setFor (locals .getSecond (), uncheckedCast (value ));
364+ save ();
365+ return updateDisplayValue (event .getMessage (), hook , value );
366+ })
367+ .orElseGet (() -> failedFuture (new IllegalArgumentException ("Invalid values: " + event .getValues ())));
368+ }).exceptionally (Debug .exceptionLogger ("Internal error when handling interaction" ));
369+ }
317370
318- @ Override
319- public void onEntitySelectInteraction (@ NotNull EntitySelectInteractionEvent event ) {
320- if (checkOutOfContext (event )) return ;
371+ @ Override
372+ public void onEntitySelectInteraction (@ NotNull EntitySelectInteractionEvent event ) {
373+ event .deferReply ().submit ().thenCompose (hook -> {
374+ if (checkOutOfContext (event )) return CompletableFuture .completedFuture (null );
321375 var path = event .getComponentId ().split ("\\ ." );
322376 var locals = descend (path );
323377
324- event .getValues ().stream ().findAny ().ifPresent (mentionable -> locals .getFirst ().setFor (locals .getSecond (), uncheckedCast (mentionable )));
325- save ();
326- resend ();
327- }
378+ return event .getValues ().stream ().findAny ().map (mentionable -> {
379+ locals .getFirst ().setFor (locals .getSecond (), uncheckedCast (mentionable ));
380+ save ();
381+ return updateDisplayValue (event .getMessage (), hook , mentionable .getId ());
382+ }).orElseGet (() -> failedFuture (new IllegalArgumentException ("Invalid values: " + event .getValues ())));
383+ }).exceptionally (Debug .exceptionLogger ("Internal error when handling interaction" ));
384+ }
328385
329- private Pair <DataStructure <?>.Property <?>, Object > descend (String ... path ) {
330- if (path .length == 0 ) throw new IllegalArgumentException ("Empty path" );
331- Wrap <DataStructure <?>.Property <?>> wrap = uncheckedCast (struct .getProperty (path [0 ]));
332- Object holder = config ;
333- if (wrap .test (prop -> !prop .getType ().isStandard ())) holder = wrap .ifPresentMap (prop -> prop .getFrom (config ));
334- for (var i = 1 ; i < path .length ; i ++) {
335- final var fi = i ;
336- final var fh = holder ;
337- wrap = wrap .map (it -> it .getType ().getTargetClass ()).map (DataStructure ::of ).flatMap (struct -> struct .getProperty (path [fi ]));
338- if (wrap .test (prop -> !prop .getType ().isStandard ())) holder = wrap .ifPresentMap (prop -> prop .getFrom (fh ));
339- }
386+ private CompletableFuture <?> updateDisplayValue (Message original , InteractionHook hook , Object value ) {
387+ var raw = original .getContentRaw ();
388+ var start = raw .indexOf ("```" );
389+ return original .editMessage (raw .substring (0 , start ) + "```\n " + value + "\n ```" ).flatMap ($ -> hook .deleteOriginal ()).submit ();
390+ }
391+
392+ private Pair <DataStructure <?>.Property <?>, Object > descend (String ... path ) {
393+ if (path .length == 0 ) throw new IllegalArgumentException ("Empty path" );
394+ Wrap <DataStructure <?>.Property <?>> wrap = uncheckedCast (struct .getProperty (path [0 ]));
395+ Object holder = config ;
396+ if (wrap .test (prop -> !prop .getType ().isStandard ())) holder = wrap .ifPresentMap (prop -> prop .getFrom (config ));
397+ for (var i = 1 ; i < path .length ; i ++) {
398+ final var fi = i ;
340399 final var fh = holder ;
341- return wrap .ifPresentMapOrElseThrow (prop -> new Pair <>(prop , fh ),
342- () -> new NoSuchElementException ("No such property: " + String .join ("." , path )));
400+ wrap = wrap .map (it -> it .getType ().getTargetClass ()).map (DataStructure ::of ).flatMap (struct -> struct .getProperty (path [fi ]));
401+ if (wrap .test (prop -> !prop .getType ().isStandard () && prop .getType ().getTargetClass ().isInstance (fh ))) {
402+ Object o = wrap .ifPresentMap (prop -> prop .getFrom (fh ));
403+ if (o != null ) holder = o ;
404+ }
343405 }
406+ final var fh = holder ;
407+ return wrap .ifPresentMapOrElseThrow (prop -> new Pair <>(prop , fh ), () -> new NoSuchElementException ("No such property: " + String .join ("." , path )));
344408 }
345409
346410 private static EntitySelectMenu .@ NotNull SelectTarget getSelectTarget (DataStructure <?>.Property <?> property ) {
0 commit comments