Skip to content

Commit 2f555e6

Browse files
committed
fix jda presentation
1 parent 309be88 commit 2f555e6

File tree

6 files changed

+203
-132
lines changed

6 files changed

+203
-132
lines changed

src/main/java/org/comroid/api/config/Adapt.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
/** indicates that a value should not be displayed in editors */
77
@Retention(RetentionPolicy.RUNTIME)
88
public @interface Adapt {
9-
/** determines a class that should be {@linkplain org.comroid.api.java.JITAssistant#prepare(Class[]) loaded} first in order to initialize it to cache */
9+
/** determines a set of type adapters that are used for the annotated target */
1010
Class<?>[] value() default { };
1111
}

src/main/java/org/comroid/api/config/ConfigurationManager.java

Lines changed: 112 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import lombok.Value;
55
import lombok.experimental.NonFinal;
66
import net.dv8tion.jda.api.entities.IMentionable;
7+
import net.dv8tion.jda.api.entities.ISnowflake;
78
import net.dv8tion.jda.api.entities.Message;
89
import net.dv8tion.jda.api.entities.Role;
910
import net.dv8tion.jda.api.entities.User;
@@ -16,6 +17,7 @@
1617
import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent;
1718
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
1819
import net.dv8tion.jda.api.hooks.ListenerAdapter;
20+
import net.dv8tion.jda.api.interactions.InteractionHook;
1921
import net.dv8tion.jda.api.interactions.components.buttons.Button;
2022
import net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu;
2123
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
@@ -42,17 +44,23 @@
4244
import org.comroid.api.tree.UncheckedCloseable;
4345
import org.jetbrains.annotations.NotNull;
4446

47+
import java.io.BufferedReader;
4548
import java.io.File;
49+
import java.io.FileInputStream;
4650
import java.io.FileOutputStream;
51+
import java.io.InputStreamReader;
4752
import java.lang.reflect.Field;
4853
import java.nio.charset.StandardCharsets;
4954
import java.time.Instant;
5055
import java.util.Arrays;
5156
import java.util.List;
5257
import java.util.NoSuchElementException;
58+
import java.util.Objects;
5359
import java.util.Optional;
60+
import java.util.concurrent.CompletableFuture;
5461
import java.util.stream.Collectors;
5562
import java.util.stream.IntStream;
63+
import java.util.stream.Stream;
5664

5765
import static org.comroid.api.Polyfill.*;
5866
import 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) {

src/main/java/org/comroid/api/config/adapter/TypeAdapter.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.Collections;
1111
import java.util.Map;
12+
import java.util.Optional;
1213
import java.util.concurrent.ConcurrentHashMap;
1314

1415
@Value
@@ -31,5 +32,7 @@ protected TypeAdapter(Class<T> type, StandardValueType<S> serialized, String nam
3132

3233
public abstract @NotNull S toSerializable(Context context, T value);
3334

34-
public abstract @Nullable T deserialize(Context context, S serialized);
35+
public abstract Optional<T> deserialize(Context context, S serialized);
36+
37+
public abstract S parseSerialized(@Nullable String string);
3538
}

src/main/java/org/comroid/api/config/adapter/impl/JdaTypeAdapter.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import org.comroid.api.data.seri.type.StandardValueType;
1010
import org.comroid.api.func.ext.Context;
1111
import org.jetbrains.annotations.NotNull;
12+
import org.jetbrains.annotations.Nullable;
1213

1314
import java.util.Arrays;
15+
import java.util.Optional;
1416
import java.util.stream.Stream;
1517

1618
@Value
@@ -23,8 +25,8 @@ protected Stream<NewsChannel> findAll(JDA jda) {
2325
}
2426

2527
@Override
26-
public NewsChannel deserialize(Context context, @NotNull Long id) {
27-
return context.getFromContext(JDA.class, false).assertion().getNewsChannelById(id);
28+
public Optional<NewsChannel> deserialize(Context context, @NotNull Long id) {
29+
return context.getFromContext(JDA.class, false).wrap().map(jda -> jda.getNewsChannelById(id));
2830
}
2931
};
3032

@@ -37,6 +39,11 @@ public JdaTypeAdapter(Class<T> type) {
3739
return value.getIdLong();
3840
}
3941

42+
@Override
43+
public @NotNull Long parseSerialized(@Nullable String str) {
44+
return str == null ? 0 : Long.parseLong(str);
45+
}
46+
4047
protected abstract Stream<T> findAll(JDA jda);
4148

4249
protected Stream<T> findAllById(JDA jda, long... id) {

0 commit comments

Comments
 (0)