Skip to content

Commit

Permalink
Add an add-embed command for tricks
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Oct 28, 2023
1 parent 103d17a commit e2ede24
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.jagrosh.jdautilities.command.SlashCommandEvent;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.EmbedType;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
Expand All @@ -29,12 +30,15 @@
import net.neoforged.camelot.db.schemas.Trick;
import net.neoforged.camelot.db.transactionals.SlashTricksDAO;
import net.neoforged.camelot.db.transactionals.TricksDAO;
import net.neoforged.camelot.listener.ReferencingListener;
import net.neoforged.camelot.module.TricksModule;
import net.neoforged.camelot.script.CannotRetrieveInformationException;
import net.neoforged.camelot.script.ScriptUtils;
import net.neoforged.camelot.script.ScriptWriter;
import net.neoforged.camelot.util.jda.ButtonManager;
import org.jetbrains.annotations.Nullable;

import java.io.StringWriter;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand All @@ -54,6 +58,7 @@ public ManageTrickCommand() {
this.children = new SlashCommand[] {
new Add(),
new AddText(),
new AddEmbed(),
new Delete(),
new Update(),
new Info(),
Expand Down Expand Up @@ -138,6 +143,82 @@ public static void handleModal(ModalInteractionEvent event, String script, List<
}
}

/**
* The command used to add a new trick with a simple embed reply.
* <p>This command takes a message, and uses its first embed as the trick reply.</p>
*/
public static final class AddEmbed extends SlashCommand {

public AddEmbed() {
this.name = "add-embed";
this.help = "Add a new trick with an embed reply";
this.options = List.of(
new OptionData(OptionType.STRING, "names", "The trick names, comma-separated", true),
new OptionData(OptionType.STRING, "message", "A link to the message with the embed", true),
new OptionData(OptionType.STRING, "description", "The description of the trick")
);
}

@Override
protected void execute(SlashCommandEvent event) {
final List<String> names = List.of(event.optString("names").split(" "));
final String description = Optional.ofNullable(event.optString("description"))
.filter(str -> !str.isBlank()).orElse(null);

if (Database.main().withExtension(TricksDAO.class, db -> names.stream()
.anyMatch(it -> db.getTrickByName(it) != null))) {
event.reply("A trick with at least one of the given names exists already!").setEphemeral(true).queue();
return;
}

for (final String name : names) {
if (!isNameValid(name)) {
event.reply(STR."`\{name}` is not a valid trick name!").setEphemeral(true).queue();
return;
}
}

final var msgOptional = ReferencingListener.decodeMessageLink(event.optString("message"))
.flatMap(link -> link.retrieve(event.getJDA()));
if (msgOptional.isEmpty()) {
event.reply("Invalid message link!").setEphemeral(true).queue();
return;
}

event.deferReply().flatMap(_ -> msgOptional.get())
.queue(msg -> {
final var validEmbed = msg.getEmbeds().stream()
.filter(embed -> embed.getType() == EmbedType.RICH)
.findFirst();

if (validEmbed.isEmpty()) {
event.getHook().editOriginal("No embeds found in the message!").queue();
return;
}

final StringBuilder script = new StringBuilder();
if (description != null) {
script.append("const description = ")
.append("'")
.append(description.replace("'", "\\'"))
.append("'")
.append('\n');
}
script.append(new ScriptWriter(new StringWriter())
.writeLine("function execute()")
.startBlock()
.writeLine("replyEmbed(")
.writeEmbed(validEmbed.get())
.writeLine(");")
.endBlock());

final int id = Database.main().withExtension(TricksDAO.class, db -> db.insertTrick(script.toString(), event.getUser().getIdLong()));
Database.main().useExtension(TricksDAO.class, dao -> names.forEach(name -> dao.addAlias(id, name)));
event.getHook().editOriginal("Trick added!").queue();
}, _ -> event.getHook().editOriginal("Unknown message!").queue());
}
}

/**
* The command used to add a new trick with a simple text reply.
* <p>This command prompts a modal asking for the trick names (separated by a space), the trick description and its string reply</p>
Expand Down
156 changes: 156 additions & 0 deletions src/main/java/net/neoforged/camelot/script/ScriptWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package net.neoforged.camelot.script;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;

import java.io.StringWriter;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class ScriptWriter {
private final StringWriter writer;
private int indentAmount;

public ScriptWriter(StringWriter writer) {
this.writer = writer;
}

public ScriptWriter writeLine(String text) {
return writeLineStart().write(text).writeLineEnd();
}

public ScriptWriter writeLineStart() {
writer.write(" ".repeat(indentAmount));
return this;
}

public ScriptWriter writeLineEnd() {
writer.write('\n');
return this;
}

public ScriptWriter startBlock() {
return writeLine("{").increaseIndent(2);
}

public ScriptWriter endBlock() {
return decreaseIndent(2).writeLine("}");
}

public ScriptWriter writeString(String text) {
return write('`').write(text.replace("`", "\\`")
.replace("\\", "\\\\")).write('`');
}

public ScriptWriter write(char character) {
writer.write(character);
return this;
}

public ScriptWriter write(String text) {
writer.write(text);
return this;
}

public ScriptWriter writeInt(int value) {
writer.write(String.valueOf(value));
return this;
}

public ScriptWriter increaseIndent(int amount) {
indentAmount += amount;
return this;
}

public ScriptWriter decreaseIndent(int amount) {
indentAmount -= Math.max(amount, 0);
return this;
}

public <T> ScriptWriter writeKeyValueIfNot(String key, T value, Predicate<T> predicate) {
return writeKeyValueIf(key, value, Predicate.not(predicate));
}

public <T> ScriptWriter writeKeyValueIf(String key, T value, Predicate<T> predicate) {
if (predicate.test(value)) {
writeKeyValue(key, _ -> {
switch (value) {
case Integer number -> writeInt(number);
case Boolean bool -> write(bool ? "true" : "false");
default -> writeString(value.toString());
}
});
}
return this;
}

@CanIgnoreReturnValue
public ScriptWriter writeKeyValue(String key, Consumer<ScriptWriter> value) {
writeLineStart().write("'").write(key).write("'").write(": ");
value.accept(this);
if (writer.getBuffer().charAt(writer.getBuffer().length() - 1) == '\n') {
writer.getBuffer().setCharAt(writer.getBuffer().length() - 1, ',');
} else {
write(',');
}
return writeLineEnd();
}

public <T> ScriptWriter writeObjectList(List<T> values, BiConsumer<T, ScriptWriter> valueWriter) {
write('[').increaseIndent(2).writeLineEnd();
final var itr = values.iterator();
while (itr.hasNext()) {
startBlock();
valueWriter.accept(itr.next(), this);
decreaseIndent(2).writeLineStart().write("}");
if (itr.hasNext()) {
write(", ");
}
writeLineEnd();
}

return writeLineStart().write(']');
}

public ScriptWriter writeEmbed(MessageEmbed embed) {
startBlock();
writeKeyValueIfNot("description", embed.getDescription(), String::isBlank);

if (embed.getTitle() != null || embed.getUrl() != null) {
writeKeyValue("title", _ -> {
write('{').increaseIndent(2).writeLineEnd();
writeKeyValueIf("value", embed.getTitle(), Objects::nonNull);
writeKeyValueIf("url", embed.getUrl(), Objects::nonNull);
endBlock();
});
}

if (!embed.getFields().isEmpty()) {
writeKeyValue("fields", _ -> writeObjectList(embed.getFields(), (field, _) -> {
writeKeyValueIf("name", field.getName(), Objects::nonNull);
writeKeyValueIf("value", field.getValue(), Objects::nonNull);
writeKeyValueIf("inline", field.isInline(), Objects::nonNull);
}));
}

writeKeyValue("color", _ -> writeInt(embed.getColorRaw()));

if (embed.getThumbnail() != null) {
writeKeyValueIf("thumbnail", embed.getThumbnail().getUrl(), Objects::nonNull);
}
if (embed.getImage() != null) {
writeKeyValueIf("image", embed.getImage().getUrl(), Objects::nonNull);
}

return endBlock();
}

@Override
public String toString() {
return writer.toString();
}
}

0 comments on commit e2ede24

Please sign in to comment.