Compare commits

...

21 Commits

Author SHA1 Message Date
Sheldan
dbf478c44c [maven-release-plugin] prepare release abstracto-application-1.4.23 2023-05-18 22:53:52 +02:00
Sheldan
3df688571f [AB-93] changing how MessageToSend handles attached plaintext files 2023-05-18 22:32:56 +02:00
Sheldan
ca530949c6 [AB-92] fixing not providing member display object for modmail thread already exists embed 2023-05-18 01:23:42 +02:00
Sheldan
724930e5a4 [AB-xxx] updating readme
upgrading JDA version to 5.0.0-beta.5
2023-03-20 23:16:52 +01:00
Sheldan
2875da117a [AB-91] adding location id to the available information for weather model 2023-03-19 10:54:54 +01:00
Sheldan
f95ba6c28f [maven-release-plugin] prepare for next development iteration 2023-03-19 00:14:21 +01:00
Sheldan
1b2e7654f9 [maven-release-plugin] prepare release abstracto-application-1.4.22 2023-03-19 00:14:16 +01:00
Sheldan
54976ed1d4 [AB-89] adding command to retrieve weather data 2023-03-19 00:09:43 +01:00
Sheldan
735816f5dd [AB-xxx] adding feature to split field value by configurable amount
using larger images for member avatar
2023-03-05 13:49:32 +01:00
Sheldan
a984bdb84e [AB-86] fixing exception in case of a re-joining user without experience role 2023-02-26 14:16:27 +01:00
Sheldan
21add6585d [maven-release-plugin] prepare for next development iteration 2023-02-26 10:58:01 +01:00
Sheldan
99e72245f3 [maven-release-plugin] prepare release abstracto-application-1.4.21 2023-02-26 10:57:57 +01:00
Sheldan
9d184ff560 [AB-85] adding feature mode to automatically create a thread for suggestions
reworking post target service to function with optionals
fixing trying to add reactions for suggestions if there was no message created
not showing votes in case the buttons feature mode is active
2023-02-26 10:50:36 +01:00
Sheldan
97895f5c56 [AB-84] fixing incorrect calculation for agreement percentage 2023-02-25 18:33:02 +01:00
Sheldan
f091559c49 [AB-84] adding necessary information to suggestion update message 2023-02-25 18:19:01 +01:00
Sheldan
fa7730975e [maven-release-plugin] prepare for next development iteration 2023-02-15 00:05:59 +01:00
Sheldan
03d7b9e2e2 [maven-release-plugin] prepare release abstracto-application-1.4.20 2023-02-15 00:05:51 +01:00
Sheldan
a0bff12263 [AB-xxx] adding default value for user experience object 2023-02-14 23:54:42 +01:00
Sheldan
70e708601e [AB-xxx] exception logging improvements for experience tracking 2023-02-14 21:14:18 +01:00
Sheldan
f01418d0de [AB-xxx] upgrading JDA version to version 5.0.0-beta.3 2023-02-04 20:57:02 +01:00
Sheldan
ad539adb2a [maven-release-plugin] prepare for next development iteration 2023-02-04 18:26:38 +01:00
129 changed files with 1071 additions and 372 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Sheldan
Copyright (c) 2023 Sheldan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -14,22 +14,20 @@ An example implementation of this bot can be seen [here](https://github.com/Shel
## Technologies
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-alpha.21
* [Spring boot](https://github.com/spring-projects/spring-boot) is used as a framework to create standalone application in Java with Java EE methods. (including Dependency injection and more)
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-beta.5
* [Spring boot](https://github.com/spring-projects/spring-boot) is used as a framework to create standalone application in Java with Java EE methods. (including dependency injection and more)
* [Hibernate](https://github.com/hibernate/hibernate-orm) is used as a reference implementation of JPA.
* [Freemarker](https://github.com/apache/freemarker) is used as a templating engine. This is used to provide internationalization for user facing text and enable dynamic embed configuration.
* [Ehcache](https://github.com/ehcache/ehcache3) is used as a caching implementation.
* [Lombok](https://github.com/rzwitserloot/lombok) is used as a framework in order to speed up creation of container classes and builders.
* [Quartz](https://github.com/quartz-scheduler/quartz) is used as a scheduling framework in order to provide functionalities which either require a delayed or cronjob behaviour.
* [Docker](https://github.com/docker) is used to package the application into a container and [Docker Compose](https://github.com/docker/compose) is used to connect the required containers together.
* [Quartz](https://github.com/quartz-scheduler/quartz) is used as a scheduling framework in order to provide functionalities which either require a scheduled or cronjob behaviour.
* [Docker](https://github.com/docker) is used to package the application into an image and [Docker Compose](https://github.com/docker/compose) is used to build the images
* [Liquibase](https://github.com/liquibase/liquibase) is used to manage changes to the database
## Documentation
A detailed documentation of the pure form of Abstracto including the terminology and commands in HTML form is available [here](https://sheldan.github.io/abstracto-docs/current). The PDF is available [here](https://sheldan.github.io/abstracto-docs/current/documentation.pdf)
If you want to view the documentation to an earlier released version you need to append the desired version to the URL. The current version will be available aforementioned URL, but it is not right now, because Abstracto has not been released yet.
## Customization documentation
TBD when Abstracto is released, as the current version is still being adapted, because of findings from the example customization in Crimson.
## Issues
If you find any issue, feel free to create a GitHub issue.

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>assignable-roles-int</artifactId>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>dynamic-activity</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>dynamic-activity</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -138,7 +138,10 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
// we store when the user is eligible for experience _again_
Long maxSeconds = configService.getLongValueOrConfigDefault(EXP_COOLDOWN_SECONDS_KEY, serverId);
serverExperience.put(userId, Instant.now().plus(maxSeconds, ChronoUnit.SECONDS));
CompletableFuture.runAsync(() -> self.addExperienceToMember(member, message), experienceUpdateExecutor);
CompletableFuture.runAsync(() -> self.addExperienceToMember(member, message), experienceUpdateExecutor).exceptionally(throwable -> {
log.error("Failed to add experience to member {} in server {}.", message.getAuthor().getId(), message.getGuild().getIdLong(), throwable);
return null;
});
}
} finally {
runTimeExperienceService.releaseLock();
@@ -272,13 +275,13 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
CompletableFuture<Void> returningFuture;
if(!Objects.equals(oldRoleId, newRoleId) || forceRoles) {
CompletableFuture<Void> addingFuture;
if(oldRoleId != null || forceRoles) {
if(oldRoleId != null) {
addingFuture = roleService.removeRoleFromMemberAsync(member, oldRoleId);
} else {
addingFuture = CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> removingFeature;
if(newRoleId != null || forceRoles) {
if(newRoleId != null) {
removingFeature = roleService.addRoleToMemberAsync(member, newRoleId);
} else {
removingFeature = CompletableFuture.completedFuture(null);
@@ -336,8 +339,8 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
oldLevel);
aUserExperience.setCurrentLevel(newLevel);
AExperienceRole calculatedNewRole = experienceRoleService.calculateRole(roles, newLevel.getLevel());
Long oldRoleId = aUserExperience.getCurrentExperienceRole() != null ? aUserExperience.getCurrentExperienceRole().getRole().getId() : null;
Long newRoleId = calculatedNewRole != null ? calculatedNewRole.getRole().getId() : null;
Long oldRoleId = aUserExperience.getCurrentExperienceRole() != null && aUserExperience.getCurrentExperienceRole().getRole() != null ? aUserExperience.getCurrentExperienceRole().getRole().getId() : null;
Long newRoleId = calculatedNewRole != null && calculatedNewRole.getRole() != null ? calculatedNewRole.getRole().getId() : null;
result.setOldRoleId(oldRoleId);
result.setNewRoleId(newRoleId);
if(message != null

View File

@@ -58,6 +58,7 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage
.messageCount(0L)
.server(aUserInAServer.getServerReference())
.experienceGainDisabled(false)
.levelUpNotification(true)
.user(aUserInAServer)
.id(aUserInAServer.getUserInServerId())
.currentLevel(startingLevel)

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -12,6 +12,7 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -81,6 +82,7 @@ public class Contact extends AbstractConditionableCommand {
ModMailThreadExistsModel model = ModMailThreadExistsModel
.builder()
.existingModMailThread(existingThread)
.executingMemberDisplay(MemberNameDisplay.fromMember(commandContext.getAuthor()))
.build();
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
@@ -106,6 +108,7 @@ public class Contact extends AbstractConditionableCommand {
ModMailThreadExistsModel model = ModMailThreadExistsModel
.builder()
.existingModMailThread(existingThread)
.executingMemberDisplay(MemberNameDisplay.fromMember(event.getMember()))
.build();
return interactionService.replyEmbed(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
import dev.sheldan.abstracto.core.utils.ChannelUtils;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import lombok.Builder;
@@ -15,6 +16,7 @@ import lombok.Setter;
@Builder
public class ModMailThreadExistsModel {
private ModMailThread existingModMailThread;
private MemberNameDisplay executingMemberDisplay;
public String getThreadUrl() {
return ChannelUtils.buildChannelUrl(existingModMailThread.getServer().getId(), existingModMailThread.getChannel().getId());

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto</groupId>
<artifactId>abstracto-application</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>profanity-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>profanity-filter</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -249,6 +249,7 @@ public class RemindServiceBeanTest {
when(guild.getIdLong()).thenReturn(8L);
when(remindedMember.getIdLong()).thenReturn(9L);
when(remindedMember.getUser()).thenReturn(jdaUser);
when(remindedMember.getEffectiveAvatar()).thenReturn(Mockito.mock(ImageProxy.class));
when(jdaUser.getDefaultAvatar()).thenReturn(Mockito.mock(ImageProxy.class));
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getTargetDate()).thenReturn(Instant.now());

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>repost-detection</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>repost-detection</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>starboard</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>starboard</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -7,7 +7,6 @@ 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.exception.UploadFileTooLargeException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
@@ -25,8 +24,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -46,8 +43,6 @@ import java.util.concurrent.CompletableFuture;
public class ExportEmoteStats extends AbstractConditionableCommand {
public static final String DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_no_stats_available_response";
public static final String DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY = "downloadEmoteStats_file_name";
public static final String DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY = "downloadEmoteStats_file_content";
public static final String DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_response";
@Autowired
private ServerManagementService serverManagementService;
@@ -93,30 +88,10 @@ public class ExportEmoteStats extends AbstractConditionableCommand {
.requester(commandContext.getAuthor())
.statsSince(toUseForModel)
.build();
String fileName = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY, model);
String fileContent = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY, model);
model.setEmoteStatsFileName(fileName);
File tempFile = fileService.createTempFile(fileName);
try {
fileService.writeContentToFile(tempFile, fileContent);
long maxFileSize = commandContext.getGuild().getMaxFileSize();
// in this case, we cannot upload the file, so we need to fail
if(tempFile.length() > maxFileSize) {
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY, model, actualServer.getId());
messageToSend.getAttachedFiles().get(0).setFile(tempFile.getAbsoluteFile());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary export emote statistics file {}.", tempFile.getAbsoluteFile(), e);
}
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY, model, actualServer.getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override

View File

@@ -2,8 +2,6 @@ package dev.sheldan.abstracto.statistic.emote.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.UploadFileTooLargeException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
@@ -14,7 +12,6 @@ import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.model.DownloadEmoteStatsModel;
import dev.sheldan.abstracto.statistic.emote.model.database.UsedEmote;
import dev.sheldan.abstracto.statistic.emote.service.management.UsedEmoteManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.junit.Assert;
import org.junit.Test;
@@ -22,11 +19,8 @@ import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -77,53 +71,11 @@ public class ExportEmoteStatsTest {
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY), any(), eq(commandContext.getChannel()));
}
@Test(expected = AbstractoRunTimeException.class)
public void testFileIOException() throws IOException {
CommandContext commandContext = CommandTestUtilities.getNoParameters();
mockServerAndFileRendering(commandContext);
File file = Mockito.mock(File.class);
when(fileService.createTempFile(FILE_NAME)).thenReturn(file);
doThrow(new IOException()).when(fileService).writeContentToFile(file, FILE_CONTENT);
testUnit.executeAsync(commandContext);
}
@Test(expected = UploadFileTooLargeException.class)
public void testExportAllEmoteStatsTooBig() throws IOException {
CommandContext commandContext = CommandTestUtilities.getNoParameters();
when(commandContext.getGuild().getMaxFileSize()).thenReturn(2L);
mockServerAndFileRendering(commandContext);
File file = Mockito.mock(File.class);
when(fileService.createTempFile(FILE_NAME)).thenReturn(file);
when(file.length()).thenReturn(3L);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
CommandTestUtilities.checkSuccessfulCompletionAsync(asyncResult);
verify(fileService, times(1)).writeContentToFile(file, FILE_CONTENT);
verify(fileService, times(1)).safeDelete(file);
verifyModel();
}
@Test
public void testFeature() {
Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature());
}
private void verifyModel() {
DownloadEmoteStatsModel model = modelArgumentCaptor.getValue();
Assert.assertEquals(1, model.getEmotes().size());
Assert.assertEquals(usedEmote, model.getEmotes().get(0));
}
private void mockServerAndFileRendering(CommandContext commandContext) {
when(commandContext.getGuild().getIdLong()).thenReturn(SERVER_ID);
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(SERVER_ID)).thenReturn(server);
List<UsedEmote> usedEmotes = Arrays.asList(usedEmote);
when(usedEmoteManagementService.loadEmoteUsagesForServerSince(server, Instant.EPOCH)).thenReturn(usedEmotes);
when(templateService.renderTemplate(eq(DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY), modelArgumentCaptor.capture())).thenReturn(FILE_NAME);
when(templateService.renderTemplate(eq(DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY), any())).thenReturn(FILE_CONTENT);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>suggestion</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -29,6 +29,8 @@ import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementS
import dev.sheldan.abstracto.suggestion.service.management.SuggestionVoteManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -37,9 +39,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Component
@@ -131,6 +131,7 @@ public class SuggestionServiceBean implements SuggestionService {
if(autoEvaluationEnabled) {
autoEvaluateDays = configService.getLongValueOrConfigDefault(SUGGESTION_AUTO_EVALUATE_DAYS_CONFIG_KEY, serverId);
}
Instant autoEvaluationTargetDate = autoEvaluationEnabled ? Instant.now().plus(autoEvaluateDays, ChronoUnit.DAYS) : null;
SuggestionLog model = SuggestionLog
.builder()
.suggestionId(newSuggestionId)
@@ -143,7 +144,7 @@ public class SuggestionServiceBean implements SuggestionService {
.suggester(suggester.getUser())
.text(text)
.autoEvaluationEnabled(autoEvaluationEnabled)
.autoEvaluationTargetDate(autoEvaluationEnabled ? Instant.now().plus(autoEvaluateDays, ChronoUnit.DAYS) : null)
.autoEvaluationTargetDate(autoEvaluationTargetDate)
.build();
if(useButtons) {
setupButtonIds(model);
@@ -151,32 +152,73 @@ public class SuggestionServiceBean implements SuggestionService {
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_CREATION_TEMPLATE, model, serverId);
log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, serverId, suggester.getIdLong());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, serverId);
List<ButtonConfigModel> buttonConfigModels = Arrays.asList(model.getAgreeButtonModel(), model.getDisAgreeButtonModel(), model.getRemoveVoteButtonModel());
return FutureUtils.toSingleFutureGeneric(completableFutures)
.thenCompose(aVoid -> self.addDeletionPossibility(suggestionChannelId, suggestionMessageId, text, suggester, serverId, newSuggestionId, completableFutures, model));
.thenCompose(aVoid -> self.addVotingPossibility(suggestionChannelId, suggestionMessageId, text, suggester, serverId, newSuggestionId, completableFutures, buttonConfigModels, useButtons))
.thenCompose(aVoid -> self.createSuggestionThread(serverId, newSuggestionId, suggester, text, autoEvaluationEnabled, autoEvaluationTargetDate));
}
@Transactional
public CompletableFuture<Void> addDeletionPossibility(Long suggestionChannelId, Long suggestionMessageId, String text, Member suggester, Long serverId,
Long newSuggestionId, List<CompletableFuture<Message>> completableFutures, SuggestionLog model) {
Message message = completableFutures.get(0).join();
if(model.getUseButtons()) {
configureDecisionButtonPayload(serverId, newSuggestionId, model.getAgreeButtonModel(), SuggestionDecision.AGREE);
configureDecisionButtonPayload(serverId, newSuggestionId, model.getDisAgreeButtonModel(), SuggestionDecision.DISAGREE);
configureDecisionButtonPayload(serverId, newSuggestionId, model.getRemoveVoteButtonModel(), SuggestionDecision.REMOVE_VOTE);
AServer server = serverManagementService.loadServer(serverId);
componentPayloadManagementService.createButtonPayload(model.getAgreeButtonModel(), server);
componentPayloadManagementService.createButtonPayload(model.getDisAgreeButtonModel(), server);
componentPayloadManagementService.createButtonPayload(model.getRemoveVoteButtonModel(), server);
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, suggestionChannelId, suggestionMessageId);
return CompletableFuture.completedFuture(null);
public CompletableFuture<Void> createSuggestionThread(Long serverId, Long suggestionId, Member suggester, String suggestionText,
Boolean autoEvaluationEnabled, Instant autoEvaluationTargetDate) {
if(featureModeService.featureModeActive(SuggestionFeatureDefinition.SUGGEST, serverId, SuggestionFeatureMode.SUGGESTION_THREAD)) {
Optional<GuildMessageChannel> suggestionTargetChannelOptional = postTargetService.getPostTargetChannel(SuggestionPostTarget.SUGGESTION, serverId);
log.info("Trying to create thread for suggestion {} in server {}.", suggestionId, serverId);
if (suggestionTargetChannelOptional.isPresent()) {
GuildMessageChannel messageChannel = suggestionTargetChannelOptional.get();
if(messageChannel instanceof IThreadContainer) {
SuggestionThreadModel model = SuggestionThreadModel
.builder()
.suggestionId(suggestionId)
.suggester(suggester.getUser())
.member(suggester)
.autoEvaluationEnabled(autoEvaluationEnabled)
.text(suggestionText)
.autoEvaluationTargetDate(autoEvaluationTargetDate)
.build();
IThreadContainer threadContainer = (IThreadContainer) messageChannel;
String threadName = templateService.renderTemplate("suggestion_thread_name", model, serverId);
return threadContainer.createThreadChannel(threadName).submit()
.thenAccept(threadChannel -> log.info("Created thread for suggestion {} in server {}.", suggestionId, serverId));
} else {
log.info("Suggestion thread was not created - post target for suggestions does not allow to create threads");
}
} else {
log.info("Suggestion thread was not created - post target did not evaluate to a channel or post target is disabled.");
}
} else {
log.debug("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(SUGGESTION_YES_EMOTE, serverId, message);
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(SUGGESTION_NO_EMOTE, serverId, message);
return CompletableFuture.allOf(firstReaction, secondReaction).thenAccept(aVoid1 -> {
log.debug("Reaction added to message {} for suggestion {}.", message.getId(), newSuggestionId);
log.info("Suggestion thread feature disabled - not creating thread for suggestion {} in server {}.", suggestionId, serverId);
}
return CompletableFuture.completedFuture(null);
}
@Transactional
public CompletableFuture<Void> addVotingPossibility(Long suggestionChannelId, Long suggestionMessageId, String text, Member suggester, Long serverId,
Long newSuggestionId, List<CompletableFuture<Message>> completableFutures, List<ButtonConfigModel> buttonConfigModels,
Boolean useButtons) {
if(!completableFutures.isEmpty()) {
Message message = completableFutures.get(0).join();
if(useButtons) {
configureDecisionButtonPayload(serverId, newSuggestionId, buttonConfigModels.get(0), SuggestionDecision.AGREE);
configureDecisionButtonPayload(serverId, newSuggestionId, buttonConfigModels.get(1), SuggestionDecision.DISAGREE);
configureDecisionButtonPayload(serverId, newSuggestionId, buttonConfigModels.get(2), SuggestionDecision.REMOVE_VOTE);
AServer server = serverManagementService.loadServer(serverId);
componentPayloadManagementService.createButtonPayload(buttonConfigModels.get(0), server);
componentPayloadManagementService.createButtonPayload(buttonConfigModels.get(1), server);
componentPayloadManagementService.createButtonPayload(buttonConfigModels.get(2), server);
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, suggestionChannelId, suggestionMessageId);
});
return CompletableFuture.completedFuture(null);
} else {
log.debug("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(SUGGESTION_YES_EMOTE, serverId, message);
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(SUGGESTION_NO_EMOTE, serverId, message);
return CompletableFuture.allOf(firstReaction, secondReaction).thenAccept(aVoid1 -> {
log.debug("Reaction added to message {} for suggestion {}.", message.getId(), newSuggestionId);
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, suggestionChannelId, suggestionMessageId);
});
}
} else {
return CompletableFuture.completedFuture(null);
}
}
@@ -275,14 +317,23 @@ public class SuggestionServiceBean implements SuggestionService {
Long serverId = suggestion.getServer().getId();
Long channelId = suggestion.getChannel().getId();
Long originalMessageId = suggestion.getMessageId();
boolean buttonsActive = featureModeService.featureModeActive(SuggestionFeatureDefinition.SUGGEST, serverId, SuggestionFeatureMode.SUGGESTION_BUTTONS);
Long agreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.AGREE);
Long disagreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.DISAGREE);
Long suggestionId = suggestion.getSuggestionId().getId();
Long totalVotes = disagreements + agreements;
float agreementPercentage = 0;
float disAgreementPercentage = 0;
if(totalVotes > 0) {
agreementPercentage = (((float) agreements) / totalVotes) * 100;
disAgreementPercentage = 100f - agreementPercentage;
}
SuggestionUpdateModel model = SuggestionUpdateModel
.builder()
.suggestionId(suggestionId)
.state(suggestion.getState())
.serverId(serverId)
.buttonsActive(buttonsActive)
.member(memberExecutingCommand)
.agreeVotes(agreements)
.disAgreeVotes(disagreements)
@@ -290,6 +341,9 @@ public class SuggestionServiceBean implements SuggestionService {
.text(suggestion.getSuggestionText())
.originalChannelId(channelId)
.reason(reason)
.totalVotes(totalVotes)
.agreementPercentage(agreementPercentage)
.disAgreementPercentage(disAgreementPercentage)
.build();
log.info("Updated posted suggestion {} in server {}.", suggestionId, suggestion.getServer().getId());
CompletableFuture<User> memberById = userService.retrieveUserForId(suggestion.getSuggester().getUserReference().getId());

View File

@@ -11,6 +11,10 @@ abstracto.featureModes.suggestionReminder.featureName=suggestion
abstracto.featureModes.suggestionReminder.mode=suggestionReminder
abstracto.featureModes.suggestionReminder.enabled=false
abstracto.featureModes.suggestionThread.featureName=suggestion
abstracto.featureModes.suggestionThread.mode=suggestionThread
abstracto.featureModes.suggestionThread.enabled=false
abstracto.systemConfigs.suggestionReminderDays.name=suggestionReminderDays
abstracto.systemConfigs.suggestionReminderDays.longValue=7

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>suggestion</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -36,7 +36,8 @@ public class SuggestionFeatureConfig implements FeatureConfig {
return Arrays.asList(
SuggestionFeatureMode.SUGGESTION_REMINDER,
SuggestionFeatureMode.SUGGESTION_BUTTONS,
SuggestionFeatureMode.SUGGESTION_AUTO_EVALUATE);
SuggestionFeatureMode.SUGGESTION_AUTO_EVALUATE,
SuggestionFeatureMode.SUGGESTION_THREAD);
}
@Override

View File

@@ -5,7 +5,10 @@ import lombok.Getter;
@Getter
public enum SuggestionFeatureMode implements FeatureMode {
SUGGESTION_REMINDER("suggestionReminder"), SUGGESTION_BUTTONS("suggestionButton"), SUGGESTION_AUTO_EVALUATE("suggestionAutoEvaluate");
SUGGESTION_REMINDER("suggestionReminder"),
SUGGESTION_BUTTONS("suggestionButton"),
SUGGESTION_AUTO_EVALUATE("suggestionAutoEvaluate"),
SUGGESTION_THREAD("suggestionThread");
private final String key;

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.suggestion.model.template;
import lombok.Builder;
import lombok.Getter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import java.time.Instant;
@Getter
@Builder
public class SuggestionThreadModel {
private Long suggestionId;
private User suggester;
private Member member;
private String text;
private Long serverId;
private Instant autoEvaluationTargetDate;
private Boolean autoEvaluationEnabled;
}

View File

@@ -15,6 +15,7 @@ import net.dv8tion.jda.api.entities.User;
public class SuggestionUpdateModel {
private Long suggestionId;
private SuggestionState state;
private Boolean buttonsActive;
private User suggester;
private Member member;
private String text;
@@ -25,6 +26,9 @@ public class SuggestionUpdateModel {
private Long originalMessageId;
private Long agreeVotes;
private Long disAgreeVotes;
private Long totalVotes;
private Float agreementPercentage;
private Float disAgreementPercentage;
public String getOriginalMessageUrl() {
return MessageUtils.buildMessageUrl(serverId, originalChannelId , originalMessageId);

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>voice-channel-context</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>voice-channel-context</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>webservices</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,177 @@
package dev.sheldan.abstracto.webservices.openeweathermap.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.openweathermap.config.OpenWeatherMapConfig;
import dev.sheldan.abstracto.webservices.openweathermap.exception.LocationNotFoundException;
import dev.sheldan.abstracto.webservices.openweathermap.model.*;
import dev.sheldan.abstracto.webservices.openweathermap.service.OpenWeatherMapService;
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;
import java.awt.*;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class OpenWeatherMap extends AbstractConditionableCommand {
private static final String OPEN_WEATHER_MAP_COMMAND = "openWeatherMap";
private static final String SEARCH_QUERY_PARAMETER = "searchQuery";
private static final String OPEN_WEATHER_MAP_RESPONSE_TEMPLATE_KEY = "openWeatherMap_command_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private ChannelService channelService;
@Autowired
private OpenWeatherMapService openWeatherMapService;
@Autowired
private TemplateService templateService;
@Autowired
private ConfigService configService;
@Autowired
private WeatherService weatherService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String parameter = (String) commandContext.getParameters().getParameters().get(0);
MessageToSend message = getMessageToSend(commandContext.getGuild().getIdLong(), parameter);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String query = slashCommandParameterService.getCommandOption(SEARCH_QUERY_PARAMETER, event, String.class);
MessageToSend messageToSend = getMessageToSend(event.getGuild().getIdLong(), query);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(Long serverId, String parameter) {
try {
GeoCodingResult geoCodingResult = openWeatherMapService.searchForLocation(parameter);
if(geoCodingResult.getResults().isEmpty()) {
throw new LocationNotFoundException();
}
String languageKey = configService.getStringValueOrConfigDefault(OpenWeatherMapConfig.OPEN_WEATHER_MAP_LANGUAGE_KEY_SYSTEM_CONFIG_KEY, serverId);
GeoCodingLocation chosenLocation = geoCodingResult.getResults().get(0);
WeatherResult weatherResult = openWeatherMapService.retrieveWeatherForLocation(chosenLocation, languageKey);
WeatherResponseModel.WeatherResponseModelBuilder builder = WeatherResponseModel
.builder()
.description(weatherResult.getWeathers() != null && !weatherResult.getWeathers().isEmpty()
? weatherResult.getWeathers().get(0).getDescription() : null)
.mainWeather(weatherResult.getWeathers() != null && !weatherResult.getWeathers().isEmpty()
? weatherResult.getWeathers().get(0).getMain() : null)
.clouds(weatherResult.getCloudInfo() != null ? weatherResult.getCloudInfo().getAll() : null)
.rain1H(weatherResult.getRainInfo() != null ? weatherResult.getRainInfo().getRain1H() : null)
.rain3H(weatherResult.getRainInfo() != null ? weatherResult.getRainInfo().getRain3H() : null)
.snow1H(weatherResult.getSnowInfo() != null ? weatherResult.getSnowInfo().getSnow1H() : null)
.snow3H(weatherResult.getSnowInfo() != null ? weatherResult.getSnowInfo().getSnow3H() : null)
.visibility(weatherResult.getVisibility())
.locationName(chosenLocation.getName())
.countryKey(chosenLocation.getCountryKey())
.dataCalculationTime(weatherResult.getDayTime() != null ? Instant.ofEpochSecond(weatherResult.getDayTime()) : null);
Color embedColor = null;
WeatherResultMain mainWeather = weatherResult.getMainWeather();
if(mainWeather != null) {
builder.feelsLikeTemperature(mainWeather.getFeelsLikeTemperature())
.temperature(mainWeather.getTemperature())
.maxTemperature(mainWeather.getMaxTemperature())
.minTemperature(mainWeather.getMinTemperature())
.pressure(mainWeather.getPressure())
.seaLevelPressure(mainWeather.getSeaLevelPressure())
.groundLevelPressure(mainWeather.getGroundLevelPressure())
.humidity(mainWeather.getHumidity());
embedColor = weatherService.getColorForTemperature(mainWeather.getFeelsLikeTemperature());
}
builder.embedColor(embedColor);
WeatherResultSystem systemInfo = weatherResult.getSystemInfo();
if(systemInfo != null) {
builder.sunset(systemInfo.getSunset() != null ? Instant.ofEpochSecond(systemInfo.getSunset()) : null);
builder.sunset(systemInfo.getSunrise() != null ? Instant.ofEpochSecond(systemInfo.getSunrise()) : null);
}
builder.locationId(weatherResult.getLocationId());
return templateService.renderEmbedTemplate(OPEN_WEATHER_MAP_RESPONSE_TEMPLATE_KEY, builder.build(), serverId);
} catch (IOException e) {
log.warn("Failed to load weather in server {}", serverId, e);
throw new AbstractoRunTimeException(e);
}
}
@Override
public FeatureDefinition getFeature() {
return WebserviceFeatureDefinition.OPEN_WEATHER_MAP;
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Parameter searchQueryParameter = Parameter
.builder()
.name(SEARCH_QUERY_PARAMETER)
.type(String.class)
.remainder(true)
.templated(true)
.build();
parameters.add(searchQueryParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(WebServicesSlashCommandNames.WEATHER)
.commandName("search")
.build();
return CommandConfiguration.builder()
.name(OPEN_WEATHER_MAP_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
}

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.webservices.openeweathermap.service;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import dev.sheldan.abstracto.webservices.openweathermap.model.GeoCodingLocation;
import dev.sheldan.abstracto.webservices.openweathermap.model.GeoCodingResult;
import dev.sheldan.abstracto.webservices.openweathermap.model.WeatherResult;
import dev.sheldan.abstracto.webservices.openweathermap.service.OpenWeatherMapService;
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.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
@Component
public class OpenWeatherMapServiceBean implements OpenWeatherMapService {
@Autowired
private OkHttpClient okHttpClient;
@Value("${abstracto.feature.webservices.openweatherMap.apiKey}")
private String apiKey;
@Value("${abstracto.feature.webservices.openweathermap.geocodingURL}")
private String geoCodingURL;
@Value("${abstracto.feature.webservices.openweathermap.weatherDataURL}")
private String weatherDataURL;
@Autowired
private Gson gson;
@Override
public GeoCodingResult searchForLocation(String query) throws IOException {
Type geoCodingType = new TypeToken<ArrayList<GeoCodingLocation>>() {}.getType();
Request request = new Request.Builder()
.url(String.format(geoCodingURL, query, 5, apiKey))
.get()
.build();
Response response = okHttpClient.newCall(request).execute();
List<GeoCodingLocation> result = gson.fromJson(response.body().string(), geoCodingType);
return GeoCodingResult
.builder()
.results(result)
.build();
}
@Override
public WeatherResult retrieveWeatherForLocation(GeoCodingLocation location, String languageKey) throws IOException {
Request request = new Request.Builder()
.url(String.format(weatherDataURL, location.getLatitude(), location.getLongitude(), apiKey, languageKey))
.get()
.build();
Response response = okHttpClient.newCall(request).execute();
return gson.fromJson(response.body().string(), WeatherResult.class);
}
}

View File

@@ -0,0 +1,67 @@
package dev.sheldan.abstracto.webservices.openeweathermap.service;
import dev.sheldan.abstracto.webservices.openweathermap.service.WeatherService;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Component;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
@Component
public class WeatherServiceBean implements WeatherService {
private static final Map<Pair<Integer, Integer>, String> TEMPERATURE_COLOR_MAP = new HashMap<>();
// source for colors: https://www.esri.com/arcgis-blog/products/arcgis-pro/mapping/a-meaningful-temperature-palette/
static {
TEMPERATURE_COLOR_MAP.put(Pair.of(-100, -48), "#E4EFFF");
TEMPERATURE_COLOR_MAP.put(Pair.of(-48, -45), "#DCE9FA");
TEMPERATURE_COLOR_MAP.put(Pair.of(-45, -42), "#D3E2F7");
TEMPERATURE_COLOR_MAP.put(Pair.of(-42, -40), "#CBDBF4");
TEMPERATURE_COLOR_MAP.put(Pair.of(-40, -37), "#C0D4ED");
TEMPERATURE_COLOR_MAP.put(Pair.of(-37, -34), "#B8CDEA");
TEMPERATURE_COLOR_MAP.put(Pair.of(-34, -31), "#AFC6E6");
TEMPERATURE_COLOR_MAP.put(Pair.of(-31, -28), "#A7BFE3");
TEMPERATURE_COLOR_MAP.put(Pair.of(-28, -26), "#9CB8DF");
TEMPERATURE_COLOR_MAP.put(Pair.of(-26, -23), "#93B1D6");
TEMPERATURE_COLOR_MAP.put(Pair.of(-23, -20), "#89A4CD");
TEMPERATURE_COLOR_MAP.put(Pair.of(-20, -17), "#7F9BC3");
TEMPERATURE_COLOR_MAP.put(Pair.of(-17, -15), "#7590B9");
TEMPERATURE_COLOR_MAP.put(Pair.of(-15, -12), "#617AA8");
TEMPERATURE_COLOR_MAP.put(Pair.of(-12, -9), "#56719C");
TEMPERATURE_COLOR_MAP.put(Pair.of(-9, -6), "#4D6591");
TEMPERATURE_COLOR_MAP.put(Pair.of(-6, -3), "#415C87");
TEMPERATURE_COLOR_MAP.put(Pair.of(-3, -1), "#39517F");
TEMPERATURE_COLOR_MAP.put(Pair.of(-1, 1), "#2F4577");
TEMPERATURE_COLOR_MAP.put(Pair.of(1, 4), "#26436F");
TEMPERATURE_COLOR_MAP.put(Pair.of(4, 7), "#254F77");
TEMPERATURE_COLOR_MAP.put(Pair.of(7, 10), "#275B80");
TEMPERATURE_COLOR_MAP.put(Pair.of(10, 12), "#27678A");
TEMPERATURE_COLOR_MAP.put(Pair.of(12, 15), "#287593");
TEMPERATURE_COLOR_MAP.put(Pair.of(15, 18), "#438190");
TEMPERATURE_COLOR_MAP.put(Pair.of(18, 21), "#648C89");
TEMPERATURE_COLOR_MAP.put(Pair.of(21, 23), "#879A84");
TEMPERATURE_COLOR_MAP.put(Pair.of(23, 26), "#ABA87D");
TEMPERATURE_COLOR_MAP.put(Pair.of(26, 29), "#C2A875");
TEMPERATURE_COLOR_MAP.put(Pair.of(29, 32), "#C19D61");
TEMPERATURE_COLOR_MAP.put(Pair.of(32, 35), "#C38A53");
TEMPERATURE_COLOR_MAP.put(Pair.of(35, 37), "#BE704C");
TEMPERATURE_COLOR_MAP.put(Pair.of(37, 40), "#AF4D4C");
TEMPERATURE_COLOR_MAP.put(Pair.of(40, 43), "#9F294C");
TEMPERATURE_COLOR_MAP.put(Pair.of(43, 46), "#87203E");
TEMPERATURE_COLOR_MAP.put(Pair.of(46, 48), "#631531");
TEMPERATURE_COLOR_MAP.put(Pair.of(48, 65), "#560C25");
TEMPERATURE_COLOR_MAP.put(Pair.of(65, 100), "#3D0216");
}
@Override
public Color getColorForTemperature(Float temperature) {
return Color.decode(TEMPERATURE_COLOR_MAP.get(TEMPERATURE_COLOR_MAP
.keySet()
.stream()
.filter(pair -> pair.getLeft() < temperature && pair.getRight() > temperature)
.findFirst().orElse(TEMPERATURE_COLOR_MAP.keySet().iterator().next())));
}
}

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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd">
<include file="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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="openWeatherMapFeature" value="(SELECT id FROM feature WHERE key = 'openWeatherMap')"/>
<changeSet author="Sheldan" id="openWeatherMap-command">
<insert tableName="command">
<column name="name" value="openWeatherMap"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${openWeatherMapFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="openWeatherMap_feature-insertion">
<insert tableName="feature">
<column name="key" value="openWeatherMap"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.2.5-webservices/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.7/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.22/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -15,4 +15,16 @@ abstracto.featureModes.videoDetails.mode=videoDetails
abstracto.featureModes.videoDetails.enabled=false
abstracto.featureFlags.threadReader.featureName=threadReader
abstracto.featureFlags.threadReader.enabled=false
abstracto.featureFlags.threadReader.enabled=false
abstracto.feature.webservices.openweathermap.geocodingURL=http://api.openweathermap.org/geo/1.0/direct?q=%s&limit=%s&appid=%s
abstracto.feature.webservices.openweathermap.weatherDataURL=https://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&appid=%s&units=metric&lang=%s
abstracto.feature.webservices.openweatherMap.apiKey=${OPEN_WEATHER_MAP_API_KEY}
abstracto.featureFlags.openWeatherMap.featureName=openWeatherMap
abstracto.featureFlags.openWeatherMap.enabled=false
abstracto.systemConfigs.openWeatherMapLanguageKey.name=openWeatherMapLanguageKey
abstracto.systemConfigs.openWeatherMapLanguageKey.stringValue=en

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>webservices</artifactId>
<version>1.4.19</version>
<version>1.4.23</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,4 +14,11 @@
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -3,4 +3,5 @@ package dev.sheldan.abstracto.webservices.config;
public class WebServicesSlashCommandNames {
public static final String YOUTUBE = "youtube";
public static final String URBAN = "urban";
public static final String WEATHER = "weather";
}

View File

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

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.webservices.openweathermap.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 OpenWeatherMapConfig implements FeatureConfig {
public static final String OPEN_WEATHER_MAP_LANGUAGE_KEY_SYSTEM_CONFIG_KEY = "openWeatherMapLanguageKey";
@Override
public FeatureDefinition getFeature() {
return WebserviceFeatureDefinition.OPEN_WEATHER_MAP;
}
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(OPEN_WEATHER_MAP_LANGUAGE_KEY_SYSTEM_CONFIG_KEY);
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.webservices.openweathermap.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class LocationNotFoundException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "no_weather_location_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class GeoCodingLocation {
@SerializedName("name")
private String name;
@SerializedName("lat")
private Double latitude;
@SerializedName("lon")
private Double longitude;
@SerializedName("country")
private String countryKey;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class GeoCodingResult {
private List<GeoCodingLocation> results;
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.awt.*;
import java.time.Instant;
@Getter
@Setter
@Builder
public class WeatherResponseModel {
private Float temperature;
private Float minTemperature;
private Float maxTemperature;
private String mainWeather;
private String description;
private Float feelsLikeTemperature;
private Integer humidity;
private Integer pressure;
private Integer seaLevelPressure;
private Integer groundLevelPressure;
private Float rain1H;
private Float rain3H;
private Float snow1H;
private Float snow3H;
private Integer clouds;
private Instant sunrise;
private Instant sunset;
private Integer visibility;
private Instant dataCalculationTime;
private String locationName;
private String countryKey;
private Color embedColor;
private Long locationId;
}

View File

@@ -0,0 +1,34 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class WeatherResult {
@SerializedName("coord")
private WeatherResultCoordinates coordinates;
@SerializedName("weather")
private List<WeatherResultWeatherDescription> weathers;
@SerializedName("visibility")
private Integer visibility;
@SerializedName("main")
private WeatherResultMain mainWeather;
@SerializedName("rain")
private WeatherResultRain rainInfo;
@SerializedName("snow")
private WeatherResultSnow snowInfo;
@SerializedName("dt")
private Long dayTime;
@SerializedName("clouds")
private WeatherResultClouds cloudInfo;
@SerializedName("sys")
private WeatherResultSystem systemInfo;
@SerializedName("timezone")
private Long timezoneShift;
@SerializedName("id")
private Long locationId;
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultClouds {
private Integer all;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultCoordinates {
@SerializedName("lon")
private Float longitude;
@SerializedName("lat")
private Float latitude;
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultMain {
@SerializedName("temp")
private Float temperature;
@SerializedName("feels_like")
private Float feelsLikeTemperature;
@SerializedName("temp_min")
private Float minTemperature;
@SerializedName("temp_max")
private Float maxTemperature;
@SerializedName("pressure")
private Integer pressure;
@SerializedName("humidity")
private Integer humidity;
@SerializedName("sea_level")
private Integer seaLevelPressure;
@SerializedName("grnd_level")
private Integer groundLevelPressure;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultRain {
@SerializedName("1h")
private Float rain1H;
@SerializedName("3h")
private Float rain3H;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultSnow {
@SerializedName("1h")
private Float snow1H;
@SerializedName("3h")
private Float snow3H;
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultSystem {
@SerializedName("country")
private String country;
@SerializedName("sunrise")
private Long sunrise;
@SerializedName("sunset")
private Long sunset;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.webservices.openweathermap.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class WeatherResultWeatherDescription {
private Long id;
private String main;
private String description;
private String icon;
}

Some files were not shown because too many files have changed in this diff Show More