mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-12-26 17:18:29 +01:00
Initial for console rotation
This commit is contained in:
parent
51cd79e700
commit
9d702ef8d9
@ -81,6 +81,7 @@ import com.discordsrv.common.feature.profile.ProfileManager;
|
||||
import com.discordsrv.common.feature.update.UpdateChecker;
|
||||
import com.discordsrv.common.helper.ChannelConfigHelper;
|
||||
import com.discordsrv.common.helper.DestinationLookupHelper;
|
||||
import com.discordsrv.common.helper.TemporaryLocalData;
|
||||
import com.discordsrv.common.logging.adapter.DependencyLoggerAdapter;
|
||||
import com.discordsrv.common.util.ApiInstanceUtil;
|
||||
import com.discordsrv.common.util.UUIDUtil;
|
||||
@ -154,6 +155,7 @@ public abstract class AbstractDiscordSRV<
|
||||
private JDAConnectionManager discordConnectionManager;
|
||||
private ChannelConfigHelper channelConfig;
|
||||
private DestinationLookupHelper destinationLookupHelper;
|
||||
private TemporaryLocalData temporaryLocalData;
|
||||
|
||||
private Storage storage;
|
||||
private LinkProvider linkProvider;
|
||||
@ -191,6 +193,7 @@ public abstract class AbstractDiscordSRV<
|
||||
this.discordConnectionManager = new JDAConnectionManager(this);
|
||||
this.channelConfig = new ChannelConfigHelper(this);
|
||||
this.destinationLookupHelper = new DestinationLookupHelper(this);
|
||||
this.temporaryLocalData = new TemporaryLocalData(this);
|
||||
this.updateChecker = new UpdateChecker(this);
|
||||
readManifest();
|
||||
|
||||
@ -363,6 +366,11 @@ public abstract class AbstractDiscordSRV<
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemporaryLocalData temporaryLocalData() {
|
||||
return temporaryLocalData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LinkProvider linkProvider() {
|
||||
return linkProvider;
|
||||
@ -707,6 +715,7 @@ public abstract class AbstractDiscordSRV<
|
||||
} catch (Throwable t) {
|
||||
logger().error("Failed to close storage connection", t);
|
||||
}
|
||||
temporaryLocalData.save();
|
||||
this.status.set(Status.SHUTDOWN);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ import com.discordsrv.common.feature.linking.LinkProvider;
|
||||
import com.discordsrv.common.feature.profile.ProfileManager;
|
||||
import com.discordsrv.common.helper.ChannelConfigHelper;
|
||||
import com.discordsrv.common.helper.DestinationLookupHelper;
|
||||
import com.discordsrv.common.helper.TemporaryLocalData;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import okhttp3.OkHttpClient;
|
||||
@ -110,6 +111,7 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
|
||||
// Storage
|
||||
Storage storage();
|
||||
TemporaryLocalData temporaryLocalData();
|
||||
|
||||
// Link Provider
|
||||
LinkProvider linkProvider();
|
||||
|
@ -32,7 +32,10 @@ import java.util.List;
|
||||
public class ConsoleConfig {
|
||||
|
||||
@Comment("The console channel or thread")
|
||||
public DestinationConfig.Single channel = new DestinationConfig.Single();
|
||||
public DestinationConfig.Single channel = new DestinationConfig.Single("DiscordSRV Console #%date:'w'%", true);
|
||||
|
||||
@Comment("The amount of threads to keep. Rotation interval is based on placeholders in the thread name")
|
||||
public int threadsToKeepInRotation = 3;
|
||||
|
||||
public Appender appender = new Appender();
|
||||
|
||||
|
@ -51,6 +51,14 @@ public class DestinationConfig {
|
||||
@Setting(nodeFromParent = true)
|
||||
public ThreadConfig thread = new ThreadConfig("");
|
||||
|
||||
@SuppressWarnings("unused") // Configurate
|
||||
public Single() {}
|
||||
|
||||
public Single(String threadName, boolean privateThread) {
|
||||
this.thread.threadName = threadName;
|
||||
this.thread.privateThread = privateThread;
|
||||
}
|
||||
|
||||
public DestinationConfig asDestination() {
|
||||
DestinationConfig config = new DestinationConfig();
|
||||
if (thread == null || StringUtils.isEmpty(thread.threadName)) {
|
||||
|
@ -94,12 +94,12 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
private final Set<DiscordMemberCachePolicy> memberCachePolicies = new HashSet<>();
|
||||
|
||||
// Bot owner details
|
||||
private final Timeout botOwnerTimeout = new Timeout(5, TimeUnit.MINUTES);
|
||||
private final Timeout botOwnerTimeout = new Timeout(Duration.ofMinutes(5));
|
||||
private final AtomicReference<CompletableFuture<DiscordUser>> botOwnerRequest = new AtomicReference<>();
|
||||
|
||||
// Logging timeouts
|
||||
private final Timeout mfaTimeout = new Timeout(30, TimeUnit.SECONDS);
|
||||
private final Timeout serverErrorTimeout = new Timeout(20, TimeUnit.SECONDS);
|
||||
private final Timeout mfaTimeout = new Timeout(Duration.ofSeconds(30));
|
||||
private final Timeout serverErrorTimeout = new Timeout(Duration.ofSeconds(20));
|
||||
|
||||
public JDAConnectionManager(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
@ -293,7 +293,9 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
throw new IllegalStateException("Cannot reconnect, still active");
|
||||
}
|
||||
|
||||
return connectionFuture = discordSRV.scheduler().execute(this::connectInternal);
|
||||
this.connectInternal();
|
||||
return CompletableFuture.completedFuture(null);
|
||||
//return connectionFuture = discordSRV.scheduler().execute(this::connectInternal);
|
||||
}
|
||||
|
||||
private void connectInternal() {
|
||||
|
@ -45,6 +45,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -511,7 +512,12 @@ public class SingleConsoleHandler {
|
||||
}
|
||||
|
||||
sendFuture = sendFuture
|
||||
.thenCompose(__ -> discordSRV.destinations().lookupDestination(config.channel.asDestination(), true, true))
|
||||
.thenCompose(__ -> discordSRV.destinations().lookupDestination(
|
||||
config.channel.asDestination(),
|
||||
true,
|
||||
true,
|
||||
OffsetDateTime.now())
|
||||
)
|
||||
.thenCompose(channels -> {
|
||||
if (channels.isEmpty()) {
|
||||
// Nowhere to send to
|
||||
|
@ -48,7 +48,8 @@ public class DestinationLookupHelper {
|
||||
public CompletableFuture<List<DiscordGuildMessageChannel>> lookupDestination(
|
||||
DestinationConfig config,
|
||||
boolean allowRequests,
|
||||
boolean logFailures
|
||||
boolean logFailures,
|
||||
Object... threadNameContext
|
||||
) {
|
||||
List<CompletableFuture<? extends DiscordGuildMessageChannel>> futures = new ArrayList<>();
|
||||
|
||||
@ -90,7 +91,9 @@ public class DestinationLookupHelper {
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscordThreadChannel existingThread = findThread(threadContainer.getActiveThreads(), threadConfig);
|
||||
String threadName = discordSRV.placeholderService().replacePlaceholders(threadConfig.threadName, threadNameContext);
|
||||
|
||||
DiscordThreadChannel existingThread = findThread(threadContainer.getActiveThreads(), threadName);
|
||||
if (existingThread != null && !existingThread.isArchived()) {
|
||||
futures.add(CompletableFuture.completedFuture(existingThread));
|
||||
continue;
|
||||
@ -100,7 +103,7 @@ public class DestinationLookupHelper {
|
||||
continue;
|
||||
}
|
||||
|
||||
String threadKey = Long.toUnsignedString(channelId) + ":" + threadConfig.threadName + "/" + threadConfig.privateThread;
|
||||
String threadKey = Long.toUnsignedString(channelId) + ":" + threadName + "/" + threadConfig.privateThread;
|
||||
|
||||
CompletableFuture<DiscordThreadChannel> future;
|
||||
synchronized (threadActions) {
|
||||
@ -110,7 +113,7 @@ public class DestinationLookupHelper {
|
||||
future = existingFuture;
|
||||
} else if (!threadConfig.unarchiveExisting) {
|
||||
// Unarchiving not allowed, create new
|
||||
future = createThread(threadContainer, threadConfig, logFailures);
|
||||
future = createThread(threadContainer, threadName, threadConfig.privateThread, logFailures);
|
||||
} else if (existingThread != null) {
|
||||
// Unarchive existing thread
|
||||
future = unarchiveThread(existingThread, logFailures);
|
||||
@ -122,14 +125,14 @@ public class DestinationLookupHelper {
|
||||
: threadContainer.retrieveArchivedPublicThreads();
|
||||
|
||||
future = threads.thenCompose(archivedThreads -> {
|
||||
DiscordThreadChannel archivedThread = findThread(archivedThreads, threadConfig);
|
||||
DiscordThreadChannel archivedThread = findThread(archivedThreads, threadName);
|
||||
if (archivedThread != null) {
|
||||
// Unarchive existing thread
|
||||
return unarchiveThread(archivedThread, logFailures);
|
||||
}
|
||||
|
||||
// Create thread
|
||||
return createThread(threadContainer, threadConfig, logFailures);
|
||||
return createThread(threadContainer, threadName, threadConfig.privateThread, logFailures);
|
||||
}).exceptionally(t -> {
|
||||
if (logFailures) {
|
||||
logger.error("Failed to lookup threads in channel #" + threadContainer.getName(), t);
|
||||
@ -164,9 +167,9 @@ public class DestinationLookupHelper {
|
||||
});
|
||||
}
|
||||
|
||||
private DiscordThreadChannel findThread(Collection<DiscordThreadChannel> threads, ThreadConfig config) {
|
||||
private DiscordThreadChannel findThread(Collection<DiscordThreadChannel> threads, String threadName) {
|
||||
for (DiscordThreadChannel thread : threads) {
|
||||
if (thread.getName().equals(config.threadName)) {
|
||||
if (thread.getName().equals(threadName)) {
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
@ -175,11 +178,12 @@ public class DestinationLookupHelper {
|
||||
|
||||
private CompletableFuture<DiscordThreadChannel> createThread(
|
||||
DiscordThreadContainer threadContainer,
|
||||
ThreadConfig threadConfig,
|
||||
String threadName,
|
||||
boolean privateThread,
|
||||
boolean logFailures
|
||||
) {
|
||||
boolean forum = threadContainer instanceof DiscordForumChannel;
|
||||
boolean privateThread = !forum && threadConfig.privateThread;
|
||||
if (forum) privateThread = false;
|
||||
|
||||
Permission createPermission;
|
||||
if (forum) {
|
||||
@ -196,7 +200,7 @@ public class DestinationLookupHelper {
|
||||
);
|
||||
if (missingPermissions != null) {
|
||||
if (logFailures) {
|
||||
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
||||
logger.error("Failed to create thread \"" + threadName + "\" "
|
||||
+ "in channel #" + threadContainer.getName() + ": " + missingPermissions);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
@ -205,15 +209,15 @@ public class DestinationLookupHelper {
|
||||
CompletableFuture<DiscordThreadChannel> future;
|
||||
if (forum) {
|
||||
future = ((DiscordForumChannel) threadContainer).createPost(
|
||||
threadConfig.threadName,
|
||||
threadName,
|
||||
SendableDiscordMessage.builder().setContent("\u200B").build() // zero-width-space
|
||||
);
|
||||
} else {
|
||||
future = threadContainer.createThread(threadConfig.threadName, privateThread);
|
||||
future = threadContainer.createThread(threadName, privateThread);
|
||||
}
|
||||
return future.exceptionally(t -> {
|
||||
if (logFailures) {
|
||||
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
||||
logger.error("Failed to create thread \"" + threadName + "\" "
|
||||
+ "in channel #" + threadContainer.getName(), t);
|
||||
}
|
||||
return null;
|
||||
|
@ -0,0 +1,97 @@
|
||||
package com.discordsrv.common.helper;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
import org.jetbrains.annotations.NonBlocking;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Data that may or may not actually ever persist.
|
||||
* @see Model
|
||||
* @see #get()
|
||||
* @see #save()
|
||||
*/
|
||||
public class TemporaryLocalData {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final Path file;
|
||||
private Model model;
|
||||
private Future<?> saveFuture;
|
||||
|
||||
public TemporaryLocalData(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.file = discordSRV.dataDirectory().resolve(".temporary-local-data.json");
|
||||
}
|
||||
|
||||
@Blocking
|
||||
public Model get() {
|
||||
if (model != null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
return model = resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@NonBlocking
|
||||
public void saveLater() {
|
||||
synchronized (this) {
|
||||
if (saveFuture != null && !saveFuture.isDone()) {
|
||||
return;
|
||||
}
|
||||
saveFuture = discordSRV.scheduler().runLater(this::save, Duration.ofSeconds(30));
|
||||
}
|
||||
}
|
||||
|
||||
@Blocking
|
||||
public void save() {
|
||||
synchronized (this) {
|
||||
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(file))) {
|
||||
discordSRV.json().writeValue(outputStream, model);
|
||||
} catch (IOException e) {
|
||||
discordSRV.logger().error("Failed to save temporary local data", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Blocking
|
||||
private Model resolve() {
|
||||
synchronized (this) {
|
||||
if (model != null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
if (!Files.exists(file)) {
|
||||
return new Model();
|
||||
}
|
||||
|
||||
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file))) {
|
||||
return discordSRV.json().readValue(inputStream, Model.class);
|
||||
} catch (IOException e) {
|
||||
discordSRV.logger().error("Failed to load temporary local data, resetting", e);
|
||||
return new Model();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saved/loaded via {@link DiscordSRV#json()} ({@link com.fasterxml.jackson.databind.ObjectMapper}).
|
||||
*/
|
||||
public static class Model {
|
||||
|
||||
/**
|
||||
* {@link com.discordsrv.common.feature.console.SingleConsoleHandler} thread rotation.
|
||||
*/
|
||||
public Map<String, List<Long>> consoleThreadRotationIds;
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ package com.discordsrv.common.helper;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@ -31,8 +31,8 @@ public class Timeout {
|
||||
private final AtomicLong last = new AtomicLong(0);
|
||||
private final long timeoutMS;
|
||||
|
||||
public Timeout(long time, @NotNull TimeUnit unit) {
|
||||
this(unit.toMillis(time));
|
||||
public Timeout(@NotNull Duration duration) {
|
||||
this(duration.toMillis());
|
||||
}
|
||||
|
||||
public Timeout(long timeoutMS) {
|
||||
|
Loading…
Reference in New Issue
Block a user