diff --git a/application/executable/pom.xml b/application/executable/pom.xml index 47a4c31..ab3f453 100644 --- a/application/executable/pom.xml +++ b/application/executable/pom.xml @@ -122,6 +122,12 @@ ${project.version} + + dev.sheldan.oneplus.bot.application.modules + setups + ${project.version} + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/pom.xml b/application/oneplus-bot-modules/pom.xml index ef7afb7..ec2833f 100644 --- a/application/oneplus-bot-modules/pom.xml +++ b/application/oneplus-bot-modules/pom.xml @@ -12,6 +12,7 @@ pom news + setups diff --git a/application/oneplus-bot-modules/setups/pom.xml b/application/oneplus-bot-modules/setups/pom.xml new file mode 100644 index 0000000..f2167f2 --- /dev/null +++ b/application/oneplus-bot-modules/setups/pom.xml @@ -0,0 +1,36 @@ + + + + oneplus-bot-modules + dev.sheldan.oneplus.bot.application.modules + 1.3.11-SNAPSHOT + + 4.0.0 + + setups + + + + + maven-assembly-plugin + + + src/main/assembly/liquibase.xml + + + + + make-assembly + package + + single + + + + + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/assembly/liquibase.xml b/application/oneplus-bot-modules/setups/src/main/assembly/liquibase.xml new file mode 100644 index 0000000..8b4774f --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/assembly/liquibase.xml @@ -0,0 +1,18 @@ + + liquibase + + zip + + false + + + . + ${project.basedir}/src/main/resources/migrations + + **/* + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsBeanConfig.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsBeanConfig.java new file mode 100644 index 0000000..51f3572 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsBeanConfig.java @@ -0,0 +1,15 @@ +package dev.sheldan.oneplus.bot.modules.setups.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Configuration +public class SetupsBeanConfig { + @Bean(value = "setupsDelayedExecutor") + public ScheduledExecutorService getDelayedExecutor() { + return Executors.newSingleThreadScheduledExecutor(); + } +} diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeature.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeature.java new file mode 100644 index 0000000..0ce8eb6 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeature.java @@ -0,0 +1,30 @@ +package dev.sheldan.oneplus.bot.modules.setups.config; + +import dev.sheldan.abstracto.core.config.FeatureConfig; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.PostTargetEnum; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +import static dev.sheldan.oneplus.bot.modules.setups.listener.SetupsListener.SETUPS_UPVOTE_EMOTE_KEY; + +@Component +public class SetupsFeature implements FeatureConfig { + + @Override + public FeatureDefinition getFeature() { + return SetupsFeatureDefinition.SETUPS; + } + + @Override + public List getRequiredPostTargets() { + return Arrays.asList(SetupsPostTarget.SETUPS); + } + + @Override + public List getRequiredEmotes() { + return Arrays.asList(SETUPS_UPVOTE_EMOTE_KEY); + } +} diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeatureDefinition.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeatureDefinition.java new file mode 100644 index 0000000..6f07831 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsFeatureDefinition.java @@ -0,0 +1,15 @@ +package dev.sheldan.oneplus.bot.modules.setups.config; + +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import lombok.Getter; + +@Getter +public enum SetupsFeatureDefinition implements FeatureDefinition { + SETUPS("setups"); + + private String key; + + SetupsFeatureDefinition(String key) { + this.key = key; + } +} \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsPostTarget.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsPostTarget.java new file mode 100644 index 0000000..300dde3 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsPostTarget.java @@ -0,0 +1,15 @@ +package dev.sheldan.oneplus.bot.modules.setups.config; + +import dev.sheldan.abstracto.core.config.PostTargetEnum; +import lombok.Getter; + +@Getter +public enum SetupsPostTarget implements PostTargetEnum { + SETUPS("setups"); + + private String key; + + SetupsPostTarget(String key) { + this.key = key; + } +} diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsProperties.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsProperties.java new file mode 100644 index 0000000..d12bb09 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/config/SetupsProperties.java @@ -0,0 +1,10 @@ +package dev.sheldan.oneplus.bot.modules.setups.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource("classpath:setups.properties") +public class SetupsProperties { + +} diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/listener/SetupsListener.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/listener/SetupsListener.java new file mode 100644 index 0000000..ee94d9f --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/listener/SetupsListener.java @@ -0,0 +1,95 @@ +package dev.sheldan.oneplus.bot.modules.setups.listener; + +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.listener.DefaultListenerResult; +import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener; +import dev.sheldan.abstracto.core.models.database.PostTarget; +import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel; +import dev.sheldan.abstracto.core.service.MessageService; +import dev.sheldan.abstracto.core.service.ReactionService; +import dev.sheldan.abstracto.core.service.management.PostTargetManagement; +import dev.sheldan.oneplus.bot.modules.setups.config.SetupsFeatureDefinition; +import dev.sheldan.oneplus.bot.modules.setups.config.SetupsPostTarget; +import dev.sheldan.oneplus.bot.modules.setups.service.SetupsService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class SetupsListener implements AsyncMessageReceivedListener { + + @Autowired + private PostTargetManagement postTargetManagement; + + @Autowired + private SetupsService setupsService; + + @Qualifier("setupsDelayedExecutor") + @Autowired + private ScheduledExecutorService scheduledExecutorService; + + @Value("${abstracto.setups.deletionDelaySeconds}") + private Long deletionDelay; + + @Autowired + private MessageService messageService; + + @Autowired + private ReactionService reactionService; + + public static final String SETUPS_UPVOTE_EMOTE_KEY = "setupsUpvote"; + + @Override + public DefaultListenerResult execute(MessageReceivedModel model) { + Optional setupsPostTargetOptional = postTargetManagement.getPostTargetOptional(SetupsPostTarget.SETUPS, model.getServerId()); + if(setupsPostTargetOptional.isPresent()) { + PostTarget setupsTarget = setupsPostTargetOptional.get(); + Message originalMessage = model.getMessage(); + if(originalMessage.getChannel().getIdLong() == setupsTarget.getChannelReference().getId()) { + Long serverId = model.getServerId(); + boolean currentlyInvalid = setupsService.currentlyInvalid(originalMessage); + if(currentlyInvalid) { + if(setupsService.mightContainEmbed(originalMessage)) { + log.info("Setup message did not contain embeds not attachments, but a link - waiting for embeds on message {}" + + " in channel {} in guild {} by user {}.", originalMessage.getIdLong(), originalMessage.getChannel().getIdLong(), + originalMessage.getGuild().getIdLong(), originalMessage.getAuthor().getIdLong()); + scheduledExecutorService.schedule(() -> { + messageService.loadMessage(originalMessage).thenAccept(loadedMessage -> { + if(setupsService.currentlyInvalid(loadedMessage)) { + log.info("Message did not contain attachments nor embeds after a delay - deleting setups message {}.", loadedMessage.getIdLong()); + messageService.deleteMessage(loadedMessage); + } else { + log.info("Message contained embeds/attachments after a delay - message was accepted {}.", loadedMessage.getIdLong()); + reactionService.addReactionToMessage(SETUPS_UPVOTE_EMOTE_KEY, serverId, loadedMessage); + } + }); + }, deletionDelay, TimeUnit.SECONDS); + } else { + log.info("Did not find any attachments nor embeds and no link to lead to any embeds - deleting setup message {} in channel {} in server {}" + + "by user {} for setups.", originalMessage.getIdLong(), originalMessage.getChannel().getIdLong(), originalMessage.getGuild().getIdLong(), + originalMessage.getAuthor().getIdLong()); + messageService.deleteMessage(originalMessage); + } + } else { + log.info("Accepting setups message {} in channel {} in guild {} from user {}.", originalMessage.getIdLong(), + originalMessage.getChannel().getIdLong(), originalMessage.getGuild().getIdLong(), originalMessage.getAuthor().getIdLong()); + reactionService.addReactionToMessage(SETUPS_UPVOTE_EMOTE_KEY, serverId, originalMessage); + } + } + } + return DefaultListenerResult.IGNORED; + } + + @Override + public FeatureDefinition getFeature() { + return SetupsFeatureDefinition.SETUPS; + } +} diff --git a/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/service/SetupsService.java b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/service/SetupsService.java new file mode 100644 index 0000000..8a40115 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/java/dev/sheldan/oneplus/bot/modules/setups/service/SetupsService.java @@ -0,0 +1,35 @@ +package dev.sheldan.oneplus.bot.modules.setups.service; + +import net.dv8tion.jda.api.entities.EmbedType; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.stereotype.Component; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Component +public class SetupsService { + + private static final Pattern URL_REGEX = Pattern.compile("((https?|ftp)://|(www|ftp)\\.)?[a-z0-9-]+(\\.[a-z0-9-]+)+([/?].*)?"); + + public boolean mightContainEmbed(Message message) { + Matcher urlMatcher = URL_REGEX.matcher(message.getContentRaw()); + return urlMatcher.find(); + } + + public boolean currentlyValid(Message message) { + return !message.getAttachments().isEmpty() || + ( + !message.getEmbeds().isEmpty() && + message + .getEmbeds() + .stream() + .anyMatch(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)) + ); + } + + public boolean currentlyInvalid(Message message) { + return !currentlyValid(message); + } + +} diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/collection.xml b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/collection.xml new file mode 100644 index 0000000..bc9fce9 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/collection.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/data.xml b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/data.xml new file mode 100644 index 0000000..13f4267 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/data.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/default_emote.xml b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/default_emote.xml new file mode 100644 index 0000000..5b9eec3 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/default_emote.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/feature.xml b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/feature.xml new file mode 100644 index 0000000..a234f83 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/1.3.11-setups/setups-seedData/feature.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/dbchangelog.xsd b/application/oneplus-bot-modules/setups/src/main/resources/migrations/dbchangelog.xsd new file mode 100644 index 0000000..83483a5 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/dbchangelog.xsd @@ -0,0 +1,1386 @@ + + + + + + + + + + + + + + Extension to standard XSD boolean type to allow ${} parameters + + + + + + + + + + + + + + + + Extension to standard XSD integer type to allow ${} parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + onChangeLogPreconditionOnSqlOutput determines what should + happen when evaluating this precondition in updateSQL mode. TEST: Run + precondition, FAIL: Fail precondition, IGNORE: Skip precondition check + [DEFAULT] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Used with valueClobFile to specify file encoding explicitly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true for a cycling sequence, false for a non-cycling sequence. + Default is false. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/migrations/setups-changeLog.xml b/application/oneplus-bot-modules/setups/src/main/resources/migrations/setups-changeLog.xml new file mode 100644 index 0000000..256d4dc --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/migrations/setups-changeLog.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/application/oneplus-bot-modules/setups/src/main/resources/setups.properties b/application/oneplus-bot-modules/setups/src/main/resources/setups.properties new file mode 100644 index 0000000..557fee2 --- /dev/null +++ b/application/oneplus-bot-modules/setups/src/main/resources/setups.properties @@ -0,0 +1,6 @@ +abstracto.postTargets.setups.name=setups + +abstracto.featureFlags.setups.featureName=setups +abstracto.featureFlags.setups.enabled=false + +abstracto.setups.deletionDelaySeconds=3 \ No newline at end of file diff --git a/deployment/image-packaging/pom.xml b/deployment/image-packaging/pom.xml index 215cb16..1907c8e 100644 --- a/deployment/image-packaging/pom.xml +++ b/deployment/image-packaging/pom.xml @@ -287,6 +287,16 @@ news.zip + + dev.sheldan.oneplus.bot.templates.translations + setups-translations + ${project.version} + zip + true + ${file.basedir}/deployment/translation-artifacts/ + setups.zip + + dev.sheldan.abstracto.scheduling @@ -444,6 +454,17 @@ news.zip + + dev.sheldan.oneplus.bot.application.modules + setups + ${project.version} + liquibase + zip + true + ${file.basedir}/deployment/liquibase-artifacts/ + setups.zip + + diff --git a/deployment/image-packaging/src/main/docker/deployment/config/artifact_versions.json b/deployment/image-packaging/src/main/docker/deployment/config/artifact_versions.json index 4b721a9..8b5e5e5 100644 --- a/deployment/image-packaging/src/main/docker/deployment/config/artifact_versions.json +++ b/deployment/image-packaging/src/main/docker/deployment/config/artifact_versions.json @@ -7,7 +7,7 @@ "translation_artifacts": ["utility", "core", "entertainment", "starboard", "link-embed", "webservices", "suggestion", "remind", "logging", "invite-filter", "starboard-custom", - "news"], + "news", "setups"], "liquibase_artifacts": [ { "zip": "scheduling", "file": "scheduling-changeLog.xml" }, { "zip": "core", "file": "core-changeLog.xml" }, @@ -20,6 +20,7 @@ { "zip": "logging", "file": "logging-changeLog.xml"}, { "zip": "suggestion", "file": "suggestion-changeLog.xml"}, { "zip": "invite-filter", "file": "inviteFilter-changeLog.xml"}, + { "zip": "setups", "file": "setups-changeLog.xml"}, { "zip": "starboard-custom", "file": "starboard-custom-changeLog.xml"}, { "zip": "news", "file": "news-changeLog.xml"} ] diff --git a/templates/translations/news-translations/pom.xml b/templates/translations/news-translations/pom.xml index 725cb92..19fabf0 100644 --- a/templates/translations/news-translations/pom.xml +++ b/templates/translations/news-translations/pom.xml @@ -21,7 +21,7 @@ single - starboard-custom-templates-${project.version} + news-templates-${project.version} false src/main/assembly/assembly.xml diff --git a/templates/translations/pom.xml b/templates/translations/pom.xml index fa6ebf5..5a06b36 100644 --- a/templates/translations/pom.xml +++ b/templates/translations/pom.xml @@ -13,5 +13,6 @@ starboard-custom-translations news-translations + setups-translations \ No newline at end of file diff --git a/templates/translations/setups-translations/pom.xml b/templates/translations/setups-translations/pom.xml new file mode 100644 index 0000000..9f46702 --- /dev/null +++ b/templates/translations/setups-translations/pom.xml @@ -0,0 +1,38 @@ + + + + translations + dev.sheldan.oneplus.bot.templates.translations + 1.3.11-SNAPSHOT + + 4.0.0 + + setups-translations + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + setups-translations-${project.version} + false + + src/main/assembly/assembly.xml + + + + + + + + + \ No newline at end of file diff --git a/templates/translations/setups-translations/src/main/assembly/assembly.xml b/templates/translations/setups-translations/src/main/assembly/assembly.xml new file mode 100644 index 0000000..aca1e51 --- /dev/null +++ b/templates/translations/setups-translations/src/main/assembly/assembly.xml @@ -0,0 +1,15 @@ + + zip + false + + zip + + + + . + ${project.basedir}/src/main/resources + + + \ No newline at end of file diff --git a/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setup_posttarget_setups_en_US.ftl b/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setup_posttarget_setups_en_US.ftl new file mode 100644 index 0000000..5494e46 --- /dev/null +++ b/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setup_posttarget_setups_en_US.ftl @@ -0,0 +1 @@ +The channel which should be scanned for setups. Currently: ${currentTarget} \ No newline at end of file diff --git a/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setups_en_US.ftl b/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setups_en_US.ftl new file mode 100644 index 0000000..3c29d26 --- /dev/null +++ b/templates/translations/setups-translations/src/main/resources/en_US/config/feature_setups_en_US.ftl @@ -0,0 +1 @@ +Setups \ No newline at end of file