mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-03-02 23:17:34 +00:00
added scheduling support
added remind command added support for parameters with spaces (they are contained by ") fixed support for remainder parameters added maxlength support for parameters added ability to embed templates, to have a text as well moved properties to a more appropriate position added method do parse a duration
This commit is contained in:
@@ -6,9 +6,9 @@ import dev.sheldan.abstracto.core.service.MessageCache;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.moderation.models.template.listener.MessageDeletedAttachmentLog;
|
||||
import dev.sheldan.abstracto.moderation.models.template.listener.MessageDeletedLog;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.templating.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
@@ -53,13 +53,13 @@ public class MessageDeletedListener extends ListenerAdapter {
|
||||
logModel.setMessage(messageFromCache);
|
||||
String simpleMessageUpdatedMessage = templateService.renderTemplate(MESSAGE_DELETED_TEMPLATE, logModel);
|
||||
postTargetService.sendTextInPostTarget(simpleMessageUpdatedMessage, DELETE_LOG_TARGET, event.getGuild().getIdLong());
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate(MESSAGE_DELETED_TEMPLATE, logModel);
|
||||
postTargetService.sendEmbedInPostTarget(embed, DELETE_LOG_TARGET, event.getGuild().getIdLong());
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_DELETED_TEMPLATE, logModel);
|
||||
postTargetService.sendEmbedInPostTarget(message, DELETE_LOG_TARGET, event.getGuild().getIdLong());
|
||||
for (int i = 0; i < messageFromCache.getAttachmentUrls().size(); i++) {
|
||||
MessageDeletedAttachmentLog log = (MessageDeletedAttachmentLog) contextUtils.fromMessage(messageFromCache, MessageDeletedAttachmentLog.class);
|
||||
log.setImageUrl(messageFromCache.getAttachmentUrls().get(i));
|
||||
log.setCounter(i + 1);
|
||||
MessageEmbed attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_DELETED_ATTACHMENT_TEMPLATE, log);
|
||||
MessageToSend attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_DELETED_ATTACHMENT_TEMPLATE, log);
|
||||
postTargetService.sendEmbedInPostTarget(attachmentEmbed, DELETE_LOG_TARGET, event.getGuild().getIdLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package dev.sheldan.abstracto.moderation.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.MessageTextUpdatedListener;
|
||||
import dev.sheldan.abstracto.core.models.CachedMessage;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.service.MessageCache;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.moderation.models.template.listener.MessageEditedLog;
|
||||
import dev.sheldan.abstracto.templating.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -45,8 +45,8 @@ public class MessageEditedListener implements MessageTextUpdatedListener {
|
||||
.member(messageAfter.getMember()).build();
|
||||
String simpleMessageUpdatedMessage = templateService.renderTemplate(MESSAGE_EDITED_TEMPLATE, log);
|
||||
postTargetService.sendTextInPostTarget(simpleMessageUpdatedMessage, EDIT_LOG_TARGET, messageAfter.getGuild().getIdLong());
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log);
|
||||
postTargetService.sendEmbedInPostTarget(embed, EDIT_LOG_TARGET, messageBefore.getServerId());
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, log);
|
||||
postTargetService.sendEmbedInPostTarget(message, EDIT_LOG_TARGET, messageBefore.getServerId());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.moderation.service;
|
||||
import dev.sheldan.abstracto.core.models.ServerContext;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUser;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.moderation.models.template.WarnLog;
|
||||
import dev.sheldan.abstracto.moderation.models.template.WarnNotification;
|
||||
import dev.sheldan.abstracto.moderation.models.Warning;
|
||||
@@ -17,7 +18,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -86,7 +86,7 @@ public class WarnServiceBean implements WarnService {
|
||||
private void sendWarnLog(ServerContext warnLogModel) {
|
||||
String warnLogMessage = templateService.renderContextAwareTemplate(WARN_LOG_TEMPLATE, warnLogModel);
|
||||
postTargetService.sendTextInPostTarget(warnLogMessage, WARN_LOG_TARGET, warnLogModel.getServer().getId());
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate("warn_log", warnLogModel);
|
||||
postTargetService.sendEmbedInPostTarget(embed, WARN_LOG_TARGET, warnLogModel.getServer().getId());
|
||||
MessageToSend message = templateService.renderEmbedTemplate("warn_log", warnLogModel);
|
||||
postTargetService.sendEmbedInPostTarget(message, WARN_LOG_TARGET, warnLogModel.getServer().getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
<artifactId>utility-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
package dev.sheldan.abstracto.utility.command.remind;
|
||||
|
||||
import dev.sheldan.abstracto.command.Command;
|
||||
import dev.sheldan.abstracto.command.HelpInfo;
|
||||
import dev.sheldan.abstracto.command.execution.*;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.utility.Utility;
|
||||
import dev.sheldan.abstracto.utility.models.template.ReminderModel;
|
||||
import dev.sheldan.abstracto.utility.service.ReminderService;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class Remind implements Command {
|
||||
|
||||
@Autowired
|
||||
private ReminderService remindService;
|
||||
|
||||
@Override
|
||||
public Result execute(CommandContext commandContext) {
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
Duration remindTime = (Duration) parameters.get(0);
|
||||
String text = (String) parameters.get(1);
|
||||
AUserInAServer aUserInAServer = commandContext.getUserInitiatedContext().getAUserInAServer();
|
||||
ReminderModel remindModel = (ReminderModel) ContextConverter.fromCommandContext(commandContext, ReminderModel.class);
|
||||
remindModel.setMessage(commandContext.getMessage());
|
||||
remindModel.setRemindText(text);
|
||||
remindService.createReminderInForUser(aUserInAServer, text, remindTime, remindModel);
|
||||
return Result.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
parameters.add(Parameter.builder().name("remindTime").type(Duration.class).build());
|
||||
parameters.add(Parameter.builder().name("remindText").type(String.class).maxLength(MessageEmbed.TEXT_MAX_LENGTH).remainder(true).build());
|
||||
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
|
||||
return CommandConfiguration.builder()
|
||||
.name("remind")
|
||||
.module(Utility.UTILITY)
|
||||
.templated(true)
|
||||
.causesReaction(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package dev.sheldan.abstracto.utility.jobs;
|
||||
|
||||
import dev.sheldan.abstracto.utility.service.ReminderService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.PersistJobDataAfterExecution;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@DisallowConcurrentExecution
|
||||
@Component
|
||||
@PersistJobDataAfterExecution
|
||||
public class ReminderJob extends QuartzJobBean {
|
||||
|
||||
private Long reminderId;
|
||||
|
||||
@Autowired
|
||||
private ReminderService reminderService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
|
||||
reminderService.executeReminder(reminderId);
|
||||
log.info("executing reminder job for reminder {}", reminderId);
|
||||
}
|
||||
|
||||
public Long getReminderId() {
|
||||
return reminderId;
|
||||
}
|
||||
|
||||
public void setReminderId(Long reminderId) {
|
||||
this.reminderId = reminderId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package dev.sheldan.abstracto.utility.repository;
|
||||
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ReminderRepository extends JpaRepository<Reminder, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package dev.sheldan.abstracto.utility.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.service.Bot;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerService;
|
||||
import dev.sheldan.abstracto.templating.TemplateService;
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
import dev.sheldan.abstracto.utility.models.template.ExecutedReminderModel;
|
||||
import dev.sheldan.abstracto.utility.models.template.ReminderModel;
|
||||
import dev.sheldan.abstracto.utility.service.management.ReminderManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RemindServiceBean implements ReminderService {
|
||||
|
||||
public static final String REMINDER_EMBED_KEY = "remind_response";
|
||||
@Autowired
|
||||
private ReminderManagementService reminderManagementService;
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Autowired
|
||||
private Bot bot;
|
||||
|
||||
@Autowired
|
||||
private ReminderService self;
|
||||
|
||||
@Override
|
||||
public void createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, ReminderModel reminderModel) {
|
||||
AChannel channel = channelManagementService.loadChannel(reminderModel.getChannel().getId());
|
||||
AServerAChannelAUser aServerAChannelAUser = AServerAChannelAUser
|
||||
.builder()
|
||||
.user(user.getUserReference())
|
||||
.aUserInAServer(user)
|
||||
.guild(user.getServerReference())
|
||||
.channel(channel)
|
||||
.build();
|
||||
Instant remindAt = Instant.now().plusNanos(remindIn.toNanos());
|
||||
Reminder reminder = reminderManagementService.createReminder(aServerAChannelAUser, remindText, remindAt, reminderModel.getMessage().getIdLong());
|
||||
reminderModel.setReminder(reminder);
|
||||
MessageToSend message = templateService.renderEmbedTemplate(REMINDER_EMBED_KEY, reminderModel);
|
||||
reminderModel.getTextChannel().sendMessage(message.getMessage()).embed(message.getEmbed()).queue();
|
||||
|
||||
if(remindIn.getSeconds() < 60) {
|
||||
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
scheduler.schedule(() -> {
|
||||
self.executeReminder(reminder.getId());
|
||||
}, remindIn.toNanos(), TimeUnit.NANOSECONDS);
|
||||
} else {
|
||||
JobDataMap parameters = new JobDataMap();
|
||||
parameters.putAsString("reminderId", reminder.getId());
|
||||
schedulerService.executeJobWithParametersOnce("reminderJob", "utility", parameters, Date.from(reminder.getTargetDate()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void executeReminder(Long reminderId) {
|
||||
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId);
|
||||
AServer server = reminderToRemindFor.getServer();
|
||||
AChannel channel = reminderToRemindFor.getChannel();
|
||||
AUser userReference = reminderToRemindFor.getToBeReminded().getUserReference();
|
||||
Member memberInServer = bot.getMemberInServer(server.getId(), userReference.getId());
|
||||
ExecutedReminderModel build = ExecutedReminderModel
|
||||
.builder()
|
||||
.reminder(reminderToRemindFor)
|
||||
.member(memberInServer)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate("remind_reminder", build);
|
||||
// todo, if channel does not exist anymore
|
||||
TextChannel channelToAnswerIn = bot.getTextChannelFromServer(server.getId(), channel.getId());
|
||||
channelToAnswerIn.sendMessage(messageToSend.getMessage()).embed(messageToSend.getEmbed()).queue();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.utility.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.management.EmoteManagementService;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.service.Bot;
|
||||
import dev.sheldan.abstracto.core.service.MessageService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
@@ -55,12 +56,12 @@ public class SuggestionServiceBean implements SuggestionService {
|
||||
Suggestion suggestion = suggestionManagementService.createSuggestion(member, text);
|
||||
suggestionLog.setSuggestion(suggestion);
|
||||
suggestionLog.setText(text);
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
|
||||
long guildId = member.getGuild().getIdLong();
|
||||
JDA instance = botService.getInstance();
|
||||
Guild guildById = instance.getGuildById(guildId);
|
||||
if(guildById != null) {
|
||||
postTargetService.sendEmbedInPostTarget(embed, SUGGESTIONS_TARGET, guildId).thenAccept(message -> {
|
||||
postTargetService.sendEmbedInPostTarget(messageToSend, SUGGESTIONS_TARGET, guildId).thenAccept(message -> {
|
||||
messageService.addReactionToMessage(SUGGESTION_YES_EMOTE, guildId, message);
|
||||
messageService.addReactionToMessage(SUGGESTION_NO_EMOTE, guildId, message);
|
||||
suggestionManagementService.setPostedMessage(suggestion, message);
|
||||
@@ -110,8 +111,8 @@ public class SuggestionServiceBean implements SuggestionService {
|
||||
MessageEmbed suggestionEmbed = embedOptional.get();
|
||||
suggestionLog.setReason(text);
|
||||
suggestionLog.setText(suggestionEmbed.getDescription());
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
|
||||
postTargetService.sendEmbedInPostTarget(embed, SUGGESTIONS_TARGET, suggestionLog.getServer().getId());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
|
||||
postTargetService.sendEmbedInPostTarget(messageToSend, SUGGESTIONS_TARGET, suggestionLog.getServer().getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package dev.sheldan.abstracto.utility.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
import dev.sheldan.abstracto.utility.repository.ReminderRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Component
|
||||
public class ReminderManagementServiceBean implements ReminderManagementService {
|
||||
|
||||
@Autowired
|
||||
private ReminderRepository reminderRepository;
|
||||
|
||||
@Override
|
||||
public Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId) {
|
||||
Reminder reminder = Reminder.builder()
|
||||
.channel(userToBeReminded.getChannel())
|
||||
.server(userToBeReminded.getGuild())
|
||||
.toBeReminded(userToBeReminded.getAUserInAServer())
|
||||
.reminded(false)
|
||||
.text(text)
|
||||
.reminderDate(Instant.now())
|
||||
.targetDate(timeToBeRemindedAt)
|
||||
.messageId(messageId)
|
||||
.build();
|
||||
|
||||
reminderRepository.save(reminder);
|
||||
return reminder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reminder loadReminder(Long reminderId) {
|
||||
return reminderRepository.getOne(reminderId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Reminds you in the given period of the given text.
|
||||
@@ -0,0 +1 @@
|
||||
More detailed help to come
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "${member.effectiveName}",
|
||||
"avatar": "${member.user.effectiveAvatarUrl}"
|
||||
},
|
||||
"color" : {
|
||||
"r": 200,
|
||||
"g": 0,
|
||||
"b": 255
|
||||
},
|
||||
"description": "You wanted to be reminded of: '${reminder.text}'. Original message was [here](${messageUrl}).",
|
||||
"additionalMessage": "${member.asMention}"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "${member.effectiveName}",
|
||||
"avatar": "${member.user.effectiveAvatarUrl}"
|
||||
},
|
||||
"color" : {
|
||||
"r": 200,
|
||||
"g": 0,
|
||||
"b": 255
|
||||
},
|
||||
"description": "Scheduled reminder ${reminder.id} to remind you of [this](${message.jumpUrl})",
|
||||
"additionalMessage": "${member.asMention}"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
remind <duration> <text>
|
||||
@@ -1,2 +1,8 @@
|
||||
abstracto.postTargets.utility=suggestions
|
||||
abstracto.emoteNames.suggestion=SUGGESTION_YES,SUGGESTION_NO
|
||||
abstracto.emoteNames.suggestion=SUGGESTION_YES,SUGGESTION_NO
|
||||
|
||||
abstracto.scheduling.jobs.reminderJob.name=reminderJob
|
||||
abstracto.scheduling.jobs.reminderJob.group=utility
|
||||
abstracto.scheduling.jobs.reminderJob.clazz=dev.sheldan.abstracto.utility.jobs.ReminderJob
|
||||
abstracto.scheduling.jobs.reminderJob.standAlone=false
|
||||
abstracto.scheduling.jobs.reminderJob.active=true
|
||||
@@ -0,0 +1,54 @@
|
||||
package dev.sheldan.abstracto.utility.models;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity
|
||||
@Table(name="reminder")
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter @Setter
|
||||
public class Reminder {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Getter
|
||||
private Long id;
|
||||
|
||||
@Getter
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "remindedUser")
|
||||
private AUserInAServer toBeReminded;
|
||||
|
||||
@Getter
|
||||
private Long messageId;
|
||||
|
||||
@Getter
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "channelId")
|
||||
private AChannel channel;
|
||||
|
||||
@Getter
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "serverId")
|
||||
private AServer server;
|
||||
|
||||
@Getter
|
||||
private Instant reminderDate;
|
||||
|
||||
@Getter
|
||||
private Instant targetDate;
|
||||
|
||||
@Getter
|
||||
private String text;
|
||||
|
||||
@Getter
|
||||
private boolean reminded;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package dev.sheldan.abstracto.utility.models.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerContext;
|
||||
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
public class ExecutedReminderModel extends ServerContext {
|
||||
private Reminder reminder;
|
||||
private Member member;
|
||||
|
||||
public String getMessageUrl() {
|
||||
return MessageUtils.buildMessageUrl(this.reminder.getServer().getId() ,this.reminder.getChannel().getId(), this.reminder.getMessageId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.sheldan.abstracto.utility.models.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.UserInitiatedServerContext;
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
public class ReminderModel extends UserInitiatedServerContext {
|
||||
private String remindText;
|
||||
private Reminder reminder;
|
||||
private Message message;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package dev.sheldan.abstracto.utility.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.utility.models.template.ReminderModel;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public interface ReminderService {
|
||||
void createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, ReminderModel reminderModel);
|
||||
void executeReminder(Long reminderId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package dev.sheldan.abstracto.utility.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
|
||||
import dev.sheldan.abstracto.utility.models.Reminder;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public interface ReminderManagementService {
|
||||
Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId);
|
||||
Reminder loadReminder(Long reminderId);
|
||||
}
|
||||
@@ -11,4 +11,5 @@ public class Parameter {
|
||||
private String description;
|
||||
private boolean optional;
|
||||
private boolean remainder;
|
||||
private Integer maxLength;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,38 @@
|
||||
package dev.sheldan.abstracto.command.meta;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Getter @Setter
|
||||
@Getter
|
||||
public class UnParsedCommandParameter {
|
||||
|
||||
private static Pattern SPLIT_REGEX = Pattern.compile("\"([^\"]*)\"|(\\S+)");
|
||||
|
||||
public UnParsedCommandParameter(String parameters) {
|
||||
this.parameters = new ArrayList<>();
|
||||
Matcher m = SPLIT_REGEX.matcher(parameters);
|
||||
boolean skippedCommand = false;
|
||||
while (m.find()) {
|
||||
if(!skippedCommand) {
|
||||
skippedCommand = true;
|
||||
continue;
|
||||
}
|
||||
if (m.group(1) != null) {
|
||||
String group = m.group(1);
|
||||
if(!group.equals("")) {
|
||||
this.parameters.add(group);
|
||||
}
|
||||
} else {
|
||||
String group = m.group(2);
|
||||
if(!group.equals("")) {
|
||||
this.parameters.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> parameters;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import dev.sheldan.abstracto.command.PostCommandExecution;
|
||||
import dev.sheldan.abstracto.command.execution.*;
|
||||
import dev.sheldan.abstracto.command.meta.UnParsedCommandParameter;
|
||||
import dev.sheldan.abstracto.commands.management.exception.IncorrectParameterException;
|
||||
import dev.sheldan.abstracto.commands.management.exception.ParameterTooLongException;
|
||||
import dev.sheldan.abstracto.core.Constants;
|
||||
import dev.sheldan.abstracto.core.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.management.UserManagementService;
|
||||
@@ -12,6 +14,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.UserInitiatedServerContext;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.utils.ParseUtils;
|
||||
import net.dv8tion.jda.api.entities.Emote;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
@@ -24,6 +27,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -64,14 +68,11 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
.userInitiatedContext(buildTemplateParameter(event));
|
||||
Command foundCommand = null;
|
||||
try {
|
||||
List<String> parameters = Arrays.asList(event.getMessage().getContentStripped().split(" "));
|
||||
parameters = parameters.stream().filter(s -> {
|
||||
return !s.equals("");
|
||||
}).collect(Collectors.toList());
|
||||
UnParsedCommandParameter unparsedParameter = new UnParsedCommandParameter();
|
||||
unparsedParameter.setParameters(parameters.subList(1, parameters.size()));
|
||||
String withoutPrefix = parameters.get(0).substring(1);
|
||||
foundCommand = commandManager.findCommandByParameters(withoutPrefix, unparsedParameter);
|
||||
String contentStripped = event.getMessage().getContentStripped();
|
||||
List<String> parameters = Arrays.asList(contentStripped.split(" "));
|
||||
UnParsedCommandParameter unparsedParameter = new UnParsedCommandParameter(contentStripped);
|
||||
String commandName = parameters.get(0).substring(1);
|
||||
foundCommand = commandManager.findCommandByParameters(commandName, unparsedParameter);
|
||||
Parameters parsedParameters = getParsedParameters(unparsedParameter, foundCommand, event.getMessage());
|
||||
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
|
||||
Result result = foundCommand.execute(commandContext);
|
||||
@@ -106,12 +107,24 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
|
||||
public Parameters getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){
|
||||
List<Object> parsedParameters = new ArrayList<>();
|
||||
if(command.getConfiguration().getParameters().size() == 0) {
|
||||
return Parameters.builder().parameters(parsedParameters).build();
|
||||
}
|
||||
Iterator<TextChannel> channelIterator = message.getMentionedChannels().iterator();
|
||||
Iterator<Emote> emoteIterator = message.getEmotes().iterator();
|
||||
Iterator<Member> memberIterator = message.getMentionedMembers().iterator();
|
||||
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
|
||||
Parameter param = command.getConfiguration().getParameters().get(i);
|
||||
Parameter param = command.getConfiguration().getParameters().get(0);
|
||||
boolean reminderActive = false;
|
||||
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
|
||||
if(i < command.getConfiguration().getParameters().size() && !param.isRemainder()) {
|
||||
param = command.getConfiguration().getParameters().get(i);
|
||||
} else {
|
||||
reminderActive = true;
|
||||
}
|
||||
String value = unParsedCommandParameter.getParameters().get(i);
|
||||
if(param.getMaxLength() != null && (value.length() + Constants.PARAMETER_LIMIT) > param.getMaxLength()) {
|
||||
throw new ParameterTooLongException("The passed parameter was too long.", command, param.getName(), value.length(), param.getMaxLength());
|
||||
}
|
||||
try {
|
||||
if(param.getType().equals(Integer.class)){
|
||||
parsedParameters.add(Integer.parseInt(value));
|
||||
@@ -126,13 +139,20 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
||||
} else if(param.getType().equals(Emote.class)) {
|
||||
// TODO maybe rework, this fails if two emotes are needed, and the second one is an emote, the first one a default one
|
||||
// the second one shadows the first one, and there are too little parameters to go of
|
||||
if(emoteIterator.hasNext()) {
|
||||
if (emoteIterator.hasNext()) {
|
||||
parsedParameters.add(emoteIterator.next());
|
||||
} else {
|
||||
parsedParameters.add(value);
|
||||
}
|
||||
} else if (param.getType().equals(Duration.class)) {
|
||||
parsedParameters.add(ParseUtils.parseDuration(value));
|
||||
} else {
|
||||
parsedParameters.add(value);
|
||||
if(!reminderActive) {
|
||||
parsedParameters.add(value);
|
||||
} else {
|
||||
int lastIndex = parsedParameters.size() - 1;
|
||||
parsedParameters.set(lastIndex, parsedParameters.get(lastIndex) + " " + value);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IncorrectParameterException("The passed parameters did not have the correct type.", command, param.getType(), param.getName());
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package dev.sheldan.abstracto.commands.management.exception;
|
||||
|
||||
import dev.sheldan.abstracto.command.Command;
|
||||
import dev.sheldan.abstracto.command.TemplatedException;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ParameterTooLongException extends RuntimeException implements TemplatedException {
|
||||
|
||||
|
||||
private Command command;
|
||||
private String parameterName;
|
||||
private Integer actualLength;
|
||||
private Integer maximumLength;
|
||||
|
||||
public ParameterTooLongException(String s, Command command, String parameterName, Integer actualLength, Integer maximumLength) {
|
||||
super(s);
|
||||
this.command = command;
|
||||
this.parameterName = parameterName;
|
||||
this.actualLength = actualLength;
|
||||
this.maximumLength = maximumLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "parameter_too_long";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
HashMap<String, Object> model = new HashMap<>();
|
||||
model.put("parameterName", parameterName);
|
||||
model.put("actualLength", actualLength);
|
||||
model.put("maximumLength", maximumLength);
|
||||
model.put("command", command);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
The parameter ${parameterName} had a too large value: ${actualLength}. The maximum is: ${maximumLength}
|
||||
@@ -28,10 +28,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.exception.ConfigurationException;
|
||||
import dev.sheldan.abstracto.core.management.PostTargetManagement;
|
||||
import dev.sheldan.abstracto.core.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.models.database.PostTarget;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
@@ -83,6 +84,18 @@ public class PostTargetServiceBean implements PostTargetService {
|
||||
return this.sendEmbedInPostTarget(embed, postTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, String postTargetName, Long serverId) {
|
||||
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
|
||||
return this.sendEmbedInPostTarget(message, postTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, PostTarget target) {
|
||||
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
|
||||
return textChannelForPostTarget.sendMessage(message.getMessage()).embed(message.getEmbed()).submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validPostTarget(String name) {
|
||||
List<String> possiblePostTargets = dynamicKeyLoader.getPostTargetsAsList();
|
||||
|
||||
@@ -10,10 +10,10 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.listener.MessageEmbeddedModel;
|
||||
import dev.sheldan.abstracto.core.service.Bot;
|
||||
import dev.sheldan.abstracto.core.service.MessageCache;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.templating.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -91,8 +91,8 @@ public class MessageEmbedListener extends ListenerAdapter {
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void createEmbedAndPostEmbed(@Nonnull GuildMessageReceivedEvent event, CachedMessage message) {
|
||||
MessageEmbeddedModel messageEmbeddedModel = buildTemplateParameter(event, message);
|
||||
MessageEmbed embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel);
|
||||
event.getChannel().sendMessage(embed).queue();
|
||||
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel);
|
||||
event.getChannel().sendMessage(embed.getMessage()).embed(embed.getEmbed()).queue();
|
||||
}
|
||||
|
||||
private MessageEmbeddedModel buildTemplateParameter(GuildMessageReceivedEvent event, CachedMessage embeddedMessage) {
|
||||
|
||||
@@ -2,19 +2,19 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/abstracto
|
||||
spring.datasource.username= abstracto
|
||||
spring.datasource.password= abstracto
|
||||
spring.jpa.hibernate.default_schema=abstracto
|
||||
spring.jpa.show-sql = true
|
||||
spring.jpa.show-sql = false
|
||||
|
||||
spring.jpa.hibernate.ddl-auto = update
|
||||
spring.jpa.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
|
||||
log4j.logger.org.hibernate.SQL=debug
|
||||
log4j.logger.org.hibernate.SQL=info
|
||||
log4j.logger.org.hibernate.type.descriptor.sql=trace
|
||||
log4j.logger.org.hibernate.type=trace
|
||||
log4j.logger.dev.sheldan=debug
|
||||
log4j.logger.dev.sheldan=info
|
||||
|
||||
spring.cache.cache-names=messages
|
||||
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
|
||||
|
||||
abstracto.startup.synchronize=true
|
||||
|
||||
logging.level.dev.sheldan=DEBUG
|
||||
abstracto.parameter.lowerBound=50
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.core;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
public class Constants {
|
||||
|
||||
@Value("${abstracto.parameter.lowerBound}")
|
||||
public static int PARAMETER_LIMIT = 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package dev.sheldan.abstracto.core.models;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class AServerAChannelAUser {
|
||||
private AServer guild;
|
||||
private AChannel channel;
|
||||
private AUserInAServer aUserInAServer;
|
||||
private AUser user;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.core.models.embed;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class MessageToSend {
|
||||
private MessageEmbed embed;
|
||||
private String message;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.PostTarget;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
@@ -12,6 +13,8 @@ public interface PostTargetService {
|
||||
CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, PostTarget target);
|
||||
CompletableFuture<Message> sendTextInPostTarget(String text, String postTargetName, Long serverId);
|
||||
CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, String postTargetName, Long serverId);
|
||||
CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, String postTargetName, Long serverId);
|
||||
CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, PostTarget target);
|
||||
boolean validPostTarget(String name);
|
||||
List<String> getAvailablePostTargets();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.sheldan.abstracto.core.utils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ParseUtils {
|
||||
|
||||
private static Pattern messageRegex = Pattern.compile("(?<number>\\d+)(?<unit>[ywdhms]+)");
|
||||
|
||||
public static Duration parseDuration(String textToParseFrom) {
|
||||
Matcher matcher = ParseUtils.messageRegex.matcher(textToParseFrom);
|
||||
Duration start = Duration.ZERO;
|
||||
while(matcher.find()) {
|
||||
String unit = matcher.group("unit");
|
||||
String number = matcher.group("number");
|
||||
long parsed = Long.parseLong(number);
|
||||
switch (unit) {
|
||||
case "w": start = start.plus(Duration.ofDays(parsed * 7)); break;
|
||||
case "d": start = start.plus(Duration.ofDays(parsed)); break;
|
||||
case "h": start = start.plus(Duration.ofHours(parsed)); break;
|
||||
case "m": start = start.plus(Duration.ofMinutes(parsed)); break;
|
||||
case "s": start = start.plus(Duration.ofSeconds(parsed)); break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package dev.sheldan.abstracto.core.utils;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ParseUtilsTest {
|
||||
|
||||
@Test
|
||||
public void oneDay() {
|
||||
Duration duration = ParseUtils.parseDuration("1d");
|
||||
assertEquals(Duration.ofDays(1), duration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoWeeks() {
|
||||
Duration duration = ParseUtils.parseDuration("2w");
|
||||
assertEquals(Duration.ofDays(14), duration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aDayWithMinutes() {
|
||||
Duration duration = ParseUtils.parseDuration("1d3m");
|
||||
assertEquals(Duration.ofDays(1).plus(Duration.ofMinutes(3)), duration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allTimeFormats() {
|
||||
Duration duration = ParseUtils.parseDuration("2w3d4h2m1s");
|
||||
assertEquals(Duration.ofDays(17).plus(Duration.ofHours(4)).plus(Duration.ofMinutes(2)).plus(Duration.ofSeconds(1)), duration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overFlowingTimeFormats() {
|
||||
Duration duration = ParseUtils.parseDuration("70s");
|
||||
assertEquals(Duration.ofMinutes(1).plus(Duration.ofSeconds(10)), duration);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-impl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<module>executable</module>
|
||||
<module>templating</module>
|
||||
<module>abstracto-modules</module>
|
||||
<module>scheduling</module>
|
||||
</modules>
|
||||
|
||||
|
||||
@@ -64,6 +65,21 @@
|
||||
<version>2.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>3.3.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -79,6 +95,18 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
|
||||
21
abstracto-application/scheduling/pom.xml
Normal file
21
abstracto-application/scheduling/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto</groupId>
|
||||
<artifactId>abstracto-application</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>scheduling-int</module>
|
||||
<module>scheduling-impl</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
25
abstracto-application/scheduling/scheduling-impl/pom.xml
Normal file
25
abstracto-application/scheduling/scheduling-impl/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>scheduling-impl</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package dev.sheldan.abstracto.scheduling.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "abstracto.scheduling")
|
||||
public class JobConfigLoader {
|
||||
private HashMap<String, SchedulerJobProperties> jobs = new HashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.sheldan.abstracto.scheduling.config;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.factory.SchedulerJobFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Properties;
|
||||
|
||||
@Configuration
|
||||
public class SchedulerConfig {
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private QuartzProperties quartzProperties;
|
||||
|
||||
@Bean
|
||||
public SchedulerFactoryBean schedulerFactoryBean() {
|
||||
SchedulerJobFactory jobFactory = new SchedulerJobFactory();
|
||||
jobFactory.setApplicationContext(applicationContext);
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(quartzProperties.getProperties());
|
||||
|
||||
SchedulerFactoryBean factory = new SchedulerFactoryBean();
|
||||
factory.setOverwriteExistingJobs(true);
|
||||
factory.setDataSource(dataSource);
|
||||
factory.setQuartzProperties(properties);
|
||||
factory.setJobFactory(jobFactory);
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.sheldan.abstracto.scheduling.config;
|
||||
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SchedulerJobProperties {
|
||||
private String name;
|
||||
private String group;
|
||||
private String cronExpression;
|
||||
private String clazz;
|
||||
private Boolean active;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package dev.sheldan.abstracto.scheduling.factory;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.*;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.quartz.SimpleScheduleBuilder.*;
|
||||
import static org.quartz.CronScheduleBuilder.*;
|
||||
import static org.quartz.TriggerBuilder.*;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class QuartzConfigFactory {
|
||||
|
||||
public JobDetail createJob(Class<? extends QuartzJobBean> jobClass, boolean isDurable,
|
||||
ApplicationContext context, String jobName, String jobGroup, boolean requestsRecovery) {
|
||||
|
||||
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
|
||||
factoryBean.setJobClass(jobClass);
|
||||
factoryBean.setDurability(isDurable);
|
||||
factoryBean.setApplicationContext(context);
|
||||
factoryBean.setRequestsRecovery(requestsRecovery);
|
||||
factoryBean.setName(jobName);
|
||||
factoryBean.setGroup(jobGroup);
|
||||
|
||||
JobDataMap jobDataMap = new JobDataMap();
|
||||
jobDataMap.put(jobName + jobGroup, jobClass.getName());
|
||||
factoryBean.setJobDataMap(jobDataMap);
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
return factoryBean.getObject();
|
||||
}
|
||||
|
||||
public CronTrigger createBasicCronTrigger(Date startTime, String cronExpression) {
|
||||
return newTrigger()
|
||||
.withSchedule(cronSchedule(cronExpression).inTimeZone(TimeZone.getTimeZone("UTC")).withMisfireHandlingInstructionIgnoreMisfires())
|
||||
.startAt(startTime)
|
||||
.build();
|
||||
}
|
||||
|
||||
public Trigger createSimpleOnceOnlyTrigger(String triggerName, Date startTime) {
|
||||
return newTrigger()
|
||||
.startAt(startTime)
|
||||
.withSchedule(simpleSchedule())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Trigger createOnceOnlyTriggerForJob(String jobName, String jobGroup, Date startTime, JobDataMap jobDataMap) {
|
||||
return newTrigger()
|
||||
.startAt(startTime)
|
||||
.forJob(jobName, jobGroup)
|
||||
.withSchedule(simpleSchedule())
|
||||
.usingJobData(jobDataMap)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.sheldan.abstracto.scheduling.factory;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.config.SchedulerJobProperties;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerJob;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SchedulerJobConverter {
|
||||
|
||||
public SchedulerJob fromJobProperties(SchedulerJobProperties properties) {
|
||||
return SchedulerJob
|
||||
.builder()
|
||||
.name(properties.getName())
|
||||
.groupName(properties.getGroup())
|
||||
.active(properties.getActive())
|
||||
.cronExpression(properties.getCronExpression())
|
||||
.clazz(properties.getClazz())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package dev.sheldan.abstracto.scheduling.factory;
|
||||
|
||||
import org.quartz.spi.TriggerFiredBundle;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
|
||||
|
||||
public class SchedulerJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
|
||||
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(final ApplicationContext context) {
|
||||
beanFactory = context.getAutowireCapableBeanFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
|
||||
final Object job = super.createJobInstance(bundle);
|
||||
beanFactory.autowireBean(job);
|
||||
return job;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.scheduling.repository;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerJob;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SchedulerJobRepository extends JpaRepository<SchedulerJob, Long> {
|
||||
boolean existsByName(String name);
|
||||
SchedulerJob findByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.scheduling.service;
|
||||
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.spi.InstanceIdGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class IdGenerationService implements InstanceIdGenerator {
|
||||
|
||||
@Override
|
||||
public String generateInstanceId() throws SchedulerException {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package dev.sheldan.abstracto.scheduling.service;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.factory.QuartzConfigFactory;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerJob;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerService;
|
||||
import dev.sheldan.abstracto.scheduling.service.management.SchedulerJobManagementServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SchedulerServiceBean implements SchedulerService {
|
||||
|
||||
@Autowired
|
||||
private SchedulerFactoryBean schedulerFactoryBean;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private QuartzConfigFactory scheduleCreator;
|
||||
|
||||
@Autowired
|
||||
private SchedulerJobManagementServiceBean schedulerJobManagementServiceBean;
|
||||
|
||||
@Override
|
||||
public void startScheduledJobs() {
|
||||
List<SchedulerJob> jobs = schedulerJobManagementServiceBean.findAll();
|
||||
Scheduler scheduler = schedulerFactoryBean.getScheduler();
|
||||
jobs.forEach(schedulerJob -> {
|
||||
if(schedulerJob.isActive()) {
|
||||
scheduleNewJob(scheduler, schedulerJob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isRecurringJob(SchedulerJob job) {
|
||||
return job.getCronExpression() != null && CronExpression.isValidExpression(job.getCronExpression());
|
||||
}
|
||||
|
||||
private void scheduleNewJob(Scheduler scheduler, SchedulerJob schedulerJob) {
|
||||
if(!schedulerJob.isActive()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JobDetail jobDetail = JobBuilder.newJob((Class<? extends QuartzJobBean>) Class.forName(schedulerJob.getClazz()))
|
||||
.withIdentity(schedulerJob.getName(), schedulerJob.getGroupName()).build();
|
||||
if (!scheduler.checkExists(jobDetail.getKey())) {
|
||||
// if its only started by triggers, it needs to be durable
|
||||
boolean recurringJob = isRecurringJob(schedulerJob);
|
||||
jobDetail = scheduleCreator.createJob((Class<? extends QuartzJobBean>) Class.forName(schedulerJob.getClazz()),
|
||||
!recurringJob, context, schedulerJob.getName(), schedulerJob.getGroupName(), false);
|
||||
if(recurringJob) {
|
||||
Trigger trigger = scheduleCreator.createBasicCronTrigger(new Date(),
|
||||
schedulerJob.getCronExpression());
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
} else {
|
||||
scheduler.addJob(jobDetail, true);
|
||||
}
|
||||
} else {
|
||||
log.info("Not scheduling job {}, because it was already scheduled.", schedulerJob.getName());
|
||||
}
|
||||
} catch (ClassNotFoundException | SchedulerException e) {
|
||||
log.error("Failed to schedule job", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleJob(SchedulerJob job) {
|
||||
log.info("Scheduling job {}", job.getName());
|
||||
this.scheduleNewJob(schedulerFactoryBean.getScheduler(), job);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateJob(SchedulerJob job, Date startDate) {
|
||||
Trigger newTrigger;
|
||||
if (job.getCronExpression() != null) {
|
||||
newTrigger = scheduleCreator.createBasicCronTrigger(startDate, job.getCronExpression());
|
||||
} else {
|
||||
newTrigger = scheduleCreator.createSimpleOnceOnlyTrigger(job.getName(), startDate);
|
||||
}
|
||||
try {
|
||||
schedulerFactoryBean.getScheduler().rescheduleJob(TriggerKey.triggerKey(job.getName()), newTrigger);
|
||||
schedulerJobManagementServiceBean.save(job);
|
||||
} catch (SchedulerException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unScheduleJob(String jobName) {
|
||||
try {
|
||||
return schedulerFactoryBean.getScheduler().unscheduleJob(new TriggerKey(jobName));
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to un-schedule job - {}", jobName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteJob(SchedulerJob job) {
|
||||
try {
|
||||
return schedulerFactoryBean.getScheduler().deleteJob(new JobKey(job.getName(), job.getGroupName()));
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to delete job - {}", job.getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pauseJob(SchedulerJob job) {
|
||||
try {
|
||||
schedulerFactoryBean.getScheduler().pauseJob(new JobKey(job.getName(), job.getGroupName()));
|
||||
return true;
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to pause job - {}", job.getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueJob(SchedulerJob job) {
|
||||
try {
|
||||
schedulerFactoryBean.getScheduler().resumeJob(new JobKey(job.getName(), job.getGroupName()));
|
||||
return true;
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to resume job - {}", job.getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeJob(SchedulerJob job) {
|
||||
try {
|
||||
schedulerFactoryBean.getScheduler().triggerJob(new JobKey(job.getName(), job.getGroupName()));
|
||||
return true;
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to start new job - {}", job.getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeJobWithParametersOnce(String name, String group, JobDataMap dataMap, Date date) {
|
||||
Trigger onceOnlyTriggerForJob = scheduleCreator.createOnceOnlyTriggerForJob(name, group, date, dataMap);
|
||||
try {
|
||||
schedulerFactoryBean.getScheduler().scheduleJob(onceOnlyTriggerForJob);
|
||||
return true;
|
||||
} catch (SchedulerException e) {
|
||||
log.error("Failed to start new job - {}", name, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sheldan.abstracto.scheduling.service;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.config.JobConfigLoader;
|
||||
import dev.sheldan.abstracto.scheduling.factory.SchedulerJobConverter;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerJob;
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerService;
|
||||
import dev.sheldan.abstracto.scheduling.service.management.SchedulerJobManagementServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SchedulerStartupService {
|
||||
|
||||
@Autowired
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Autowired
|
||||
private JobConfigLoader jobConfigLoader;
|
||||
|
||||
@Autowired
|
||||
private SchedulerJobManagementServiceBean schedulerJobManagementServiceBean;
|
||||
|
||||
@Autowired
|
||||
private SchedulerJobConverter schedulerJobConverter;
|
||||
|
||||
@EventListener
|
||||
@Transactional
|
||||
public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
|
||||
jobConfigLoader.getJobs().forEach((s, schedulerJob) -> {
|
||||
SchedulerJob job = schedulerJobConverter.fromJobProperties(schedulerJob);
|
||||
if(!schedulerJobManagementServiceBean.doesJobExist(job) || !schedulerJobManagementServiceBean.isJobDefinitionTheSame(job)) {
|
||||
schedulerJobManagementServiceBean.createOrUpdate(job);
|
||||
}
|
||||
});
|
||||
schedulerService.startScheduledJobs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package dev.sheldan.abstracto.scheduling.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.scheduling.model.SchedulerJob;
|
||||
import dev.sheldan.abstracto.scheduling.repository.SchedulerJobRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SchedulerJobManagementServiceBean {
|
||||
@Autowired
|
||||
private SchedulerJobRepository repository;
|
||||
|
||||
public SchedulerJob createOrUpdate(SchedulerJob job) {
|
||||
if(repository.existsByName(job.getName())) {
|
||||
SchedulerJob byName = repository.findByName(job.getName());
|
||||
byName.setActive(job.isActive());
|
||||
byName.setClazz(job.getClazz());
|
||||
byName.setCronExpression(job.getCronExpression());
|
||||
byName.setGroupName(job.getGroupName());
|
||||
return repository.save(byName);
|
||||
} else {
|
||||
return this.createJob(job);
|
||||
}
|
||||
}
|
||||
|
||||
public SchedulerJob createJob(SchedulerJob job) {
|
||||
log.info("Creating job {}", job.getName());
|
||||
repository.save(job);
|
||||
return job;
|
||||
}
|
||||
|
||||
public List<SchedulerJob> findAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
public SchedulerJob save(SchedulerJob job) {
|
||||
repository.save(job);
|
||||
return job;
|
||||
}
|
||||
|
||||
public boolean doesJobExist(SchedulerJob schedulerJob) {
|
||||
return repository.existsByName(schedulerJob.getName());
|
||||
}
|
||||
|
||||
public boolean isJobDefinitionTheSame(SchedulerJob job) {
|
||||
SchedulerJob old = repository.findByName(job.getName());
|
||||
if(old == null) {
|
||||
return false;
|
||||
}
|
||||
boolean cronExp;
|
||||
if(old.getCronExpression() == null && job.getCronExpression() != null) {
|
||||
cronExp = false;
|
||||
} else if(old.getCronExpression() != null && job.getCronExpression() == null) {
|
||||
cronExp = false;
|
||||
} else if(old.getCronExpression() == null && job.getCronExpression() == null) {
|
||||
cronExp = true;
|
||||
} else {
|
||||
cronExp = old.getCronExpression().equals(job.getCronExpression());
|
||||
}
|
||||
boolean active = old.isActive() == job.isActive();
|
||||
boolean classEqual = old.getClazz().equals(job.getClazz());
|
||||
boolean group = old.getGroupName().equals(job.getGroupName());
|
||||
return cronExp && active && classEqual && group;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
spring.quartz.job-store-type=jdbc
|
||||
spring.quartz.jdbc.initialize-schema=never
|
||||
|
||||
spring.quartz.properties.org.quartz.scheduler.instanceName=quartz-abstracto-app
|
||||
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
|
||||
spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=dev.sheldan.abstracto.scheduling.service.IdGenerationService
|
||||
spring.quartz.properties.org.quartz.threadPool.threadCount=20
|
||||
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
|
||||
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
|
||||
spring.quartz.properties.org.quartz.jobStore.useProperties=true
|
||||
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000
|
||||
spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_
|
||||
spring.quartz.properties.org.quartz.jobStore.isClustered=false
|
||||
spring.quartz.properties.org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
|
||||
spring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown=TRUE
|
||||
30
abstracto-application/scheduling/scheduling-int/pom.xml
Normal file
30
abstracto-application/scheduling/scheduling-int/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.sheldan.abstracto.scheduling.model;
|
||||
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "scheduler_job")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SchedulerJob {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String groupName;
|
||||
|
||||
private String clazz;
|
||||
|
||||
private String cronExpression;
|
||||
|
||||
private boolean active;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.sheldan.abstracto.scheduling.model;
|
||||
|
||||
import org.quartz.JobDataMap;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface SchedulerService {
|
||||
void startScheduledJobs();
|
||||
|
||||
void scheduleJob(SchedulerJob job);
|
||||
|
||||
void updateJob(SchedulerJob job, Date startDate);
|
||||
|
||||
boolean unScheduleJob(String jobName);
|
||||
|
||||
boolean deleteJob(SchedulerJob job);
|
||||
|
||||
boolean pauseJob(SchedulerJob job);
|
||||
|
||||
boolean continueJob(SchedulerJob job);
|
||||
|
||||
boolean executeJob(SchedulerJob job);
|
||||
|
||||
boolean executeJobWithParametersOnce(String name, String group, JobDataMap dataMap, Date date);
|
||||
}
|
||||
@@ -17,4 +17,5 @@ public class EmbedConfiguration {
|
||||
private List<EmbedField> fields;
|
||||
private EmbedFooter footer;
|
||||
private OffsetDateTime timeStamp;
|
||||
private String additionalMessage;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.templating.loading;
|
||||
import com.google.gson.Gson;
|
||||
import dev.sheldan.abstracto.core.models.ContextAware;
|
||||
import dev.sheldan.abstracto.core.models.ServerContext;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
import dev.sheldan.abstracto.templating.TemplateDto;
|
||||
import dev.sheldan.abstracto.templating.TemplateService;
|
||||
import dev.sheldan.abstracto.templating.embed.*;
|
||||
@@ -11,7 +12,6 @@ import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
@@ -51,7 +51,7 @@ public class TemplateServiceBean implements TemplateService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageEmbed renderEmbedTemplate(String key, Object model) {
|
||||
public MessageToSend renderEmbedTemplate(String key, Object model) {
|
||||
String embedConfig = this.renderTemplate(key + "_embed", model);
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
EmbedConfiguration configuration = gson.fromJson(embedConfig, EmbedConfiguration.class);
|
||||
@@ -91,7 +91,11 @@ public class TemplateServiceBean implements TemplateService {
|
||||
builder.setColor(new Color(color.getR(), color.getG(), color.getB()).getRGB());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
||||
return MessageToSend.builder()
|
||||
.embed(builder.build())
|
||||
.message(configuration.getAdditionalMessage())
|
||||
.build();
|
||||
}
|
||||
|
||||
private String impromptu(String templateStr, Object model) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package dev.sheldan.abstracto.templating;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerContext;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface TemplateService {
|
||||
TemplateDto getTemplateByKey(String key);
|
||||
boolean templateExists(String key);
|
||||
String renderTemplate(TemplateDto templateDto);
|
||||
MessageEmbed renderEmbedTemplate(String key, Object model);
|
||||
MessageToSend renderEmbedTemplate(String key, Object model);
|
||||
String renderTemplate(String key, HashMap<String, Object> parameters);
|
||||
String renderTemplate(String key, Object model);
|
||||
String renderContextAwareTemplate(String key, ServerContext serverContext);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmbedAuthor {
|
||||
private String name;
|
||||
private String url;
|
||||
private String avatar;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class EmbedColor {
|
||||
private Integer r;
|
||||
private Integer g;
|
||||
private Integer b;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmbedConfiguration {
|
||||
private EmbedAuthor author;
|
||||
private EmbedTitle title;
|
||||
private EmbedColor color;
|
||||
private String description;
|
||||
private String thumbnail;
|
||||
private String imageUrl;
|
||||
private List<EmbedField> fields;
|
||||
private EmbedFooter footer;
|
||||
private OffsetDateTime timeStamp;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmbedField {
|
||||
private String name;
|
||||
private String value;
|
||||
private Boolean inline;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmbedFooter {
|
||||
private String text;
|
||||
private String icon;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package dev.sheldan.abstracto.templating.embed;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmbedTitle {
|
||||
private String title;
|
||||
private String url;
|
||||
}
|
||||
Reference in New Issue
Block a user