[AB-82] adding urban dictionary api with a command

adding Instant handling to GSON
This commit is contained in:
Sheldan
2021-03-29 01:13:46 +02:00
parent c2b413e4b9
commit 45c1df0d44
21 changed files with 292 additions and 6 deletions

View File

@@ -0,0 +1,82 @@
package dev.sheldan.abstracto.webservices.urban.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.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
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.WebserviceFeatureDefinition;
import dev.sheldan.abstracto.webservices.urban.model.UrbanDefinition;
import dev.sheldan.abstracto.webservices.urban.model.UrbanResponseModel;
import dev.sheldan.abstracto.webservices.urban.service.UrbanService;
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 UrbanDefine extends AbstractConditionableCommand {
@Autowired
private UrbanService urbanService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
private static final String URBAN_DEFINE_RESPONSE_MODEL_TEMPLATE_KEY = "urban_define_response_model";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String parameter = (String) commandContext.getParameters().getParameters().get(0);
try {
UrbanDefinition definition = urbanService.getUrbanDefinition(parameter);
UrbanResponseModel model = (UrbanResponseModel) ContextConverter.slimFromCommandContext(commandContext, UrbanResponseModel.class);
model.setDefinition(definition);
MessageToSend message = templateService.renderEmbedTemplate(URBAN_DEFINE_RESPONSE_MODEL_TEMPLATE_KEY, model);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
}
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("searchQuery").type(String.class).remainder(true).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
List<String> aliases = Arrays.asList("ud");
return CommandConfiguration.builder()
.name("urbanDefine")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.aliases(aliases)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return WebserviceFeatureDefinition.URBAN_DICTIONARY;
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.webservices.urban.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class UrbanResponse {
private List<UrbanResponseDefinition> list;
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.webservices.urban.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@Builder
public class UrbanResponseDefinition {
private String definition;
private String permalink;
private Long thumbs_up;
private Long thumbs_down;
private String author;
private String word;
private Long defid;
private Instant written_on;
private String example;
}

View File

@@ -0,0 +1,50 @@
package dev.sheldan.abstracto.webservices.urban.service;
import com.google.gson.Gson;
import dev.sheldan.abstracto.webservices.urban.exception.NoUrbanDefinitionFoundException;
import dev.sheldan.abstracto.webservices.urban.model.UrbanDefinition;
import dev.sheldan.abstracto.webservices.urban.model.UrbanResponse;
import dev.sheldan.abstracto.webservices.urban.model.UrbanResponseDefinition;
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
public class UrbanServiceBean implements UrbanService {
@Autowired
private OkHttpClient okHttpClient;
@Value("${abstracto.feature.webservices.urban.requestURL}")
private String requestUrl;
@Autowired
private Gson gson;
@Override
public UrbanDefinition getUrbanDefinition(String query) throws IOException {
Request request = new Request.Builder().url(String.format(requestUrl, query)).get().build();
Response response = okHttpClient.newCall(request).execute();
UrbanResponse urbanResponse = gson.fromJson(response.body().string(), UrbanResponse.class);
if(urbanResponse.getList().isEmpty()) {
throw new NoUrbanDefinitionFoundException();
} else {
UrbanResponseDefinition definition = urbanResponse.getList().get(0);
return UrbanDefinition
.builder()
.definition(definition.getDefinition())
.author(definition.getAuthor())
.url(definition.getPermalink())
.creationDate(definition.getWritten_on())
.downVoteCount(definition.getThumbs_down())
.upvoteCount(definition.getThumbs_up())
.example(definition.getExample())
.build();
}
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.webservices.youtube.commands;
package dev.sheldan.abstracto.webservices.youtube.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;

View File

@@ -8,6 +8,7 @@
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="youtubeFeature" value="(SELECT id FROM feature WHERE key = 'youtube')"/>
<property name="urbanFeature" value="(SELECT id FROM feature WHERE key = 'urban')"/>
<changeSet author="Sheldan" id="webservices_youtube-commands">
<insert tableName="command">
@@ -17,4 +18,12 @@
</insert>
</changeSet>
<changeSet author="Sheldan" id="webservices_urban-commands">
<insert tableName="command">
<column name="name" value="urbanDefine"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${urbanFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -10,5 +10,8 @@
<insert tableName="feature">
<column name="key" value="youtube"/>
</insert>
<insert tableName="feature">
<column name="key" value="urban"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -6,5 +6,5 @@
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="1.0-webservices/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.5-webservices/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -1,4 +1,9 @@
abstracto.featureFlags.youtube.featureName=youtube
abstracto.featureFlags.youtube.enabled=false
abstracto.feature.youtube.apiKey=${YOUTUBE_API_KEY}
abstracto.featureFlags.urban.featureName=urban
abstracto.featureFlags.urban.enabled=false
abstracto.feature.youtube.apiKey=${YOUTUBE_API_KEY}
abstracto.feature.webservices.urban.requestURL=https://api.urbandictionary.com/v0/define?term=%s

View File

@@ -5,7 +5,7 @@ import lombok.Getter;
@Getter
public enum WebserviceFeatureDefinition implements FeatureDefinition {
YOUTUBE("youtube");
YOUTUBE("youtube"), URBAN_DICTIONARY("urban");
private String key;

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.webservices.urban.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 UrbanFeatureConfig implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return WebserviceFeatureDefinition.URBAN_DICTIONARY;
}
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.webservices.urban.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
public class NoUrbanDefinitionFoundException extends AbstractoRunTimeException implements Templatable {
@Override
public String getTemplateName() {
return "no_urban_definition_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.webservices.urban.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@Builder
public class UrbanDefinition {
private String definition;
private String author;
private String example;
private Instant creationDate;
private String url;
private Long upvoteCount;
private Long downVoteCount;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.webservices.urban.model;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class UrbanResponseModel extends SlimUserInitiatedServerContext {
private UrbanDefinition definition;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.webservices.urban.service;
import dev.sheldan.abstracto.webservices.urban.model.UrbanDefinition;
import java.io.IOException;
public interface UrbanService {
UrbanDefinition getUrbanDefinition(String query) throws IOException;
}

View File

@@ -15,6 +15,7 @@ import org.springframework.context.annotation.Configuration;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -35,6 +36,7 @@ public class CoreConfig {
public Gson gson() {
return new GsonBuilder()
.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter())
.registerTypeAdapter(Instant.class, new InstantTimeAdapter())
.setPrettyPrinting().create();
}

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.core.config;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import java.lang.reflect.Type;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
public class InstantTimeAdapter implements JsonDeserializer<Instant> {
@Override
public Instant deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) {
TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(jsonElement.getAsString());
return Instant.from(ta);
}
}

View File

@@ -3,7 +3,6 @@ package dev.sheldan.abstracto.core.config;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.time.Instant;
@@ -15,7 +14,7 @@ import java.time.temporal.TemporalAccessor;
public class OffsetDateTimeAdapter implements JsonDeserializer<OffsetDateTime> {
@Override
public OffsetDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
public OffsetDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) {
TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(jsonElement.getAsString());
Instant i = Instant.from(ta);
return OffsetDateTime.ofInstant(i, ZoneId.systemDefault());

View File

@@ -11,3 +11,12 @@ Search for a youtube video::
* Usage: `youtubeSearch <query>`
* Aliases: `yt`
* Description: Searches youtube for a video with this query, and returns the link with additional information.
=== Urban dictionary
Feature key: `urban`
==== Command
Search for an urban dictionary definition::
* Usage: `urbanDefine <query>`
* Aliases: `ud`
* Description: Searches an urban dictionary definition, and returns the definition, with an example and meta information.