Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.features.Routine;

import javax.annotation.Nullable;

import java.awt.Color;
import java.awt.*;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.concurrent.TimeUnit;
Expand All @@ -35,6 +33,7 @@ public final class RemindRoutine implements Routine {
static final Color AMBIENT_COLOR = Color.decode("#F7F492");
private static final int SCHEDULE_INTERVAL_SECONDS = 30;
private final Database database;
private static final int MAX_FAILURE_RETRY=3;

/**
* Creates a new instance.
Expand All @@ -55,25 +54,25 @@ public Schedule createSchedule() {
public void runRoutine(JDA jda) {
Instant now = Instant.now();
database.write(context -> context.selectFrom(PENDING_REMINDERS)
.where(PENDING_REMINDERS.REMIND_AT.lessOrEqual(now))
.stream()
.forEach(pendingReminder -> {
sendReminder(jda, pendingReminder.getId(), pendingReminder.getChannelId(),
pendingReminder.getAuthorId(), pendingReminder.getContent(),
pendingReminder.getCreatedAt());

pendingReminder.delete();
}));
.where(PENDING_REMINDERS.REMIND_AT.lessOrEqual(now))
.stream()
.forEach(pendingReminder -> {
Reminder reminder = new Reminder(pendingReminder.getId(),pendingReminder.getCreatedAt(),
pendingReminder.getGuildId(),pendingReminder.getChannelId(),pendingReminder.getAuthorId(),
pendingReminder.getRemindAt(),pendingReminder.getContent(),pendingReminder.getFailureAttempts());
sendReminder(jda, reminder);

pendingReminder.delete();
}));
}

private static void sendReminder(JDA jda, long id, long channelId, long authorId,
CharSequence content, TemporalAccessor createdAt) {
RestAction<ReminderRoute> route = computeReminderRoute(jda, channelId, authorId);
sendReminderViaRoute(route, id, content, createdAt);
private void sendReminder(JDA jda, Reminder reminder) {
RestAction<ReminderRoute> route = computeReminderRoute(jda, reminder.channelId(), reminder.authorId());
sendReminderViaRoute(route, reminder);
}

private static RestAction<ReminderRoute> computeReminderRoute(JDA jda, long channelId,
long authorId) {
long authorId) {
// If guild channel can still be found, send there
MessageChannel channel = jda.getChannelById(MessageChannel.class, channelId);
if (channel != null) {
Expand All @@ -85,43 +84,35 @@ private static RestAction<ReminderRoute> computeReminderRoute(JDA jda, long chan
}

private static RestAction<ReminderRoute> createGuildReminderRoute(JDA jda, long authorId,
MessageChannel channel) {
MessageChannel channel) {
return jda.retrieveUserById(authorId)
.onErrorMap(error -> null)
.map(author -> ReminderRoute.toPublic(channel, author));
.onErrorMap(error -> null)
.map(author -> ReminderRoute.toPublic(channel, author));
}

private static RestAction<ReminderRoute> createDmReminderRoute(JDA jda, long authorId) {
return jda.openPrivateChannelById(authorId).map(ReminderRoute::toPrivate);
}

private static void sendReminderViaRoute(RestAction<ReminderRoute> routeAction, long id,
CharSequence content, TemporalAccessor createdAt) {
private void sendReminderViaRoute(RestAction<ReminderRoute> routeAction,Reminder reminder) {
Function<ReminderRoute, MessageCreateAction> sendMessage = route -> route.channel
.sendMessageEmbeds(createReminderEmbed(content, createdAt, route.target()))
.setContent(route.description());

Consumer<Throwable> logFailure = failure -> logger.warn(
"""
Failed to send a reminder (id '{}'), skipping it. This can be due to a network issue, \
but also happen if the bot disconnected from the target guild and the \
user has disabled DMs or has been deleted.""",
id);
.sendMessageEmbeds(createReminderEmbed(reminder.content(), reminder.createdAt(), route.target()))
.setContent(route.description());

routeAction.flatMap(sendMessage).queue(doNothing(), logFailure);
routeAction.flatMap(sendMessage).queue(doNothing(), failure-> attemptRetryReminder(reminder));
}

private static MessageEmbed createReminderEmbed(CharSequence content,
TemporalAccessor createdAt, @Nullable User author) {
TemporalAccessor createdAt, @Nullable User author) {
String authorName = author == null ? "Unknown user" : author.getAsTag();
String authorIconUrl = author == null ? null : author.getAvatarUrl();

return new EmbedBuilder().setAuthor(authorName, null, authorIconUrl)
.setDescription(content)
.setFooter("reminder from")
.setTimestamp(createdAt)
.setColor(AMBIENT_COLOR)
.build();
.setDescription(content)
.setFooter("reminder from")
.setTimestamp(createdAt)
.setColor(AMBIENT_COLOR)
.build();
}

private static <T> Consumer<T> doNothing() {
Expand All @@ -130,7 +121,7 @@ private static <T> Consumer<T> doNothing() {
}

private record ReminderRoute(MessageChannel channel, @Nullable User target,
@Nullable String description) {
@Nullable String description) {
static ReminderRoute toPublic(MessageChannel channel, @Nullable User target) {
return new ReminderRoute(channel, target,
target == null ? null : target.getAsMention());
Expand All @@ -142,4 +133,30 @@ static ReminderRoute toPrivate(PrivateChannel channel) {
+ " the original channel you wanted it to be send to)");
}
}

private void attemptRetryReminder(Reminder reminder) {
if (!(reminder.failureAttempts() <= MAX_FAILURE_RETRY)) {
logger.warn(
"""
Failed to send a reminder with (authorID '{}') and (content '{}'), skipping it. This can be due to a network issue, \
but also happen if the bot disconnected from the target guild and the \
user has disabled DMs or has been deleted.""",
reminder.authorId(),reminder.content());
return;

}

Instant current = Instant.now().plusSeconds(60);
database.write(context -> context.newRecord(PENDING_REMINDERS)
.setId(reminder.id())
.setCreatedAt(reminder.createdAt())
.setGuildId(reminder.guildId())
.setChannelId(reminder.channelId())
.setAuthorId(reminder.authorId())
.setRemindAt(current)
.setContent(reminder.content())
.setReminderFailureCounter(reminder.failureAttempts()+1)
.insert());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.togetherjava.tjbot.features.reminder;

import java.time.temporal.TemporalAccessor;

public record Reminder(int id, TemporalAccessor createdAt,long guildId,long channelId,long authorId,TemporalAccessor remindAt,String content,int failureAttempts) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE pending_reminders ADD failure_attempts INTEGER DEFAULT 0 NOT NULL ;