mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-08 10:31:01 +00:00
Compare commits
38 Commits
abstracto-
...
abstracto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd70e6ac90 | ||
|
|
29bde70796 | ||
|
|
ecd4feabb2 | ||
|
|
abf60409f1 | ||
|
|
080733957f | ||
|
|
8a41f366ae | ||
|
|
dbf478c44c | ||
|
|
3df688571f | ||
|
|
ca530949c6 | ||
|
|
724930e5a4 | ||
|
|
2875da117a | ||
|
|
f95ba6c28f | ||
|
|
1b2e7654f9 | ||
|
|
54976ed1d4 | ||
|
|
735816f5dd | ||
|
|
a984bdb84e | ||
|
|
21add6585d | ||
|
|
99e72245f3 | ||
|
|
9d184ff560 | ||
|
|
97895f5c56 | ||
|
|
f091559c49 | ||
|
|
fa7730975e | ||
|
|
03d7b9e2e2 | ||
|
|
a0bff12263 | ||
|
|
70e708601e | ||
|
|
f01418d0de | ||
|
|
ad539adb2a | ||
|
|
3c9fec989f | ||
|
|
21db3e3ee5 | ||
|
|
5b1ad2e075 | ||
|
|
63b3f68bdb | ||
|
|
7c1537c4a7 | ||
|
|
9836998087 | ||
|
|
8a1bb4cad8 | ||
|
|
a9dadec8ef | ||
|
|
3eaffaef87 | ||
|
|
b3a943e155 | ||
|
|
06dd4af131 |
2
LICENSE
2
LICENSE
@@ -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
|
||||
|
||||
10
README.md
10
README.md
@@ -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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>assignable-roles-int</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package dev.sheldan.abstracto.experience.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
|
||||
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class ExpLevelUpNotification extends AbstractConditionableCommand {
|
||||
|
||||
private static final String FLAG_PARAMETER = "newValue";
|
||||
private static final String EXP_LEVEL_UP_NOTIFICATION_COMMAND = "expLevelUpNotification";
|
||||
private static final String EXP_LEVEL_UP_NOTIFICATION_RESPONSE = "expLevelUpNotification_response";
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private AUserExperienceService aUserExperienceService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
Boolean newValue = (Boolean) commandContext.getParameters().getParameters().get(0);
|
||||
updateExpLevelNotification(commandContext.getAuthor(), newValue);
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Boolean newValue = slashCommandParameterService.getCommandOption(FLAG_PARAMETER, event, Boolean.class);
|
||||
updateExpLevelNotification(event.getMember(), newValue);
|
||||
return interactionService.replyEmbed(EXP_LEVEL_UP_NOTIFICATION_RESPONSE, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
private void updateExpLevelNotification(Member member, Boolean newValue) {
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
aUserExperienceService.setLevelUpNotification(aUserInAServer, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ExperienceFeatureDefinition.EXPERIENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(FLAG_PARAMETER)
|
||||
.templated(true)
|
||||
.type(Boolean.class)
|
||||
.build();
|
||||
List<Parameter> parameters = Arrays.asList(memberParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE)
|
||||
.commandName(EXP_LEVEL_UP_NOTIFICATION_COMMAND)
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(EXP_LEVEL_UP_NOTIFICATION_COMMAND)
|
||||
.module(ExperienceModuleDefinition.EXPERIENCE)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.causesReaction(true)
|
||||
.supportsEmbedException(true)
|
||||
.templated(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -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,11 +339,13 @@ 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 && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, serverId, ExperienceFeatureMode.LEVEL_UP_NOTIFICATION)) {
|
||||
if(message != null
|
||||
&& aUserExperience.getLevelUpNotification()
|
||||
&& featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, serverId, ExperienceFeatureMode.LEVEL_UP_NOTIFICATION)) {
|
||||
LevelUpNotificationModel model = LevelUpNotificationModel
|
||||
.builder()
|
||||
.memberDisplay(MemberDisplay.fromMember(member))
|
||||
@@ -429,6 +434,13 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
|
||||
userExperience.setExperienceGainDisabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLevelUpNotification(AUserInAServer aUserInAServer, Boolean newValue) {
|
||||
Optional<AUserExperience> aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(aUserInAServer.getUserInServerId());
|
||||
AUserExperience aUserExperience = aUserExperienceOptional.orElseGet(() -> userExperienceManagementService.createUserInServer(aUserInAServer));
|
||||
aUserExperience.setLevelUpNotification(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a {@link MessageToSend messageToSend} to be used as a status message for the ongoing user synchronization
|
||||
* @param current The amount of users which have been synced
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?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="experienceModule" value="(SELECT id FROM module WHERE name = 'experience')"/>
|
||||
<property name="experienceFeature" value="(SELECT id FROM feature WHERE key = 'experience')"/>
|
||||
<changeSet author="Sheldan" id="experience-expLevelUpNotification_command">
|
||||
<insert tableName="command">
|
||||
<column name="name" value="expLevelUpNotification"/>
|
||||
<column name="module_id" valueComputed="${experienceModule}"/>
|
||||
<column name="feature_id" valueComputed="${experienceFeature}"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -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="command.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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="user_experience.xml" relativeToChangelogFile="true"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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="user_experience-add_level_up_notification">
|
||||
<addColumn tableName="user_experience">
|
||||
<column name="level_up_notification" type="BOOLEAN" defaultValueBoolean="true"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -9,4 +9,5 @@
|
||||
<include file="1.0-experience/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.2.15/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.8/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.17/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -60,6 +60,9 @@ public class AUserExperience implements Serializable {
|
||||
@Column(name = "experience_gain_disabled", nullable = false)
|
||||
private Boolean experienceGainDisabled = false;
|
||||
|
||||
@Column(name = "level_up_notification")
|
||||
private Boolean levelUpNotification;
|
||||
|
||||
/**
|
||||
* The {@link AExperienceLevel level} which the user currently has.
|
||||
*/
|
||||
|
||||
@@ -75,6 +75,7 @@ public interface AUserExperienceService {
|
||||
* @param userInAServer The {@link AUserInAServer} to enable experience for
|
||||
*/
|
||||
void enableExperienceForUser(AUserInAServer userInAServer);
|
||||
void setLevelUpNotification(AUserInAServer aUserInAServer, Boolean newValue);
|
||||
|
||||
boolean experienceGainEnabledInChannel(MessageChannel messageChannel);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>modmail</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>modmail</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto</groupId>
|
||||
<artifactId>abstracto-application</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>profanity-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>profanity-filter</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>remind</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>remind</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>repost-detection</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>repost-detection</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>starboard</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>starboard</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>statistic</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>statistic</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>suggestion</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>suggestion</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>utility</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>utility</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>voice-channel-context</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>voice-channel-context</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>webservices</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>webservices</artifactId>
|
||||
<version>1.4.16</version>
|
||||
<version>1.4.25</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>
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user