[AB-97] adding react command

we are now actively loading messages in case its a parameter, because the provided message is only partially available
This commit is contained in:
Sheldan
2021-04-13 23:47:22 +02:00
parent 23379e4498
commit 537fd85be8
36 changed files with 820 additions and 22 deletions

View File

@@ -57,6 +57,10 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>

View File

@@ -12,7 +12,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -13,7 +13,7 @@ import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -12,7 +12,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -13,7 +13,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.MockResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.MockResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;

View File

@@ -0,0 +1,71 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.service.ReactionService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.exception.ReactTooManyReactionsException;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Component
public class React extends AbstractConditionableCommand {
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private ReactionService reactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Message message = (Message) parameters.get(0);
String text = (String) parameters.get(1);
List<String> reactionChars = entertainmentService.convertTextToEmojis(text);
int existingReactions = message.getReactions().size();
if(reactionChars.size() + existingReactions > Message.MAX_REACTIONS) {
throw new ReactTooManyReactionsException();
}
List<CompletableFuture<Void>> futures = new ArrayList<>();
reactionChars.forEach(s -> futures.add(reactionService.addDefaultReactionToMessageAsync(s, message)));
return FutureUtils.toSingleFutureGeneric(futures)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("message").type(Message.class).templated(true).build());
parameters.add(Parameter.builder().name("text").type(String.class).remainder(true).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("react")
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.causesReaction(true)
.async(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return EntertainmentFeatureDefinition.ENTERTAINMENT;
}
}

View File

@@ -15,7 +15,7 @@ import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.RollResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.RollResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -12,7 +12,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.model.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -1,16 +1,26 @@
package dev.sheldan.abstracto.entertainment.service;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig;
import dev.sheldan.abstracto.entertainment.exception.ReactDuplicateCharacterException;
import dev.sheldan.abstracto.entertainment.model.ReactMapping;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.*;
@Component
@Slf4j
public class EntertainmentServiceBean implements EntertainmentService {
public static final List<String> EIGHT_BALL_ANSWER_KEYS = Arrays.asList(
@@ -20,12 +30,20 @@ public class EntertainmentServiceBean implements EntertainmentService {
"DONT_COUNT", "REPLY_NO", "SOURCES_NO", "OUTLOOK_NOT_GOOD", "DOUBTFUL" // negative
);
private ReactMapping reactMapping;
@Autowired
private SecureRandom secureRandom;
@Autowired
private ConfigService configService;
@Value("classpath:react_mappings.json")
private Resource reactMappingSource;
@Autowired
private Gson gson;
@Override
public String getEightBallValue(String text) {
return EIGHT_BALL_ANSWER_KEYS.get(secureRandom.nextInt(EIGHT_BALL_ANSWER_KEYS.size()));
@@ -57,6 +75,9 @@ public class EntertainmentServiceBean implements EntertainmentService {
@Override
public String createMockText(String text, Member memberExecuting, Member mockedUser) {
if(text == null) {
return "";
}
char[] textChars = text.toLowerCase().toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0, textCharsLength = textChars.length; i < textCharsLength; i++) {
@@ -69,4 +90,101 @@ public class EntertainmentServiceBean implements EntertainmentService {
}
return sb.toString();
}
@Override
public List<String> convertTextToEmojis(String text) {
return convertTextToEmojis(text, false);
}
@Override
public String convertTextToEmojisAString(String text) {
return String.join("", convertTextToEmojis(text));
}
@Override
public List<String> convertTextToEmojis(String text, boolean allowDuplicates) {
if(text == null) {
return new ArrayList<>();
}
text = text.toLowerCase();
// we have to have a separate set to check for combo duplicates, because the checks are different:
// first check is if we already used it as an replacement
// the second check below is whether or not we used it as a replacement, that way we allow
// unicode cars from users as well, this leads to things like sos[sos] not being allowed, because the
// unicode chars get removed, and the first sos gets replaced with the unicode
Set<String> replacedCombos = new HashSet<>();
List<String> result = new ArrayList<>();
// this is used to replace the replacements for more than one character
for (String s : this.reactMapping.getCombinationKeys()) {
if (text.contains(s)) {
String replacement = this.reactMapping.getCombination().get(s);
if(!replacedCombos.contains(replacement) || allowDuplicates) {
if(allowDuplicates) {
text = text.replaceAll(s, replacement);
} else {
text = text.replaceFirst(s, replacement);
}
replacedCombos.add(replacement);
}
}
}
Set<String> usedReplacements = new HashSet<>();
char[] split = text.toCharArray();
for (int i = 0, splitLength = split.length; i < splitLength; i++) {
char normalCharacter = split[i];
String charAsString = Character.toString(normalCharacter);
// the split, also splits surrogate chars (naturally), therefore we need this additional checks
// to ignore the first part, and connect the chars again in order to check them
if(Character.isHighSurrogate(normalCharacter)) {
continue;
}
// in this case we already have unicode, this can either come from the multiple char replacement
// or because we already got unicode to begin with (multi char only), in that case, we also do a duplicate check
// and add it directly
if(Character.isLowSurrogate(normalCharacter)) {
String usedUnicode = split[i - 1] + charAsString;
if(!usedReplacements.contains(usedUnicode) || allowDuplicates) {
usedReplacements.add(usedUnicode);
result.add(usedUnicode);
}
continue;
}
// reject any other character, as the ones we can deal with
if (!this.reactMapping.getSingle().containsKey(charAsString)) {
continue;
}
List<String> listToUse = this.reactMapping.getSingle().get(charAsString);
boolean foundReplacement = false;
for (String replacementChar : listToUse) {
if (!usedReplacements.contains(replacementChar) || allowDuplicates) {
result.add(replacementChar);
usedReplacements.add(replacementChar);
foundReplacement = true;
break;
}
}
if (!foundReplacement) {
throw new ReactDuplicateCharacterException();
}
}
return result;
}
@Override
public String convertTextToEmojisAsString(String text, boolean allowDuplicates) {
return String.join("", convertTextToEmojis(text, allowDuplicates));
}
@PostConstruct
public void postConstruct() {
try {
JsonReader reader = new JsonReader(new InputStreamReader(reactMappingSource.getInputStream()));
this.reactMapping = gson.fromJson(reader, ReactMapping.class);
this.reactMapping.populateKeys();
} catch (IOException e) {
log.error("Failed to load react bindings.", e);
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="entertainment-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,20 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="entertainmentModule" value="(SELECT id FROM module WHERE name = 'entertainment')"/>
<property name="entertainmentFeature" value="(SELECT id FROM feature WHERE key = 'entertainment')"/>
<changeSet author="Sheldan" id="react-command">
<insert tableName="command">
<column name="name" value="react"/>
<column name="module_id" valueComputed="${entertainmentModule}"/>
<column name="feature_id" valueComputed="${entertainmentFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog-3.8.xsd" >
<include file="1.0-entertainment/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.8-entertainment/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.9-entertainment/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,280 @@
{
"source": "https://github.com/TrustyJAID/Trusty-cogs/blob/master/fun/constants.py with additions",
"single": {
"a": [
"🇦",
"🅰",
"🍙",
"🔼",
"4⃣",
"🇱🇨",
"🎄",
"🌲",
"🖇️",
"🔼"
],
"b": [
"🇧",
"🅱",
"8⃣"
],
"c": [
"🇨",
"🗜"
],
"d": [
"🇩",
"↩"
],
"e": [
"🇪",
"3⃣",
"📧",
"💶"
],
"f": [
"🇫",
"🎏",
"🚩",
"🏳️️",
"🏴",
"🏁"
],
"g": [
"🇬",
"🗜",
"6⃣",
"9⃣",
"⛽"
],
"h": [
"🇭",
"♓",
"🏩",
"⛩️",
"🪜"
],
"i": [
"🇮",
"",
"🚹",
"1⃣",
"📍"
],
"j": [
"🇯",
"🗾"
],
"k": [
"🇰",
"🎋"
],
"l": [
"🇱",
"1⃣",
"🇮",
"👢",
"💷"
],
"m": [
"🇲",
"Ⓜ",
"📉"
],
"n": [
"🇳",
"♑",
"🎵"
],
"o": [
"🇴",
"🅾",
"0⃣",
"⭕",
"🔘",
"⏺",
"⚪",
"⚫",
"🔵",
"🔴",
"💫",
"⚽",
"🏀",
"⚾",
"🥎",
"🎾",
"🏐",
"💿",
"📀",
"🧭",
"☯️"
],
"p": [
"🇵",
"🅿",
"🚏"
],
"q": [
"🇶",
"♌"
],
"r": [
"🇷"
],
"s": [
"🇸",
"💲",
"5⃣",
"⚡",
"💰",
"💵"
],
"t": [
"🇹",
"✝",
"",
"🎚",
"🌴",
"7⃣"
],
"u": [
"🇺",
"⛎",
"🐉"
],
"v": [
"🇻",
"♈",
"☑"
],
"w": [
"🇼",
"〰",
"📈"
],
"x": [
"🇽",
"❎",
"✖",
"❌",
"⚒",
"🏴‍☠",
"✂️️"
],
"y": [
"🇾",
"✌",
"💴"
],
"z": [
"🇿",
"2⃣"
],
"0": [
"0⃣",
"🅾",
"0⃣",
"⭕",
"🔘",
"⏺",
"⚪",
"⚫",
"🔵",
"🔴",
"💫",
"⚽",
"🏀",
"⚾",
"🥎",
"🎾",
"🏐",
"💿",
"📀",
"🧭",
"☯️"
],
"1": [
"1⃣",
"🇮"
],
"2": [
"2⃣",
"🇿"
],
"3": [
"3⃣"
],
"4": [
"4⃣"
],
"5": [
"5⃣",
"🇸",
"💲",
"⚡"
],
"6": [
"6⃣"
],
"7": [
"7⃣"
],
"8": [
"8⃣",
"🎱",
"🇧",
"🅱"
],
"9": [
"9⃣"
],
"?": [
"❓"
],
"!": [
"❗",
"❕",
"⚠",
"❣"
]
},
"combination": {
"abcd": "🔠",
"1234": "🔢",
"cool": "🆒",
"back": "🔙",
"777": "🎰",
"soon": "🔜",
"free": "🆓",
"end": "🔚",
"top": "🔝",
"abc": "🔤",
"atm": "🏧",
"new": "🆕",
"sos": "🆘",
"100": "💯",
"loo": "💯",
"zzz": "💤",
"...": "💬",
"ng": "🆖",
"id": "🆔",
"vs": "🆚",
"wc": "🚾",
"ab": "🆎",
"cl": "🆑",
"ok": "🆗",
"up": "🆙",
"10": "🔟",
"24": "🏪",
"11": "⏸",
"ll": "⏸",
"ii": "⏸",
"18": "🏪",
"tm": "™",
"on": "🔛",
"oo": "🈁",
"!?": "⁉",
"!!": "‼",
"17": "📅"
}
}

View File

@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.RollResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.RollResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.model.command.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;

View File

@@ -1,21 +1,34 @@
package dev.sheldan.abstracto.entertainment.service;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig;
import dev.sheldan.abstracto.entertainment.exception.ReactDuplicateCharacterException;
import dev.sheldan.abstracto.entertainment.model.ReactMapping;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.io.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Reader;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -29,6 +42,19 @@ public class EntertainmentServiceBeanTest {
@Mock
private ConfigService configService;
// requires the org.mockito.plugins.MockMaker file
@Mock
private Gson gson;
@Mock
private Resource resource;
@Mock
private Member member;
@Mock
private Member secondMember;
private static final String INPUT_TEXT = "input";
private static final int RANDOM_VALUE = 0;
@@ -94,4 +120,99 @@ public class EntertainmentServiceBeanTest {
Assert.assertEquals(choices.get(0), choiceTaken);
}
@Test
public void testMocking() {
Assert.assertEquals("AsDf", testUnit.createMockText("asdf", member, secondMember));
}
@Test
public void testMockingUpperCase() {
Assert.assertEquals("AsDf", testUnit.createMockText("ASDF", member, secondMember));
}
@Test
public void testMockingNull() {
Assert.assertEquals("", testUnit.createMockText(null, member, secondMember));
}
@Test
public void testConvertTextToEmojis() throws IOException {
setupMappings();
Assert.assertEquals("bceg", testUnit.convertTextToEmojisAsString("asdf", false));
}
@Test(expected = ReactDuplicateCharacterException.class)
public void testConvertTextToEmojisDuplicateReplacement() throws IOException {
setupMappings();
testUnit.convertTextToEmojisAsString("aa", false);
}
@Test
public void testConvertTextToEmojisNoReplacementFound() throws IOException {
setupMappings();
Assert.assertEquals("", testUnit.convertTextToEmojisAsString("e", false));
}
@Test
public void testConvertTextToEmojisNullInput() throws IOException {
setupMappings();
Assert.assertEquals("", testUnit.convertTextToEmojisAsString(null, false));
}
@Test
public void testConvertTextToEmojisDoubleUnicodePassThrough() throws IOException {
setupMappings();
Assert.assertEquals("\uD83C\uDD98", testUnit.convertTextToEmojisAsString("\uD83C\uDD98", false));
}
@Test
public void testConvertTextToEmojisDuplicate() throws IOException {
setupMappings();
Assert.assertEquals("bb", testUnit.convertTextToEmojisAsString("aa", true));
}
@Test
public void testConvertTextToEmojisCombinations() throws IOException {
setupMappings();
Assert.assertEquals("\uD83C\uDD98", testUnit.convertTextToEmojisAsString("kk", true));
}
@Test
public void testConvertTextToEmojisCombinationWithNormalText() throws IOException {
setupMappings();
Assert.assertEquals("\uD83C\uDD98l", testUnit.convertTextToEmojisAsString("kkk", false));
}
@Test
public void testConvertTextToEmojisCombinationWithNormalTextMixed() throws IOException {
setupMappings();
Assert.assertEquals("\uD83C\uDD98lm", testUnit.convertTextToEmojisAsString("kkkk", false));
}
@Test
public void testConvertTextToEmojisCombinationDuplicates() throws IOException {
setupMappings();
Assert.assertEquals("\uD83C\uDD98\uD83C\uDD98", testUnit.convertTextToEmojisAsString("kkkk", true));
}
private void setupMappings() throws IOException {
ReactMapping mapping = Mockito.mock(ReactMapping.class);
HashMap<String, List<String>> singleMappings = new HashMap<>();
singleMappings.put("a", Arrays.asList("b"));
singleMappings.put("s", Arrays.asList("c"));
singleMappings.put("d", Arrays.asList("e"));
singleMappings.put("f", Arrays.asList("g"));
singleMappings.put("k", Arrays.asList("l", "m"));
when(resource.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[]{1}));
when(mapping.getSingle()).thenReturn(singleMappings);
HashMap<String, String> combinations = new HashMap<>();
combinations.put("kk", "\uD83C\uDD98");
when(mapping.getCombination()).thenReturn(combinations);
TreeSet<String> combinationKeys = new TreeSet<>();
combinationKeys.add("kk");
when(mapping.getCombinationKeys()).thenReturn(combinationKeys);
when(gson.fromJson(any(JsonReader.class), eq(ReactMapping.class))).thenReturn(mapping);
testUnit.postConstruct();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.entertainment.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
public class ReactDuplicateCharacterException extends AbstractoRunTimeException implements Templatable {
public ReactDuplicateCharacterException() {
super("Could not replace all characters to be duplicate free.");
}
@Override
public String getTemplateName() {
return "react_duplicate_character_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.entertainment.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
public class ReactTooManyReactionsException extends AbstractoRunTimeException implements Templatable {
public ReactTooManyReactionsException() {
super("Adding reactions would lead to too many reactions.");
}
@Override
public String getTemplateName() {
return "react_too_many_reactions_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.entertainment.model;
import lombok.*;
import java.util.*;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReactMapping {
@Builder.Default
private HashMap<String, List<String>> single = new HashMap<>();
@Builder.Default
private HashMap<String, String> combination = new HashMap<>();
@Builder.Default
private SortedSet<String> combinationKeys = new TreeSet<>((o1, o2) -> {
if(o2.length() == o1.length()) {
return o2.compareTo(o1);
} else {
return Integer.compare(o2.length(), o1.length());
}
});
@Builder.Default
private Set<String> combinationReplacements = new HashSet<>();
public void populateKeys() {
combinationKeys.addAll(combination.keySet());
combinationKeys.forEach(s -> combinationReplacements.add(this.combination.get(s)));
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.entertainment.model;
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.entertainment.service;
import dev.sheldan.abstracto.entertainment.exception.ReactDuplicateCharacterException;
import net.dv8tion.jda.api.entities.Member;
import java.util.List;
@@ -11,4 +12,64 @@ public interface EntertainmentService {
boolean executeRoulette(Member memberExecuting);
String takeChoice(List<String> choices, Member memberExecuting);
String createMockText(String text, Member memberExecuting, Member mockedUser);
/**
* Converts the given text to unicode characters (with predefined values from a manual mapping) and returns the matched
* characters as a list. If the given text is null, an empty list will be returned. This method will actively try
* to avoid duplicates, and try to use alternatives, and throw an exception in case it was not possible to return unique values.
* The size of the list might not be equal to the length of the provided string, because sometimes multiple
* characters are combined into one unicode char.
* @throws ReactDuplicateCharacterException In case it was not possible to replace all text with appropriate unicode
* in case there too many duplicated characters
* @param text The text to convert
* @return A {@link List} of unicode characters, represented as strings, which look similar to the individual characters
* from the text
*/
List<String> convertTextToEmojis(String text);
/**
* Converts the given text to unicode characters (with predefined values from a manual mapping) and returns the matched
* characters as a string. If the given text is null, an empty string will be returned. This method will actively try
* to avoid duplicates, and try to use alternatives, and throw an exception in case it was not possible to return unique values.
* The length of the string might not be equal to the length of the provided string, because sometimes multiple
* characters are combined into one unicode char.
* @throws ReactDuplicateCharacterException In case it was not possible to replace all text with appropriate unicode
* in case there too many duplicated characters
* @param text The text to convert
* @return A string of unicode characters which look similar to the individual characters from the text
*/
String convertTextToEmojisAString(String text);
/**
* Converts the given text to unicode characters (with predefined values from a manual mapping) and returns the matched
* characters as a list. If the given text is null, an empty list will be returned. This method will actively try
* to avoid duplicates (if requested), and try to use alternatives, and throw an exception in case it was not possible
* to return unique values. In case duplicates are allowed, the first possible replacement value will be used,
* leading to all 1:1 replacements being of the same character.
* The size of the list might not be equal to the length of the provided string, because sometimes multiple
* characters are combined into one unicode char.
* @throws ReactDuplicateCharacterException In case it was not possible to replace all text with appropriate unicode
* in case there too many duplicated characters
* @param text The text to convert
* @param allowDuplicates Whether or not to allow duplicates
* @return A list of characters, represented as strings, which look similar to the individual characters
* from the text, possible with duplicates, if requested
*/
List<String> convertTextToEmojis(String text, boolean allowDuplicates);
/**
* Converts the given text to unicode characters (with predefined values from a manual mapping) and returns the matched
* characters as a string. If the given text is null, an empty string will be returned. This method will actively try
* to avoid duplicates (if requested), and try to use alternatives, and throw an exception in case it was not possible
* to return unique values. In case duplicates are allowed, the first possible replacement value will be used,
* leading to all 1:1 replacements being of the same character.
* The length of the string might not be equal to the length of the provided string, because sometimes multiple
* characters are combined into one unicode char.
* @throws ReactDuplicateCharacterException In case it was not possible to replace all text with appropriate unicode
* in case there too many duplicated characters
* @param text The text to convert
* @param allowDuplicates Whether or not to allow duplicates
* @return A string of unicode characters which look similar to the individual characters from the text
*/
String convertTextToEmojisAsString(String text, boolean allowDuplicates);
}