diff --git a/pom.xml b/pom.xml
index e2b9978..4fc6d83 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
false
- true
+ false
true
@@ -80,12 +80,12 @@
org.slf4j
slf4j-api
- 2.0.1
+ 2.0.3
org.slf4j
slf4j-simple
- 2.0.1
+ 2.0.3
com.sedmelluq
@@ -110,7 +110,12 @@
org.json
json
- 20220320
+ 20220924
+
+
+ com.github.markozajc
+ akiwrapper
+ 1.5.2
org.mariadb.jdbc
diff --git a/src/main/java/net/tylermurphy/ken/command/Responder.java b/src/main/java/net/tylermurphy/ken/command/Responder.java
index 63aaf55..c8d3967 100644
--- a/src/main/java/net/tylermurphy/ken/command/Responder.java
+++ b/src/main/java/net/tylermurphy/ken/command/Responder.java
@@ -95,7 +95,8 @@ public class Responder extends ListenerAdapter {
new UnMute(),
new SetJoining(),
new History(),
- new Sticker()
+ new Sticker(),
+ new Akinator()
};
Arrays.stream(objects).forEach(register::register);
}
@@ -194,13 +195,14 @@ public class Responder extends ListenerAdapter {
try {
event.deferEdit().queue();
Object temp = method.invoke(register.getHandle(method.getDeclaringClass().getName()), parameters);
+ if(temp == null) return;
Response response = (Response) temp;
edit(response, event.getHook());
} catch (Exception e) {
EmbedBuilder builder = Ken.getInstance().getDefaultEmbed()
.setColor(Color.RED)
.setTitle(":x: **Error**")
- .setDescription(e.getCause().getMessage());
+ .setDescription(e.getCause() != null ? e.getCause().getMessage() : "An unknown error has occurred");
event.getHook().editOriginalEmbeds(builder.build()).queue();
event.getHook().editOriginalComponents(new ArrayList<>()).queue();
}
@@ -242,6 +244,10 @@ public class Responder extends ListenerAdapter {
// Invoke Method and Respond to User
try {
Object temp = method.invoke(register.getHandle(method.getDeclaringClass().getName()), parameters);
+ if(temp == null) {
+ event.reply("You dont have permission to use this!").setEphemeral(true).queue();
+ return;
+ }
Response response = (Response) temp;
event.deferReply(response.isError()).queue();
reply(response, event.getHook());
diff --git a/src/main/java/net/tylermurphy/ken/command/Response.java b/src/main/java/net/tylermurphy/ken/command/Response.java
index 763ccae..54148e0 100644
--- a/src/main/java/net/tylermurphy/ken/command/Response.java
+++ b/src/main/java/net/tylermurphy/ken/command/Response.java
@@ -22,6 +22,7 @@ public class Response {
private String fileName;
private boolean hidden = false;
+ private boolean removeComponents = false;
public static Response error(String message){
Response response = new Response();
@@ -94,6 +95,11 @@ public class Response {
return this;
}
+ public Response setRemoveComponents(boolean removeComponents) {
+ this.removeComponents = removeComponents;
+ return this;
+ }
+
public boolean isError(){
return type.equals("error");
}
@@ -110,6 +116,8 @@ public class Response {
public boolean hasSelectMenu() { return menu != null; }
+ public boolean hasNoComponents() { return removeComponents; }
+
public Button[] getButtons() {
Button[] arr = new Button[buttons.size()];
arr = buttons.toArray(arr);
diff --git a/src/main/java/net/tylermurphy/ken/command/game/Akinator.java b/src/main/java/net/tylermurphy/ken/command/game/Akinator.java
new file mode 100644
index 0000000..b86d99b
--- /dev/null
+++ b/src/main/java/net/tylermurphy/ken/command/game/Akinator.java
@@ -0,0 +1,293 @@
+package net.tylermurphy.ken.command.game;
+
+import com.github.markozajc.akiwrapper.Akiwrapper;
+import com.github.markozajc.akiwrapper.AkiwrapperBuilder;
+import com.github.markozajc.akiwrapper.core.entities.Guess;
+import com.github.markozajc.akiwrapper.core.entities.Identifiable;
+import com.github.markozajc.akiwrapper.core.entities.Question;
+import com.github.markozajc.akiwrapper.core.exceptions.ServerNotFoundException;
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
+import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
+import net.tylermurphy.ken.Ken;
+import net.tylermurphy.ken.command.Response;
+import net.tylermurphy.ken.command.annotation.ButtonCallback;
+import net.tylermurphy.ken.command.annotation.Command;
+import net.tylermurphy.ken.util.Checks;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class Akinator {
+
+ protected Map gameMappings = new HashMap<>();
+ protected Map games = new HashMap<>();
+
+ @Command(name="akinator", description="Plays akinator")
+ public Response execute(Member sender, GuildMessageChannel channel) {
+ if(gameMappings.containsKey(sender.getIdLong())) {
+ games.remove(gameMappings.get(sender.getIdLong()));
+ gameMappings.remove(sender.getIdLong());
+ }
+ new Thread(() -> {
+ try {
+ AkinatorGame game = new AkinatorGame(sender, this);
+ gameMappings.put(sender.getIdLong(), game.getGameId());
+ games.put(game.getGameId(), game);
+ Response response = game.respond(null);
+ channel.sendMessageEmbeds(response.getEmbeds()).setActionRow(response.getButtons()).queue();
+ } catch (ServerNotFoundException e) {
+ channel.sendMessage(":x: Failed to connect to Akinator").queue();
+ }
+ }).start();
+ return Response.success(":arrows_clockwise: Connecting to Akinator");
+ }
+
+ @ButtonCallback(name="akinator")
+ public Response onButton(String id, Message message, Member sender) {
+
+ try { Checks.hasPagedEmbed(message); }
+ catch (RuntimeException e) { return Response.error(e.getMessage()); }
+
+ int gameId = Integer.parseInt(message.getEmbeds().get(0).getFooter().getText().substring(9));
+
+ AkinatorGame game = games.get(gameId);
+ if(game == null) {
+ return Response.error("Game has ended");
+ }
+
+ if(game.getUserId() != sender.getIdLong()) {
+ return null;
+ }
+
+ return game.respond(id);
+
+ }
+
+}
+
+enum AkiStatus {
+ QUESTION,
+ GUESS,
+ CONTINUE
+}
+
+class AkinatorGame {
+
+ private static int lastGameId = 0;
+
+ private final Akinator container;
+ private final Akiwrapper wrapper;
+ private final List sequence;
+ private final List guesses;
+
+ private final String displayName;
+ private final long userId;
+
+ private final int gameId;
+
+ private AkiStatus status;
+ private int questionNumber;
+ private int sequenceIndex;
+ private int attemptedGuesses;
+
+
+ public AkinatorGame(Member member, Akinator container) throws ServerNotFoundException {
+ this.container = container;
+ this.wrapper = new AkiwrapperBuilder().build();
+ this.sequence = new LinkedList<>();
+ this.guesses = new LinkedList<>();
+
+ this.displayName = member.getEffectiveName();
+ this.userId = member.getUser().getIdLong();
+
+ this.gameId = lastGameId++;
+
+ this.status = AkiStatus.QUESTION;
+ this.questionNumber = 0;
+ this.sequenceIndex = -1;
+ this.attemptedGuesses = 0;
+ }
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public int getGameId() {
+ return gameId;
+ }
+
+ public Response respond(String label) {
+ if(label == null) return next();
+ if(status == AkiStatus.GUESS) {
+ return respondGuess(label);
+ } else if (status == AkiStatus.QUESTION) {
+ return respondQuestion(label);
+ } else if (status == AkiStatus.CONTINUE) {
+ return respondContinue(label);
+ }
+ throw new RuntimeException("Failed to respond to user input");
+ }
+
+ private Response respondGuess(String label) {
+ switch (label) {
+ case "yes":
+ container.games.remove(gameId);
+ container.gameMappings.remove(userId);
+ return sendLoose();
+ case "no":
+ status = AkiStatus.CONTINUE;
+ return sendContinue();
+ case "back":
+ return back();
+ }
+ throw new RuntimeException("Failed to respond to user input");
+ }
+
+ private Response respondContinue(String label) {
+ switch (label) {
+ case "yes":
+ return next();
+ case "no":
+ container.games.remove(gameId);
+ container.gameMappings.remove(userId);
+ return sendWin();
+ case "back":
+ return back();
+ }
+ throw new RuntimeException("Failed to respond to user input");
+ }
+
+ private Response respondQuestion(String label) {
+ switch (label) {
+ case "yes":
+ wrapper.answer(Akiwrapper.Answer.YES);
+ break;
+ case "no":
+ wrapper.answer(Akiwrapper.Answer.NO);
+ break;
+ case "idk":
+ wrapper.answer(Akiwrapper.Answer.DONT_KNOW);
+ break;
+ case "back":
+ return back();
+ }
+ return next();
+ }
+
+ private Response next() {
+ sequenceIndex++;
+ Optional guess = wrapper.getGuesses().stream().filter(g -> g.getProbability() > .85 && !guesses.contains(g.getId())).reduce((first, second) -> second);
+ if(guess.isPresent()) {
+ status = AkiStatus.GUESS;
+ sequence.add(guess.get());
+ attemptedGuesses++;
+ return sendGuess(guess.get());
+ } else {
+ status = AkiStatus.QUESTION;
+ Question question = wrapper.getQuestion();
+ sequence.add(question);
+ questionNumber++;
+ if(question == null) {
+ // remove game
+ return sendWin();
+ }
+ return sendQuestion(question);
+ }
+ }
+
+ private Response back() {
+ sequenceIndex--;
+ Identifiable current = sequence.get(sequenceIndex+1);
+ if(current instanceof Guess) {
+ attemptedGuesses--;
+ guesses.remove(guesses.size() - 1);
+ sequence.remove(current);
+ } else if(current instanceof Question) {
+ questionNumber--;
+ sequence.remove(current);
+ }
+ Identifiable previous = sequence.get(sequenceIndex);
+ if(previous instanceof Guess guess) {
+ status = AkiStatus.GUESS;
+ return sendGuess(guess);
+ } else if(previous instanceof Question question){
+ status = AkiStatus.QUESTION;
+ return sendQuestion(question);
+ }
+ throw new RuntimeException("Failed to go back in sequence");
+ }
+
+ private Response sendGuess(Guess guess) {
+ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed();
+ builder.setAuthor("Akinator: " + displayName);
+ builder.setTitle("Is this your character?");
+ builder.appendDescription("**" + guess.getName() + "**\n" + guess.getDescription());
+ builder.setImage(guess.getImage().toExternalForm());
+ builder.setFooter("Game id: " + gameId);
+ Response response = Response.success(builder.build())
+ .addPrimaryButton("akinator", "yes", "Yes")
+ .addPrimaryButton("akinator", "no", "No");
+ if(sequenceIndex > 0)
+ response.addSecondaryButton("akinator", "back", "Back");
+ return response;
+ }
+
+ private Response sendQuestion(Question question) {
+ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed();
+ builder.setAuthor("Akinator: " + displayName);
+ builder.setTitle("Question " + questionNumber);
+ builder.appendDescription(question.getQuestion());
+ builder.setFooter("Game id: " + gameId);
+ Response response = Response.success(builder.build())
+ .addPrimaryButton("akinator", "yes", "Yes")
+ .addPrimaryButton("akinator", "no", "No")
+ .addPrimaryButton("akinator", "idk", "IDK");
+ if(sequenceIndex > 0)
+ response.addSecondaryButton("akinator", "back", "Back");
+ return response;
+ }
+
+ private Response sendContinue() {
+ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed();
+ builder.setAuthor("Akinator: " + displayName);
+ builder.setTitle("Continue?");
+ builder.appendDescription("Akinator guessed incorrectly, would you like to continue?");
+ builder.setFooter("Game id: " + gameId);
+ Response response = Response.success(builder.build())
+ .addPrimaryButton("akinator", "yes", "Yes")
+ .addPrimaryButton("akinator", "no", "No");
+ if(sequenceIndex > 0)
+ response.addSecondaryButton("akinator", "back", "Back");
+ return response;
+ }
+
+ private Response sendWin() {
+ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed();
+ builder.setAuthor("Akinator: " + displayName);
+ builder.setTitle("You win!");
+ builder.appendDescription("Total guesses: " + attemptedGuesses + "\n");
+ builder.appendDescription("Total questions: " + questionNumber);
+ builder.setFooter("Game id: " + gameId);
+ builder.setColor(new Color(130,255,130));
+ return Response.success(builder.build()).setRemoveComponents(true);
+ }
+
+ private Response sendLoose() {
+ Guess guess = (Guess) sequence.get(sequenceIndex);
+ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed();
+ builder.setAuthor("Akinator: " + displayName);
+ builder.setTitle("You loose :(");
+ builder.appendDescription("Total guesses: " + attemptedGuesses + "\n");
+ builder.appendDescription("Total questions: " + questionNumber + "\n");
+ builder.appendDescription("Character Guessed: " + guess.getName());
+ builder.setImage(guess.getImage().toExternalForm());
+ builder.setFooter("Game id: " + gameId);
+ builder.setColor(new Color(255,130,130));
+ return Response.success(builder.build()).setRemoveComponents(true);
+ }
+
+}
diff --git a/src/main/java/net/tylermurphy/ken/command/main/Help.java b/src/main/java/net/tylermurphy/ken/command/main/Help.java
index 80daa3c..b881a29 100644
--- a/src/main/java/net/tylermurphy/ken/command/main/Help.java
+++ b/src/main/java/net/tylermurphy/ken/command/main/Help.java
@@ -88,7 +88,8 @@ public class Help {
.appendDescription("**/claim** Claim moneys every 24h\n")
.appendDescription("**/money** Check how much moneys you have\n")
.appendDescription("**/roulette ** Gamble on roulette\n")
- .appendDescription("**/slots ** Gamble on slots\n"),
+ .appendDescription("**/slots ** Gamble on slots\n")
+ .appendDescription("**/akinator** Play akinator\n"),
Ken.getInstance().getDefaultEmbed()
.setAuthor("Command List")
.setTitle(":desktop: **Social Commands**")