diff --git a/docs/changelog/index.md b/docs/changelog/index.md index fd2ae903..6da58d1a 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -3,6 +3,10 @@ Detailed changelogs can be found on the [Ninbot GitHub Releases page](https://github.com/Nincodedo/Ninbot/releases). This page will highlight more interesting changes between versions. +## [3.19.0](https://github.com/Nincodedo/Ninbot/releases/tag/3.19.0) + +* Added the [Haiku Analyzer](../commands/index.md#haiku-analyzer) message command + ## [3.9.0](https://github.com/Nincodedo/Ninbot/releases/tag/3.9.0) * Added the [Emojitizer](../commands/index.md#emojitizer) message command diff --git a/docs/commands/index.md b/docs/commands/index.md index f8651cd4..451c08f1 100644 --- a/docs/commands/index.md +++ b/docs/commands/index.md @@ -102,3 +102,8 @@ Like the Dab slash command, dabs on that specific message. There is also a Huge Emojitizer lets you enter a word you want to turn into reaction emojis on a message. You can only use each letter once so words with duplicate letters are not allowed. Also, if you use Emojitizer on a message that already has emoji letters on it, those are not available to you. + +### Haiku Analyzer + +The Haiku Analyzer breaks down why a message is or isn't considered a haiku. On first run it will only show the results +to you. You can click the share button on the results to send it as a message to the rest of the channel. diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommand.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommand.java new file mode 100644 index 00000000..b5c679c4 --- /dev/null +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommand.java @@ -0,0 +1,165 @@ +package dev.nincodedo.ninbot.components.haiku; + +import dev.nincodedo.nincord.Emojis; +import dev.nincodedo.nincord.command.message.MessageContextCommand; +import dev.nincodedo.nincord.message.MessageContextInteractionEventMessageExecutor; +import dev.nincodedo.nincord.message.MessageExecutor; +import dev.nincodedo.nincord.message.MessageUtils; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.utils.MarkdownSanitizer; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class HaikuAnalyzeCommand implements MessageContextCommand { + + private HaikuMessageParser haikuMessageParser; + + public HaikuAnalyzeCommand(HaikuMessageParser haikuMessageParser) { + this.haikuMessageParser = haikuMessageParser; + } + + @Override + public MessageExecutor execute(@NotNull MessageContextInteractionEvent event, @NotNull MessageContextInteractionEventMessageExecutor messageExecutor) { + var message = getContentStrippedMessage(event); + var rawMessage = getRawMessage(event); + boolean messageHasCharacters = !message.isEmpty(); + boolean messageOnlyCharacters = haikuMessageParser.isMessageOnlyCharacters(message); + boolean messageIsCorrectSyllables = haikuMessageParser.getSyllableCount(message) == 17; + int calculatedSyllableTotal = haikuMessageParser.getSyllableCount(message); + + boolean correctNumberOfLines = false; + boolean line1SyllablesCorrect = false; + boolean line2SyllablesCorrect = false; + boolean line3SyllablesCorrect = false; + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Message Haikuability Analysis"); + embedBuilder.setUrl(event.getTarget().getJumpUrl()); + embedBuilder.addField("Has text", Emojis.getCheckOrXResponse(messageHasCharacters), true); + if (messageHasCharacters) { + embedBuilder.addField("Only characters", Emojis.getCheckOrXResponse(messageOnlyCharacters), true); + } + if (messageOnlyCharacters) { + embedBuilder.addField("17 syllables", Emojis.getCheckOrXResponse(messageIsCorrectSyllables), true); + } + List lineTotals = new ArrayList<>(); + if (messageOnlyCharacters && messageIsCorrectSyllables) { + var splitMessage = message.split("\\s+"); + StringBuilder lines = new StringBuilder(); + int syllableTotal = 0; + int lineSyllableTotal = 0; + int nextSyllableCount = 5; + for (int i = 0; i < splitMessage.length; i++) { + var word = splitMessage[i]; + var wordSyllable = haikuMessageParser.getSyllableCount(word); + syllableTotal += wordSyllable; + lineSyllableTotal += wordSyllable; + lines.append(word).append(" (").append(wordSyllable).append(") "); + if (lineSyllableTotal >= nextSyllableCount) { + lines.append(String.format(" = %s actual, %s expected", lineSyllableTotal, nextSyllableCount)); + lines.append("\n"); + if (nextSyllableCount == 7) { + nextSyllableCount = 5; + } else { + nextSyllableCount = 7; + } + lineTotals.add(lineSyllableTotal); + lineSyllableTotal = 0; + } + if ((syllableTotal >= 17 || lineTotals.size() == 3 || i == splitMessage.length - 1) && lineSyllableTotal > 0) { + lines.append(String.format(" = %s actual, %s expected", lineSyllableTotal, nextSyllableCount)); + lineTotals.add(lineSyllableTotal); + break; + } + } + correctNumberOfLines = lineTotals.size() == 3; + line1SyllablesCorrect = !lineTotals.isEmpty() && lineTotals.get(0) == 5; + line2SyllablesCorrect = lineTotals.size() >= 2 && lineTotals.get(1) == 7; + line3SyllablesCorrect = lineTotals.size() == 3 && lineTotals.get(2) == 5; + embedBuilder.addField("Line Analysis", MessageUtils.addSpoilerText(lines.toString(), rawMessage), false); + } else if (messageOnlyCharacters) { + var splitMessage = message.split("\\s+"); + StringBuilder messageAnalysis = new StringBuilder(); + for (String word : splitMessage) { + var wordSyllable = haikuMessageParser.getSyllableCount(word); + messageAnalysis.append(word).append(" (").append(wordSyllable).append(") "); + } + embedBuilder.addField("Message Analysis", MessageUtils.addSpoilerText(messageAnalysis.toString(), rawMessage), false); + } + + String overallResponse = overallResponseMessage(messageOnlyCharacters, messageIsCorrectSyllables, correctNumberOfLines, line1SyllablesCorrect, line2SyllablesCorrect, line3SyllablesCorrect, messageHasCharacters, calculatedSyllableTotal, lineTotals); + + embedBuilder.addField("Overall", overallResponse, false); + + MessageCreateBuilder createBuilder = new MessageCreateBuilder(); + createBuilder.addEmbeds(embedBuilder.build()); + createBuilder.addActionRow(Button.primary("haiku-share", "Share to channel")); + + messageExecutor.addEphemeralMessage(createBuilder.build()); + return messageExecutor; + } + + private @NotNull String overallResponseMessage(boolean messageOnlyCharacters, boolean messageIsCorrectSyllables, boolean correctNumberOfLines, boolean line1SyllablesCorrect, boolean line2SyllablesCorrect, boolean line3SyllablesCorrect, boolean messageHasCharacters, int calculatedSyllableTotal, List lineTotals) { + String overallResponse; + if (messageOnlyCharacters && messageIsCorrectSyllables && correctNumberOfLines && line1SyllablesCorrect && line2SyllablesCorrect && line3SyllablesCorrect) { + overallResponse = "Haikuable"; + } else { + overallResponse = "Not Haikuable"; + String additionalReason = ". Too %s %s."; + if (!messageHasCharacters) { + overallResponse += ". Message has no text."; + } else if (!messageOnlyCharacters) { + overallResponse += ". Message has unsyllable characters."; + } else if (calculatedSyllableTotal != 17) { + overallResponse += String.format(additionalReason, getFewOrMany(calculatedSyllableTotal < 17), String.format("syllables: %s", calculatedSyllableTotal)); + } else if (lineTotals.size() != 3) { + overallResponse += String.format(additionalReason, getFewOrMany(lineTotals.size() < 3), "lines"); + } else if (lineTotals.get(0) != 5) { + overallResponse += String.format(additionalReason, getFewOrMany(lineTotals.getFirst() < 5), " syllables in line 1"); + } else if (lineTotals.get(1) != 7) { + overallResponse += String.format(additionalReason, getFewOrMany(lineTotals.get(1) < 7), "syllables in line 2"); + } else if (lineTotals.get(2) != 5) { + overallResponse += String.format(additionalReason, getFewOrMany(lineTotals.get(2) < 5), "syllables in line 3"); + } else { + overallResponse += ". Heck I dunno how you got here."; + } + } + return overallResponse; + } + + private @NotNull String getContentStrippedMessage(@NotNull MessageContextInteractionEvent event) { + var message = event.getTarget(); + if (message.getAuthor().equals(event.getJDA().getSelfUser())) { + var embeds = event.getTarget().getEmbeds(); + return embeds.getFirst().getDescription() == null ? "" : MarkdownSanitizer.sanitize(embeds.getFirst().getDescription()); + } else { + return event.getTarget().getContentStripped(); + } + } + + private @NotNull String getRawMessage(@NotNull MessageContextInteractionEvent event) { + var message = event.getTarget(); + if (message.getAuthor().equals(event.getJDA().getSelfUser())) { + var embeds = event.getTarget().getEmbeds(); + return embeds.getFirst().getDescription() == null ? "" : embeds.getFirst().getDescription(); + } else { + return event.getTarget().getContentRaw(); + } + } + + private @NotNull String getFewOrMany(boolean isLessThan) { + return isLessThan ? "few" : "many"; + } + + @Override + public String getName() { + return HaikuCommandName.HAIKU.get(); + } +} diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeShareButtonInteraction.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeShareButtonInteraction.java new file mode 100644 index 00000000..2188a7cf --- /dev/null +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeShareButtonInteraction.java @@ -0,0 +1,35 @@ +package dev.nincodedo.ninbot.components.haiku; + +import dev.nincodedo.nincord.command.component.ButtonInteraction; +import dev.nincodedo.nincord.command.component.ComponentData; +import dev.nincodedo.nincord.message.ButtonInteractionCommandMessageExecutor; +import dev.nincodedo.nincord.message.MessageExecutor; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class HaikuAnalyzeShareButtonInteraction implements ButtonInteraction { + @Override + public MessageExecutor execute(@NotNull ButtonInteractionEvent event, + @NotNull ButtonInteractionCommandMessageExecutor messageExecutor, @NotNull ComponentData componentData) { + MessageCreateBuilder messageCreateBuilder = new MessageCreateBuilder(); + messageCreateBuilder.setEmbeds(event.getMessage().getEmbeds()); + messageExecutor.addMessageResponse(messageCreateBuilder.build()); + return messageExecutor; + } + + @Override + public Logger log() { + return log; + } + + @Override + public String getName() { + return "haiku"; + } +} diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuCommandName.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuCommandName.java new file mode 100644 index 00000000..40da44f3 --- /dev/null +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuCommandName.java @@ -0,0 +1,7 @@ +package dev.nincodedo.ninbot.components.haiku; + +import dev.nincodedo.nincord.command.CommandNameEnum; + +enum HaikuCommandName implements CommandNameEnum { + HAIKU +} diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuListener.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuListener.java index d4cdd1c1..a574ee45 100644 --- a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuListener.java +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuListener.java @@ -7,7 +7,6 @@ import dev.nincodedo.nincord.config.db.component.ComponentType; import dev.nincodedo.nincord.message.MessageUtils; import dev.nincodedo.nincord.stats.StatManager; -import eu.crydee.syllablecounter.SyllableCounter; import io.micrometer.core.instrument.Metrics; import io.opentelemetry.instrumentation.annotations.WithSpan; import net.dv8tion.jda.api.EmbedBuilder; @@ -17,28 +16,24 @@ import org.springframework.stereotype.Component; import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; import java.util.Random; import java.util.concurrent.ExecutorService; -import java.util.regex.Pattern; @Component public class HaikuListener extends StatAwareListenerAdapter { private ComponentService componentService; private ConfigService configService; - private SyllableCounter syllableCounter; + private HaikuMessageParser haikuMessageParser; private Random random; private String componentName; public HaikuListener(StatManager statManager, @Qualifier("statCounterThreadPool") ExecutorService executorService - , ComponentService componentService, ConfigService configService) { + , ComponentService componentService, ConfigService configService, HaikuMessageParser haikuMessageParser) { super(statManager, executorService); this.componentService = componentService; this.configService = configService; - this.syllableCounter = new SyllableCounter(); + this.haikuMessageParser = haikuMessageParser; this.random = new SecureRandom(); this.componentName = "haiku"; componentService.registerComponent(componentName, ComponentType.ACTION); @@ -53,7 +48,11 @@ public void onMessageReceived(MessageReceivedEvent event) { } var message = event.getMessage().getContentStripped(); var guildId = event.getGuild().getId(); - isHaikuable(message, guildId).ifPresent(haikuLines -> { + Metrics.counter("bot.listener.haiku.checked").increment(); + haikuMessageParser.isHaikuable(message).ifPresent(haikuLines -> { + if (!checkChance(guildId)) { + return; + } EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.appendDescription(MessageUtils.addSpoilerText( "_" + haikuLines + "_", event.getMessage().getContentRaw())); @@ -65,57 +64,6 @@ public void onMessageReceived(MessageReceivedEvent event) { }); } - Optional isHaikuable(String message, String guildId) { - Metrics.counter("bot.listener.haiku.checked").increment(); - if (isMessageOnlyCharacters(message) && getSyllableCount(message) == 17) { - String[] splitMessage = message.split("\\s+"); - List lines = new ArrayList<>(); - var results = checkLine(0, 5, splitMessage); - if (results.counter() == -1) { - return Optional.empty(); - } else { - lines.add(results.line()); - } - results = checkLine(results.counter(), 7, splitMessage); - if (results.counter() == -1) { - return Optional.empty(); - } else { - lines.add(results.line()); - } - results = checkLine(results.counter(), 5, splitMessage); - if (results.counter() == -1) { - return Optional.empty(); - } else { - lines.add(results.line()); - } - if (results.counter() == splitMessage.length && checkChance(guildId)) { - return Optional.of(lines.get(0) + "\n" + lines.get(1) + "\n" + lines.get(2)); - } - } - return Optional.empty(); - } - - private HaikuParsingResults checkLine(int counter, int syllables, String[] splitMessage) { - StringBuilder line = new StringBuilder(); - for (; counter < splitMessage.length; counter++) { - String word = splitMessage[counter]; - line.append(word); - line.append(" "); - int syllableCount = getSyllableCount(line.toString()); - if (syllableCount > syllables) { - return new HaikuParsingResults(-1, line.toString()); - } else if (syllableCount == syllables) { - counter++; - return new HaikuParsingResults(counter, line.toString()); - } - } - return new HaikuParsingResults(-1, line.toString()); - } - - private boolean isMessageOnlyCharacters(String message) { - return Pattern.compile("^[a-zA-Z\\sé.,!?]+$").matcher(message).matches(); - } - boolean checkChance(String guildId) { var optionalConfig = configService.getGlobalConfigByName(ConfigConstants.HAIKU_CHANCE, guildId); int chance = 10; @@ -124,15 +72,4 @@ boolean checkChance(String guildId) { } return random.nextInt(100) < chance; } - - private int getSyllableCount(String message) { - int count = 0; - for (String word : message.split("\\s+")) { - count += syllableCounter.count(word.replaceAll("\\W", "")); - } - return count; - } -} - -record HaikuParsingResults(int counter, String line) { } diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParser.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParser.java new file mode 100644 index 00000000..bb2020c0 --- /dev/null +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParser.java @@ -0,0 +1,77 @@ +package dev.nincodedo.ninbot.components.haiku; + +import eu.crydee.syllablecounter.SyllableCounter; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; + +@Component +public class HaikuMessageParser { + + private SyllableCounter syllableCounter; + + public HaikuMessageParser() { + this.syllableCounter = new SyllableCounter(); + } + + Optional isHaikuable(String message) { + if (isMessageOnlyCharacters(message) && getSyllableCount(message) == 17) { + String[] splitMessage = message.split("\\s+"); + List lines = new ArrayList<>(); + var results = checkLine(0, 5, splitMessage); + if (results.counter() == -1) { + return Optional.empty(); + } else { + lines.add(results.line()); + } + results = checkLine(results.counter(), 7, splitMessage); + if (results.counter() == -1) { + return Optional.empty(); + } else { + lines.add(results.line()); + } + results = checkLine(results.counter(), 5, splitMessage); + if (results.counter() == -1) { + return Optional.empty(); + } else { + lines.add(results.line()); + } + if (results.counter() == splitMessage.length) { + return Optional.of(lines.get(0) + "\n" + lines.get(1) + "\n" + lines.get(2)); + } + } + return Optional.empty(); + } + + HaikuParsingResults checkLine(int counter, int syllables, String[] splitMessage) { + StringBuilder line = new StringBuilder(); + for (; counter < splitMessage.length; counter++) { + String word = splitMessage[counter]; + line.append(word); + line.append(" "); + int syllableCount = getSyllableCount(line.toString()); + if (syllableCount > syllables) { + return new HaikuParsingResults(-1, line.toString()); + } else if (syllableCount == syllables) { + counter++; + return new HaikuParsingResults(counter, line.toString()); + } + } + return new HaikuParsingResults(-1, line.toString()); + } + + boolean isMessageOnlyCharacters(String message) { + return Pattern.compile("^[a-zA-Z\\sé.,!?]+$").matcher(message).matches(); + } + + int getSyllableCount(String message) { + int count = 0; + for (String word : message.split("\\s+")) { + count += syllableCounter.count(word.replaceAll("\\W", "")); + } + return count; + } +} diff --git a/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuParsingResults.java b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuParsingResults.java new file mode 100644 index 00000000..b79d0569 --- /dev/null +++ b/ninbot-app/src/main/java/dev/nincodedo/ninbot/components/haiku/HaikuParsingResults.java @@ -0,0 +1,4 @@ +package dev.nincodedo.ninbot.components.haiku; + +record HaikuParsingResults(int counter, String line) { +} diff --git a/ninbot-app/src/test/java/dev/nincodedo/ninbot/VerifyArchitectureTest.java b/ninbot-app/src/test/java/dev/nincodedo/ninbot/VerifyArchitectureTest.java index b8d156fc..46481e53 100644 --- a/ninbot-app/src/test/java/dev/nincodedo/ninbot/VerifyArchitectureTest.java +++ b/ninbot-app/src/test/java/dev/nincodedo/ninbot/VerifyArchitectureTest.java @@ -8,8 +8,8 @@ import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction; import com.tngtech.archunit.library.GeneralCodingRules; import dev.nincodedo.nincord.BaseListenerAdapter; +import dev.nincodedo.nincord.command.Command; import dev.nincodedo.nincord.command.CommandNameEnum; -import dev.nincodedo.nincord.command.slash.SlashCommand; import dev.nincodedo.nincord.persistence.BaseEntity; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -36,11 +36,11 @@ static List slashCommandRules() { .and() .areNotInterfaces(); return List.of(slashCommandClasses.should() - .implement(SlashCommand.class) - .because("Slash commands need the SlashCommand implementation to be auto registered"), + .implement(Command.class) + .because("Commands need the Command implementation to be auto registered"), slashCommandClasses.should() .dependOnClassesThat(JavaClass.Predicates.simpleNameEndingWith("CommandName")) - .because("Slash commands should use CommandName enums")); + .because("Commands should use CommandName enums")); } @Test diff --git a/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommandTest.java b/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommandTest.java new file mode 100644 index 00000000..510e6411 --- /dev/null +++ b/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuAnalyzeCommandTest.java @@ -0,0 +1,85 @@ +package dev.nincodedo.ninbot.components.haiku; + +import dev.nincodedo.nincord.message.MessageContextInteractionEventMessageExecutor; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HaikuAnalyzeCommandTest { + + HaikuMessageParser haikuMessageParser = new HaikuMessageParser(); + + HaikuAnalyzeCommand haikuAnalyzeCommand = new HaikuAnalyzeCommand(haikuMessageParser); + + static List nonhaikuables() { + return List.of("too short", "the the the the the the the the the the the the the the the the 9", + "Because Amazon continues to be a curse on my work life."); + } + + static List haikuables() { + return List.of("the the the the the the the the the the the the the the the the the", + "Because Amazon continues to be a curse on my whole work life."); + } + + @ParameterizedTest + @MethodSource("nonhaikuables") + void nonHaikuTest(String words) { + var event = Mockito.mock(MessageContextInteractionEvent.class); + var messageExecutor = new MessageContextInteractionEventMessageExecutor(event); + var message = Mockito.mock(Message.class); + var jda = Mockito.mock(JDA.class); + var user = Mockito.mock(User.class); + + when(event.getTarget()).thenReturn(message); + when(message.getAuthor()).thenReturn(user); + when(event.getJDA()).thenReturn(jda); + when(message.getContentStripped()).thenReturn(words); + when(message.getContentRaw()).thenReturn(words); + + var result = (MessageContextInteractionEventMessageExecutor) haikuAnalyzeCommand.execute(event, + messageExecutor); + + assertThat(result.getEphemeralMessageResponses()).isNotEmpty(); + var embeds = result.getEphemeralMessageResponses().getFirst().getEmbeds(); + assertThat(embeds).isNotEmpty(); + assertThat(embeds.getFirst().getFields()).isNotEmpty(); + assertThat(embeds.getFirst().getFields().getLast().getValue()).contains("Not"); + } + + @ParameterizedTest + @MethodSource("haikuables") + void haikuTest(String words) { + var event = Mockito.mock(MessageContextInteractionEvent.class); + var messageExecutor = new MessageContextInteractionEventMessageExecutor(event); + var message = Mockito.mock(Message.class); + var jda = Mockito.mock(JDA.class); + var user = Mockito.mock(User.class); + + when(event.getTarget()).thenReturn(message); + when(message.getAuthor()).thenReturn(user); + when(event.getJDA()).thenReturn(jda); + when(message.getContentStripped()).thenReturn(words); + when(message.getContentRaw()).thenReturn(words); + + var result = (MessageContextInteractionEventMessageExecutor) haikuAnalyzeCommand.execute(event, + messageExecutor); + + assertThat(result.getEphemeralMessageResponses()).isNotEmpty(); + var embeds = result.getEphemeralMessageResponses().getFirst().getEmbeds(); + assertThat(embeds).isNotEmpty(); + assertThat(embeds.getFirst().getFields()).isNotEmpty(); + assertThat(embeds.getFirst().getFields().getLast().getValue()).contains("Haikuable"); + } +} diff --git a/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuListenerTest.java b/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuListenerTest.java deleted file mode 100644 index 41a445fa..00000000 --- a/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuListenerTest.java +++ /dev/null @@ -1,124 +0,0 @@ -package dev.nincodedo.ninbot.components.haiku; - - -import dev.nincodedo.nincord.config.db.ConfigService; -import dev.nincodedo.nincord.config.db.component.ComponentService; -import dev.nincodedo.nincord.stats.StatManager; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.List; -import java.util.concurrent.ExecutorService; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class HaikuListenerTest { - - @Mock - public MessageReceivedEvent messageEvent; - - @Mock - public Message message; - - @Mock - ComponentService componentService; - - @Mock - ExecutorService executorService; - - @Mock - ConfigService configService; - - @Mock - StatManager statManager; - - @InjectMocks - MockHaikuListener haikuListener; - - static List nonhaikuables() { - return List.of("too short", "the the the the the the the the the the the the the the the the 9", - "Because Amazon continues to be a curse on my work life."); - } - - static List haikuables() { - return List.of("the the the the the the the the the the the the the the the the the", - "Because Amazon continues to be a curse on my whole work life."); - } - - @ParameterizedTest - @MethodSource("nonhaikuables") - void messagesUnhaikuable(String unhaikuable) { - Guild guild = Mockito.mock(Guild.class); - User user = Mockito.mock(User.class); - when(messageEvent.getMessage()).thenReturn(message); - when(message.getContentStripped()).thenReturn(unhaikuable); - when(messageEvent.getGuild()).thenReturn(guild); - when(guild.getId()).thenReturn("1"); - when(componentService.isDisabled("haiku", "1")).thenReturn(false); - when(messageEvent.isFromGuild()).thenReturn(true); - when(messageEvent.getAuthor()).thenReturn(user); - when(user.isBot()).thenReturn(false); - - haikuListener.onMessageReceived(messageEvent); - - assertThat(haikuListener.isHaikuable(unhaikuable, "1")).isNotPresent(); - } - - @ParameterizedTest - @MethodSource("haikuables") - void messageHaikuable(String haikuable) { - Guild guild = Mockito.mock(Guild.class); - MessageChannelUnion channel = Mockito.mock(MessageChannelUnion.class); - User user = Mockito.mock(User.class); - Member member = Mockito.mock(Member.class); - MessageCreateAction action = Mockito.mock(MessageCreateAction.class); - when(messageEvent.getMessage()).thenReturn(message); - when(message.getContentStripped()) - .thenReturn(haikuable); - when(message.getContentRaw()).thenReturn(haikuable); - when(messageEvent.getGuild()).thenReturn(guild); - when(guild.getId()).thenReturn("1"); - when(componentService.isDisabled("haiku", "1")).thenReturn(false); - when(messageEvent.isFromGuild()).thenReturn(true); - when(messageEvent.getAuthor()).thenReturn(user); - when(user.isBot()).thenReturn(false); - when(messageEvent.getMember()).thenReturn(member); - when(messageEvent.getChannel()).thenReturn(channel); - when(channel.sendMessage(any(MessageCreateData.class))).thenReturn(action); - - haikuListener.onMessageReceived(messageEvent); - - assertThat(haikuListener.isHaikuable(haikuable, "1")).isPresent(); - } - - public static class MockHaikuListener extends HaikuListener { - - public MockHaikuListener(ComponentService componentService, StatManager statManager, - ExecutorService executorService, - ConfigService configService) { - super(statManager, executorService, componentService, configService); - } - - @Override - boolean checkChance(String guildId) { - return true; - } - } - -} diff --git a/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParserTest.java b/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParserTest.java new file mode 100644 index 00000000..4d8389ac --- /dev/null +++ b/ninbot-app/src/test/java/dev/nincodedo/ninbot/components/haiku/HaikuMessageParserTest.java @@ -0,0 +1,39 @@ +package dev.nincodedo.ninbot.components.haiku; + + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) +class HaikuMessageParserTest { + + HaikuMessageParser haikuMessageParser = new HaikuMessageParser(); + + static List nonhaikuables() { + return List.of("too short", "the the the the the the the the the the the the the the the the 9", + "Because Amazon continues to be a curse on my work life."); + } + + static List haikuables() { + return List.of("the the the the the the the the the the the the the the the the the", + "Because Amazon continues to be a curse on my whole work life."); + } + + @ParameterizedTest + @MethodSource("nonhaikuables") + void messagesUnhaikuable(String unhaikuable) { + assertThat(haikuMessageParser.isHaikuable(unhaikuable)).isNotPresent(); + } + + @ParameterizedTest + @MethodSource("haikuables") + void messageHaikuable(String haikuable) { + assertThat(haikuMessageParser.isHaikuable(haikuable)).isPresent(); + } +} diff --git a/nincord-common/src/main/java/dev/nincodedo/nincord/Emojis.java b/nincord-common/src/main/java/dev/nincodedo/nincord/Emojis.java index 007c9226..7d5d96a4 100644 --- a/nincord-common/src/main/java/dev/nincodedo/nincord/Emojis.java +++ b/nincord-common/src/main/java/dev/nincodedo/nincord/Emojis.java @@ -41,4 +41,14 @@ public class Emojis { public static String getRandomDoctorEmoji() { return DOCTOR_LIST.get(random.nextInt(DOCTOR_LIST.size())); } + + /** + * Returns the appropriate check or x emoji based on the passed in boolean of true or false. + * + * @param decidingBoolean boolean deciding factor + * @return returns check for true and x for false + */ + public static String getCheckOrXResponse(boolean decidingBoolean) { + return decidingBoolean ? CHECK_MARK : CROSS_X; + } } diff --git a/nincord-common/src/main/java/dev/nincodedo/nincord/command/CommandRegistration.java b/nincord-common/src/main/java/dev/nincodedo/nincord/command/CommandRegistration.java index 431d467d..074719db 100644 --- a/nincord-common/src/main/java/dev/nincodedo/nincord/command/CommandRegistration.java +++ b/nincord-common/src/main/java/dev/nincodedo/nincord/command/CommandRegistration.java @@ -26,18 +26,20 @@ public CommandRegistration(List commands) { @Override public void onReady(ReadyEvent readyEvent) { var shardManager = readyEvent.getJDA().getShardManager(); - if (shardManager != null) { - var shards = shardManager.getShards(); - log.info("Registering commands on {} shards(s)", shards.size()); - shards.forEach(this::registerCommands); - shardManager.getGuilds().forEach(guild -> guild.updateCommands().queue()); - log.info("Finished registering commands"); + if (shardManager == null) { + log.trace("Null shard manager"); + return; } + var shards = shardManager.getShards(); + log.info("Registering commands on {} shards(s)", shards.size()); + shards.forEach(this::registerCommands); + shardManager.getGuilds().forEach(guild -> guild.updateCommands().queue()); + log.info("Finished registering commands"); } private void registerCommands(JDA jda) { if (jda == null) { - log.trace("Null shard found?"); + log.trace("Null shard"); return; } try {