From d4f07bd7197c5e3f5b817c171a57b9d70128375d Mon Sep 17 00:00:00 2001
From: Sheldan <5037282+Sheldan@users.noreply.github.com>
Date: Thu, 28 Dec 2023 22:27:33 +0100
Subject: [PATCH] [AB-xxx] adding wikipedia and dictionary features
---
.../webservices/webservices-impl/pom.xml | 5 -
.../DictionaryApiDefinitionCommand.java | 137 +++++++++++++++
.../service/DictionaryApiServiceBean.java | 73 ++++++++
.../command/OpenWeatherMap.java | 1 -
.../WikipediaArticleSummaryCommand.java | 158 ++++++++++++++++++
.../service/WikipediaServiceBean.java | 64 +++++++
.../migrations/1.5.20/collection.xml | 10 ++
.../migrations/1.5.20/seedData/command.xml | 26 +++
.../migrations/1.5.20/seedData/data.xml | 11 ++
.../migrations/1.5.20/seedData/feature.xml | 17 ++
.../migrations/webservices-changeLog.xml | 1 +
.../resources/webservices-config.properties | 15 +-
.../config/WebServicesSlashCommandNames.java | 2 +
.../config/WebserviceFeatureDefinition.java | 4 +-
.../config/DictionaryApiFeatureConfig.java | 16 ++
.../DictionaryApiRequestException.java | 21 +++
...DictionaryApiDefinitionFoundException.java | 20 +++
.../dictionaryapi/model/WordDefinition.java | 22 +++
.../dictionaryapi/model/WordMeaning.java | 15 ++
.../model/api/DictionaryApiResponseItem.java | 16 ++
.../api/DictionaryApiWordDefinition.java | 13 ++
.../model/api/DictionaryApiWordMeaning.java | 15 ++
.../model/template/DictionaryDefinition.java | 20 +++
.../model/template/DictionaryMeaning.java | 13 ++
.../service/DictionaryApiService.java | 9 +
.../config/WikipediaFeatureConfig.java | 24 +++
.../NoWikipediaArticleFoundException.java | 20 +++
.../exception/WikipediaRequestException.java | 21 +++
.../model/WikipediaArticleSummary.java | 14 ++
.../model/api/WikipediaResponse.java | 13 ++
.../model/api/WikipediaResponsePage.java | 25 +++
.../model/api/WikipediaResponseQuery.java | 14 ++
.../WikipediaArticleSummaryModel.java | 14 ++
.../wikipedia/service/WikipediaService.java | 9 +
34 files changed, 850 insertions(+), 8 deletions(-)
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/command/DictionaryApiDefinitionCommand.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiServiceBean.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/command/WikipediaArticleSummaryCommand.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaServiceBean.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/collection.xml
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/command.xml
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/data.xml
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/feature.xml
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/config/DictionaryApiFeatureConfig.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/DictionaryApiRequestException.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/NoDictionaryApiDefinitionFoundException.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordDefinition.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordMeaning.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiResponseItem.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordDefinition.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordMeaning.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryDefinition.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryMeaning.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiService.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/config/WikipediaFeatureConfig.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/NoWikipediaArticleFoundException.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/WikipediaRequestException.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/WikipediaArticleSummary.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponse.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponsePage.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponseQuery.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/template/WikipediaArticleSummaryModel.java
create mode 100644 abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaService.java
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/pom.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/pom.xml
index 94ec58895..ab2d34d11 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-impl/pom.xml
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/pom.xml
@@ -9,11 +9,6 @@
webservices-impl
-
- 8
- 8
-
-
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/command/DictionaryApiDefinitionCommand.java b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/command/DictionaryApiDefinitionCommand.java
new file mode 100644
index 000000000..c83468cea
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/command/DictionaryApiDefinitionCommand.java
@@ -0,0 +1,137 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+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.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
+import dev.sheldan.abstracto.core.service.ChannelService;
+import dev.sheldan.abstracto.core.templating.model.MessageToSend;
+import dev.sheldan.abstracto.core.templating.service.TemplateService;
+import dev.sheldan.abstracto.core.utils.FutureUtils;
+import dev.sheldan.abstracto.webservices.config.WebServicesSlashCommandNames;
+import dev.sheldan.abstracto.webservices.config.WebserviceFeatureDefinition;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.template.DictionaryMeaning;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.template.DictionaryDefinition;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.WordMeaning;
+import dev.sheldan.abstracto.webservices.dictionaryapi.service.DictionaryApiService;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+@Component
+public class DictionaryApiDefinitionCommand extends AbstractConditionableCommand {
+
+ private static final String DICTIONARY_DEFINITION_RESPONSE_MODEL_TEMPLATE_KEY = "dictionaryDefinition_response";
+ private static final String DICTIONARY_DEFINITION_COMMAND = "dictionaryDefinition";
+ private static final String SEARCH_QUERY_PARAMETER = "searchQuery";
+
+ @Autowired
+ private TemplateService templateService;
+
+ @Autowired
+ private ChannelService channelService;
+
+ @Autowired
+ private SlashCommandParameterService slashCommandParameterService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Autowired
+ private DictionaryApiService dictionaryApiService;
+
+ @Override
+ public CompletableFuture executeAsync(CommandContext commandContext) {
+ String parameter = (String) commandContext.getParameters().getParameters().get(0);
+ try {
+ MessageToSend message = getMessageToSend(commandContext.getGuild().getIdLong(), parameter);
+ return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()))
+ .thenApply(unused -> CommandResult.fromSuccess());
+ } catch (IOException e) {
+ throw new AbstractoRunTimeException(e);
+ }
+ }
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ String query = slashCommandParameterService.getCommandOption(SEARCH_QUERY_PARAMETER, event, String.class);
+ try {
+ MessageToSend messageToSend = getMessageToSend(event.getGuild().getIdLong(), query);
+ return interactionService.replyMessageToSend(messageToSend, event)
+ .thenApply(interactionHook -> CommandResult.fromSuccess());
+ } catch (IOException e) {
+ throw new AbstractoRunTimeException(e);
+ }
+ }
+
+ private MessageToSend getMessageToSend(Long serverId, String searchInput) throws IOException {
+ WordMeaning meaning = dictionaryApiService.getDefinitions(searchInput);
+ List definitions = meaning
+ .getDefinitions()
+ .stream().map(DictionaryDefinition::fromWordDefinition).toList();
+ DictionaryMeaning model = DictionaryMeaning
+ .builder()
+ .definitions(definitions)
+ .word(meaning.getWord())
+ .build();
+ return templateService.renderEmbedTemplate(DICTIONARY_DEFINITION_RESPONSE_MODEL_TEMPLATE_KEY, model, serverId);
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ List parameters = new ArrayList<>();
+ Parameter searchQueryParameter = Parameter
+ .builder()
+ .name(SEARCH_QUERY_PARAMETER)
+ .type(String.class)
+ .templated(true)
+ .remainder(true)
+ .build();
+ parameters.add(searchQueryParameter);
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ List aliases = Arrays.asList("dict");
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(WebServicesSlashCommandNames.DICTIONARY)
+ .commandName("definition")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(DICTIONARY_DEFINITION_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .slashCommandConfig(slashCommandConfig)
+ .async(true)
+ .aliases(aliases)
+ .supportsEmbedException(true)
+ .causesReaction(false)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return WebserviceFeatureDefinition.DICTIONARY;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiServiceBean.java b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiServiceBean.java
new file mode 100644
index 000000000..44b8e2f5f
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiServiceBean.java
@@ -0,0 +1,73 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.service;
+
+import com.google.api.client.http.HttpStatusCodes;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import dev.sheldan.abstracto.webservices.dictionaryapi.exception.DictionaryApiRequestException;
+import dev.sheldan.abstracto.webservices.dictionaryapi.exception.NoDictionaryApiDefinitionFoundException;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.WordDefinition;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.WordMeaning;
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.api.DictionaryApiResponseItem;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.List;
+
+@Component
+@Slf4j
+public class DictionaryApiServiceBean implements DictionaryApiService {
+
+ @Autowired
+ private OkHttpClient okHttpClient;
+
+ @Value("${abstracto.feature.webservices.dictionaryapi.definitionURL}")
+ private String dictionaryDefinitionURL;
+
+ @Autowired
+ private Gson gson;
+
+ @Override
+ public WordMeaning getDefinitions(String query) throws IOException {
+ String formattedUrl = dictionaryDefinitionURL.replace("{1}", query);
+ Request request = new Request.Builder()
+ .url(formattedUrl)
+ .get()
+ .build();
+ Response response = okHttpClient.newCall(request).execute();
+ if(response.code() == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
+ throw new NoDictionaryApiDefinitionFoundException();
+ }
+ if(!response.isSuccessful()) {
+ if(log.isDebugEnabled()) {
+ log.error("Failed to retrieve dictionary api definition. Response had code {} with body {}.",
+ response.code(), response.body());
+ }
+ throw new DictionaryApiRequestException(response.code());
+ }
+ List dictionaryapiResponse = gson.fromJson(response.body().string(), new TypeToken>(){}.getType());
+ DictionaryApiResponseItem selectedWord = dictionaryapiResponse.get(0);
+ if(dictionaryapiResponse.size() > 1) {
+ log.warn("Dictionary had multiple words. One example {}.", selectedWord.getWord());
+ }
+ List wordDefinitions = selectedWord
+ .getMeanings()
+ .stream()
+ .flatMap(dictionaryApiWordMeaning ->
+ dictionaryApiWordMeaning
+ .getDefinitions()
+ .stream()
+ .map(WordDefinition::fromResponseDefinition))
+ .toList();
+ return WordMeaning
+ .builder()
+ .word(selectedWord.getWord())
+ .definitions(wordDefinitions)
+ .build();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/openeweathermap/command/OpenWeatherMap.java b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/openeweathermap/command/OpenWeatherMap.java
index e23103369..c04875b77 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/openeweathermap/command/OpenWeatherMap.java
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/openeweathermap/command/OpenWeatherMap.java
@@ -26,7 +26,6 @@ import dev.sheldan.abstracto.webservices.openweathermap.service.OpenWeatherMapSe
import dev.sheldan.abstracto.webservices.openweathermap.service.WeatherService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
-import org.checkerframework.checker.index.qual.SameLen;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/command/WikipediaArticleSummaryCommand.java b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/command/WikipediaArticleSummaryCommand.java
new file mode 100644
index 000000000..b40fed25a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/command/WikipediaArticleSummaryCommand.java
@@ -0,0 +1,158 @@
+package dev.sheldan.abstracto.webservices.wikipedia.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+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.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
+import dev.sheldan.abstracto.core.service.ChannelService;
+import dev.sheldan.abstracto.core.service.ConfigService;
+import dev.sheldan.abstracto.core.templating.model.MessageToSend;
+import dev.sheldan.abstracto.core.templating.service.TemplateService;
+import dev.sheldan.abstracto.core.utils.FutureUtils;
+import dev.sheldan.abstracto.webservices.config.WebServicesSlashCommandNames;
+import dev.sheldan.abstracto.webservices.config.WebserviceFeatureDefinition;
+import dev.sheldan.abstracto.webservices.wikipedia.config.WikipediaFeatureConfig;
+import dev.sheldan.abstracto.webservices.wikipedia.model.WikipediaArticleSummary;
+import dev.sheldan.abstracto.webservices.wikipedia.model.template.WikipediaArticleSummaryModel;
+import dev.sheldan.abstracto.webservices.wikipedia.service.WikipediaService;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+@Component
+public class WikipediaArticleSummaryCommand extends AbstractConditionableCommand {
+
+ private static final String WIKIPEDIA_ARTICLE_SUMMARY_RESPONSE_MODEL_TEMPLATE_KEY = "wikipediaArticleSummary_response";
+ private static final String WIKIPEDIA_ARTICLE_SUMMARY_COMMAND = "wikipediaArticleSummary";
+ private static final String SEARCH_QUERY_PARAMETER = "searchQuery";
+ private static final String LANGUAGE_PARAMETER = "language";
+
+ @Autowired
+ private WikipediaService wikipediaService;
+
+ @Autowired
+ private TemplateService templateService;
+
+ @Autowired
+ private ChannelService channelService;
+
+ @Autowired
+ private SlashCommandParameterService slashCommandParameterService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Autowired
+ private ConfigService configService;
+
+ @Override
+ public CompletableFuture executeAsync(CommandContext commandContext) {
+ String parameter = (String) commandContext.getParameters().getParameters().get(0);
+ try {
+ MessageToSend message = getMessageToSend(commandContext.getGuild().getIdLong(), parameter, null);
+ return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()))
+ .thenApply(unused -> CommandResult.fromSuccess());
+ } catch (IOException e) {
+ throw new AbstractoRunTimeException(e);
+ }
+ }
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ String query = slashCommandParameterService.getCommandOption(SEARCH_QUERY_PARAMETER, event, String.class);
+ String language;
+ if(slashCommandParameterService.hasCommandOption(LANGUAGE_PARAMETER, event)) {
+ language = slashCommandParameterService.getCommandOption(LANGUAGE_PARAMETER, event, String.class);
+ } else {
+ language = null;
+ }
+ try {
+ MessageToSend messageToSend = getMessageToSend(event.getGuild().getIdLong(), query, language);
+ return interactionService.replyMessageToSend(messageToSend, event)
+ .thenApply(interactionHook -> CommandResult.fromSuccess());
+ } catch (IOException e) {
+ throw new AbstractoRunTimeException(e);
+ }
+ }
+
+ private MessageToSend getMessageToSend(Long serverId, String searchInput, String language) throws IOException {
+ String languageKey = language != null ? language :
+ configService.getStringValueOrConfigDefault(WikipediaFeatureConfig.WIKIPEDIA_LANGUAGE_KEY_SYSTEM_CONFIG_KEY, serverId);
+ WikipediaArticleSummary definition = wikipediaService.getSummary(searchInput, languageKey);
+ WikipediaArticleSummaryModel model = WikipediaArticleSummaryModel
+ .builder()
+ .summary(definition.getSummary())
+ .fullURL(definition.getFullURL())
+ .title(definition.getTitle())
+ .build();
+ return templateService.renderEmbedTemplate(WIKIPEDIA_ARTICLE_SUMMARY_RESPONSE_MODEL_TEMPLATE_KEY, model, serverId);
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ List parameters = new ArrayList<>();
+ Parameter searchQueryParameter = Parameter
+ .builder()
+ .name(SEARCH_QUERY_PARAMETER)
+ .type(String.class)
+ .remainder(true)
+ .templated(true)
+ .build();
+ parameters.add(searchQueryParameter);
+ Parameter languageParameter = Parameter
+ .builder()
+ .name(LANGUAGE_PARAMETER)
+ .type(String.class)
+ .slashCommandOnly(true)
+ .optional(true)
+ .templated(true)
+ .build();
+ parameters.add(languageParameter);
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ List aliases = Arrays.asList("wiki");
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(WebServicesSlashCommandNames.WIKIPEDIA)
+ .groupName("article")
+ .commandName("summary")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(WIKIPEDIA_ARTICLE_SUMMARY_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .slashCommandConfig(slashCommandConfig)
+ .async(true)
+ .aliases(aliases)
+ .supportsEmbedException(true)
+ .causesReaction(false)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return WebserviceFeatureDefinition.WIKIPEDIA;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaServiceBean.java b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaServiceBean.java
new file mode 100644
index 000000000..eccb8db0e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaServiceBean.java
@@ -0,0 +1,64 @@
+package dev.sheldan.abstracto.webservices.wikipedia.service;
+
+import com.google.gson.Gson;
+import dev.sheldan.abstracto.webservices.wikipedia.exception.NoWikipediaArticleFoundException;
+import dev.sheldan.abstracto.webservices.wikipedia.exception.WikipediaRequestException;
+import dev.sheldan.abstracto.webservices.wikipedia.model.WikipediaArticleSummary;
+import dev.sheldan.abstracto.webservices.wikipedia.model.api.WikipediaResponse;
+import dev.sheldan.abstracto.webservices.wikipedia.model.api.WikipediaResponsePage;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+@Slf4j
+public class WikipediaServiceBean implements WikipediaService {
+
+ @Autowired
+ private OkHttpClient okHttpClient;
+
+ @Value("${abstracto.feature.webservices.wikipedia.summaryURL}")
+ private String articleSummaryUrl;
+
+ @Autowired
+ private Gson gson;
+
+ @Override
+ public WikipediaArticleSummary getSummary(String query, String language) throws IOException {
+ String formattedUrl = articleSummaryUrl.replace("{1}", language).replace("{2}", query);
+ Request request = new Request.Builder()
+ .url(formattedUrl)
+ .get()
+ .build();
+ Response response = okHttpClient.newCall(request).execute();
+ if(!response.isSuccessful()) {
+ if(log.isDebugEnabled()) {
+ log.error("Failed to retrieve wikipedia summary. Response had code {} with body {}.",
+ response.code(), response.body());
+ }
+ throw new WikipediaRequestException(response.code());
+ }
+ WikipediaResponse wikipediaResponse = gson.fromJson(response.body().string(), WikipediaResponse.class);
+ if(wikipediaResponse.getQuery() == null
+ || wikipediaResponse.getQuery().getPages() == null
+ || (wikipediaResponse.getQuery().getPages().stream().anyMatch(wikipediaResponsePageModel -> wikipediaResponsePageModel.getPageId().equals(-1L))
+ && wikipediaResponse.getQuery().getPages().size() == 1)
+ ) {
+ throw new NoWikipediaArticleFoundException();
+ } else {
+ WikipediaResponsePage page = wikipediaResponse.getQuery().getPages().get(0);
+ return WikipediaArticleSummary
+ .builder()
+ .title(page.getTitle())
+ .summary(page.getExtract())
+ .fullURL(page.getFullUrl())
+ .build();
+ }
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/collection.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/collection.xml
new file mode 100644
index 000000000..29116ec67
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/collection.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/command.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/command.xml
new file mode 100644
index 000000000..6b5ebb40a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/command.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/data.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/data.xml
new file mode 100644
index 000000000..d492a359d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/data.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/feature.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/feature.xml
new file mode 100644
index 000000000..1c6cd9f12
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/1.5.20/seedData/feature.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/webservices-changeLog.xml b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/webservices-changeLog.xml
index ba4363854..ead5748e6 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/webservices-changeLog.xml
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/migrations/webservices-changeLog.xml
@@ -9,4 +9,5 @@
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/webservices-config.properties b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/webservices-config.properties
index 7057b9511..0299ed368 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/webservices-config.properties
+++ b/abstracto-application/abstracto-modules/webservices/webservices-impl/src/main/resources/webservices-config.properties
@@ -27,4 +27,17 @@ abstracto.featureFlags.openWeatherMap.featureName=openWeatherMap
abstracto.featureFlags.openWeatherMap.enabled=false
abstracto.systemConfigs.openWeatherMapLanguageKey.name=openWeatherMapLanguageKey
-abstracto.systemConfigs.openWeatherMapLanguageKey.stringValue=en
\ No newline at end of file
+abstracto.systemConfigs.openWeatherMapLanguageKey.stringValue=en
+
+abstracto.featureFlags.wikipedia.featureName=wikipedia
+abstracto.featureFlags.wikipedia.enabled=false
+
+abstracto.feature.webservices.wikipedia.summaryURL=https://{1}.wikipedia.org/w/api.php?action=query&format=json&prop=pageprops%7Cextracts%7Cinfo&list=&meta=&inprop=url&redirects=1&formatversion=2&ppprop=disambiguation&exintro=1&explaintext=1&titles={2}
+
+abstracto.systemConfigs.wikipediaLanguageKey.name=wikipediaLanguageKey
+abstracto.systemConfigs.wikipediaLanguageKey.stringValue=en
+
+abstracto.featureFlags.dictionary.featureName=dictionary
+abstracto.featureFlags.dictionary.enabled=false
+
+abstracto.feature.webservices.dictionaryapi.definitionURL=https://api.dictionaryapi.dev/api/v2/entries/en/{1}
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebServicesSlashCommandNames.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebServicesSlashCommandNames.java
index b461637dc..9f5195e71 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebServicesSlashCommandNames.java
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebServicesSlashCommandNames.java
@@ -4,4 +4,6 @@ public class WebServicesSlashCommandNames {
public static final String YOUTUBE = "youtube";
public static final String URBAN = "urban";
public static final String WEATHER = "weather";
+ public static final String WIKIPEDIA = "wikipedia";
+ public static final String DICTIONARY = "dictionary";
}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebserviceFeatureDefinition.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebserviceFeatureDefinition.java
index 36631bfa8..fa9edb685 100644
--- a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebserviceFeatureDefinition.java
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/config/WebserviceFeatureDefinition.java
@@ -8,7 +8,9 @@ public enum WebserviceFeatureDefinition implements FeatureDefinition {
YOUTUBE("youtube"),
URBAN_DICTIONARY("urban"),
THREAD_READER("threadReader"),
- OPEN_WEATHER_MAP("openWeatherMap");
+ OPEN_WEATHER_MAP("openWeatherMap"),
+ WIKIPEDIA("wikipedia"),
+ DICTIONARY("dictionary");
private String key;
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/config/DictionaryApiFeatureConfig.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/config/DictionaryApiFeatureConfig.java
new file mode 100644
index 000000000..23148f0c0
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/config/DictionaryApiFeatureConfig.java
@@ -0,0 +1,16 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.config;
+
+import dev.sheldan.abstracto.core.config.FeatureConfig;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.webservices.config.WebserviceFeatureDefinition;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DictionaryApiFeatureConfig implements FeatureConfig {
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return WebserviceFeatureDefinition.DICTIONARY;
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/DictionaryApiRequestException.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/DictionaryApiRequestException.java
new file mode 100644
index 000000000..6e00e8c45
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/DictionaryApiRequestException.java
@@ -0,0 +1,21 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class DictionaryApiRequestException extends AbstractoRunTimeException implements Templatable {
+
+ public DictionaryApiRequestException(Integer responseCode) {
+ super(String.format("Request failure towards dictionary api %s.", responseCode));
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "dictionary_api_request_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/NoDictionaryApiDefinitionFoundException.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/NoDictionaryApiDefinitionFoundException.java
new file mode 100644
index 000000000..5722d26da
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/exception/NoDictionaryApiDefinitionFoundException.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class NoDictionaryApiDefinitionFoundException extends AbstractoRunTimeException implements Templatable {
+ public NoDictionaryApiDefinitionFoundException() {
+ super("No dictionary api definition found.");
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "no_dictionary_api_definition_found_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordDefinition.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordDefinition.java
new file mode 100644
index 000000000..856593697
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordDefinition.java
@@ -0,0 +1,22 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model;
+
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.api.DictionaryApiWordDefinition;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Builder
+@Setter
+public class WordDefinition {
+ private String definition;
+ private String example;
+
+ public static WordDefinition fromResponseDefinition(DictionaryApiWordDefinition responseItem) {
+ return WordDefinition
+ .builder()
+ .example(responseItem.getExample())
+ .definition(responseItem.getDefinition())
+ .build();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordMeaning.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordMeaning.java
new file mode 100644
index 000000000..026e04a58
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/WordMeaning.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Builder
+@Setter
+public class WordMeaning {
+ private String word;
+ private List definitions;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiResponseItem.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiResponseItem.java
new file mode 100644
index 000000000..784be9f7e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiResponseItem.java
@@ -0,0 +1,16 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model.api;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Builder
+@Setter
+public class DictionaryApiResponseItem {
+ private String word;
+ private String origin;
+ private List meanings;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordDefinition.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordDefinition.java
new file mode 100644
index 000000000..8b67c7b50
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordDefinition.java
@@ -0,0 +1,13 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model.api;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class DictionaryApiWordDefinition {
+ private String definition;
+ private String example;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordMeaning.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordMeaning.java
new file mode 100644
index 000000000..b7ffde8db
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/api/DictionaryApiWordMeaning.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model.api;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@Builder
+public class DictionaryApiWordMeaning {
+ private String partOfSpeech;
+ private List definitions;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryDefinition.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryDefinition.java
new file mode 100644
index 000000000..4155ff148
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryDefinition.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model.template;
+
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.WordDefinition;
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class DictionaryDefinition {
+ private String definition;
+ private String example;
+
+ public static DictionaryDefinition fromWordDefinition(WordDefinition definition) {
+ return DictionaryDefinition
+ .builder()
+ .example(definition.getExample())
+ .definition(definition.getDefinition())
+ .build();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryMeaning.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryMeaning.java
new file mode 100644
index 000000000..2fea7f7ab
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/model/template/DictionaryMeaning.java
@@ -0,0 +1,13 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.model.template;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+@Builder
+public class DictionaryMeaning {
+ private String word;
+ private List definitions;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiService.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiService.java
new file mode 100644
index 000000000..5819a25ac
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/dictionaryapi/service/DictionaryApiService.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.webservices.dictionaryapi.service;
+
+import dev.sheldan.abstracto.webservices.dictionaryapi.model.WordMeaning;
+
+import java.io.IOException;
+
+public interface DictionaryApiService {
+ WordMeaning getDefinitions(String query) throws IOException;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/config/WikipediaFeatureConfig.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/config/WikipediaFeatureConfig.java
new file mode 100644
index 000000000..e4a4d3d18
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/config/WikipediaFeatureConfig.java
@@ -0,0 +1,24 @@
+package dev.sheldan.abstracto.webservices.wikipedia.config;
+
+import dev.sheldan.abstracto.core.config.FeatureConfig;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.webservices.config.WebserviceFeatureDefinition;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Component
+public class WikipediaFeatureConfig implements FeatureConfig {
+
+ public static final String WIKIPEDIA_LANGUAGE_KEY_SYSTEM_CONFIG_KEY = "wikipediaLanguageKey";
+ @Override
+ public FeatureDefinition getFeature() {
+ return WebserviceFeatureDefinition.WIKIPEDIA;
+ }
+
+ @Override
+ public List getRequiredSystemConfigKeys() {
+ return Arrays.asList(WIKIPEDIA_LANGUAGE_KEY_SYSTEM_CONFIG_KEY);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/NoWikipediaArticleFoundException.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/NoWikipediaArticleFoundException.java
new file mode 100644
index 000000000..34ff1ea2d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/NoWikipediaArticleFoundException.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.webservices.wikipedia.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class NoWikipediaArticleFoundException extends AbstractoRunTimeException implements Templatable {
+ public NoWikipediaArticleFoundException() {
+ super("No wikipedia article found.");
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "no_wikipedia_article_found_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/WikipediaRequestException.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/WikipediaRequestException.java
new file mode 100644
index 000000000..143c556e9
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/exception/WikipediaRequestException.java
@@ -0,0 +1,21 @@
+package dev.sheldan.abstracto.webservices.wikipedia.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class WikipediaRequestException extends AbstractoRunTimeException implements Templatable {
+
+ public WikipediaRequestException(Integer responseCode) {
+ super(String.format("Request failure towards wikipedia %s.", responseCode));
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "wikipedia_request_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/WikipediaArticleSummary.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/WikipediaArticleSummary.java
new file mode 100644
index 000000000..1817229ac
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/WikipediaArticleSummary.java
@@ -0,0 +1,14 @@
+package dev.sheldan.abstracto.webservices.wikipedia.model;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class WikipediaArticleSummary {
+ private String title;
+ private String summary;
+ private String fullURL;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponse.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponse.java
new file mode 100644
index 000000000..6f719fddc
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponse.java
@@ -0,0 +1,13 @@
+package dev.sheldan.abstracto.webservices.wikipedia.model.api;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class WikipediaResponse {
+ private WikipediaResponseQuery query;
+}
+
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponsePage.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponsePage.java
new file mode 100644
index 000000000..be87ad4bb
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponsePage.java
@@ -0,0 +1,25 @@
+package dev.sheldan.abstracto.webservices.wikipedia.model.api;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Map;
+
+@Getter
+@Setter
+@Builder
+public class WikipediaResponsePage {
+ @SerializedName("pageid")
+ private Long pageId;
+ private String title;
+ private String extract;
+ @SerializedName("fullurl")
+ private String fullUrl;
+ private Map pageprops;
+
+ public boolean isDisambiguation() {
+ return pageprops != null && pageprops.containsKey("disambiguation");
+ }
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponseQuery.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponseQuery.java
new file mode 100644
index 000000000..e07785d69
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/api/WikipediaResponseQuery.java
@@ -0,0 +1,14 @@
+package dev.sheldan.abstracto.webservices.wikipedia.model.api;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@Builder
+public class WikipediaResponseQuery {
+ private List pages;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/template/WikipediaArticleSummaryModel.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/template/WikipediaArticleSummaryModel.java
new file mode 100644
index 000000000..24a92917b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/model/template/WikipediaArticleSummaryModel.java
@@ -0,0 +1,14 @@
+package dev.sheldan.abstracto.webservices.wikipedia.model.template;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class WikipediaArticleSummaryModel {
+ private String summary;
+ private String title;
+ private String fullURL;
+}
diff --git a/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaService.java b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaService.java
new file mode 100644
index 000000000..4214337f8
--- /dev/null
+++ b/abstracto-application/abstracto-modules/webservices/webservices-int/src/main/java/dev/sheldan/abstracto/webservices/wikipedia/service/WikipediaService.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.webservices.wikipedia.service;
+
+import dev.sheldan.abstracto.webservices.wikipedia.model.WikipediaArticleSummary;
+
+import java.io.IOException;
+
+public interface WikipediaService {
+ WikipediaArticleSummary getSummary(String query, String language) throws IOException;
+}