mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-10-31 08:32:18 +01:00
Improve docs, change game chat events a bit, add in large errors for certain things going wrong with the Discord connection
This commit is contained in:
parent
ff8385dfda
commit
a8cce7cf8d
@ -37,6 +37,7 @@ public interface DiscordMessageChannel extends Snowflake {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the provided message to the channel.
|
* Sends the provided message to the channel.
|
||||||
|
*
|
||||||
* @param message the channel to send to the channel
|
* @param message the channel to send to the channel
|
||||||
* @return a future returning the message after being sent
|
* @return a future returning the message after being sent
|
||||||
* @throws com.discordsrv.api.discord.api.exception.NotReadyException if DiscordSRV is not ready, {@link com.discordsrv.api.DiscordSRVApi#isReady()}
|
* @throws com.discordsrv.api.discord.api.exception.NotReadyException if DiscordSRV is not ready, {@link com.discordsrv.api.DiscordSRVApi#isReady()}
|
||||||
@ -44,8 +45,17 @@ public interface DiscordMessageChannel extends Snowflake {
|
|||||||
@NotNull
|
@NotNull
|
||||||
CompletableFuture<ReceivedDiscordMessage> sendMessage(SendableDiscordMessage message);
|
CompletableFuture<ReceivedDiscordMessage> sendMessage(SendableDiscordMessage message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the message identified by the id.
|
||||||
|
*
|
||||||
|
* @param id the id of the message to delete
|
||||||
|
* @return a future that will fail if the request fails
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> deleteMessageById(String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits the message identified by the id.
|
* Edits the message identified by the id.
|
||||||
|
*
|
||||||
* @param id the id of the message to edit
|
* @param id the id of the message to edit
|
||||||
* @param message the new message content
|
* @param message the new message content
|
||||||
* @return a future returning the message after being edited
|
* @return a future returning the message after being edited
|
||||||
|
@ -52,10 +52,19 @@ public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflak
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits this message to the provided message, the webhook username and avatar url will be ignored.
|
* Deletes this message.
|
||||||
|
*
|
||||||
|
* @return a future that will fail if the request fails
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> delete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits this message to the provided message.
|
||||||
*
|
*
|
||||||
* @param message the new message
|
* @param message the new message
|
||||||
* @return the future for the message edit
|
* @return a future that will fail if the request fails, otherwise the new message provided by the request response
|
||||||
|
* @throws IllegalArgumentException if the message is not a webhook message,
|
||||||
|
* but the provided {@link SendableDiscordMessage} specifies a webhook username.
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message);
|
CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message);
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.discordsrv.api.discord.api.entity.message;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cluster of Discord messages, or just a single message.
|
||||||
|
*/
|
||||||
|
public interface ReceivedDiscordMessageCluster {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the messages in this cluster.
|
||||||
|
* @return the messages in this cluster
|
||||||
|
*/
|
||||||
|
List<? extends ReceivedDiscordMessage> getMessages();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all the messages from this cluster, one request per message.
|
||||||
|
* @return a future that fails if any of the requests fail.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> deleteAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits all the messages in this cluster, one request per edit.
|
||||||
|
* @param newMessage the new content of the messages
|
||||||
|
* @return a future that fails if any of the requests fail.
|
||||||
|
*/
|
||||||
|
CompletableFuture<ReceivedDiscordMessageCluster> editAll(SendableDiscordMessage newMessage);
|
||||||
|
|
||||||
|
}
|
@ -45,4 +45,11 @@ public interface DiscordUser extends Snowflake {
|
|||||||
@NotNull
|
@NotNull
|
||||||
String getDiscriminator();
|
String getDiscriminator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Discord user's username followed by a {@code #} and their discriminator.
|
||||||
|
* @return the Discord user's username & discriminator in the following format {@code Username#1234}
|
||||||
|
*/
|
||||||
|
default String getAsTag() {
|
||||||
|
return getUsername() + "#" + getDiscriminator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public interface EventBus {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes the provided event listener to this {@link EventBus}.
|
* Subscribes the provided event listener to this {@link EventBus}.
|
||||||
* @param eventListener a event listener with atleast one valid {@link Subscribe} method.
|
* @param eventListener a event listener with at least one valid {@link Subscribe} method.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the given listener does not contain any valid listeners
|
* @throws IllegalArgumentException if the given listener does not contain any valid listeners
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +42,7 @@ public interface EventListener {
|
|||||||
String className();
|
String className();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the method for this event listener
|
* The name of the method for this event listener.
|
||||||
* @return the method name for this event listener
|
* @return the method name for this event listener
|
||||||
* @see Method#getName()
|
* @see Method#getName()
|
||||||
*/
|
*/
|
||||||
|
@ -33,8 +33,12 @@ public final class EventStateHolder {
|
|||||||
public static final ThreadLocal<EventListener> CANCELLED = new ThreadLocal<>();
|
public static final ThreadLocal<EventListener> CANCELLED = new ThreadLocal<>();
|
||||||
public static final ThreadLocal<EventListener> PROCESSED = new ThreadLocal<>();
|
public static final ThreadLocal<EventListener> PROCESSED = new ThreadLocal<>();
|
||||||
|
|
||||||
public static final EventListener FAKE_LISTENER = new FakeListener();
|
/**
|
||||||
|
* Used to indicate an unknown event listener.
|
||||||
|
*/
|
||||||
|
public static final EventListener UNKNOWN_LISTENER = new FakeListener();
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
private static class FakeListener implements EventListener {
|
private static class FakeListener implements EventListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,7 +61,7 @@ public interface Cancellable extends Event {
|
|||||||
EventListener listener = EventStateHolder.CANCELLED.get();
|
EventListener listener = EventStateHolder.CANCELLED.get();
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
throw new IllegalStateException("Event is not cancelled");
|
throw new IllegalStateException("Event is not cancelled");
|
||||||
} else if (listener == EventStateHolder.FAKE_LISTENER) {
|
} else if (listener == EventStateHolder.UNKNOWN_LISTENER) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public interface Processable extends Event {
|
|||||||
EventListener listener = EventStateHolder.PROCESSED.get();
|
EventListener listener = EventStateHolder.PROCESSED.get();
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
throw new IllegalStateException("Event has not been processed");
|
throw new IllegalStateException("Event has not been processed");
|
||||||
} else if (listener == EventStateHolder.FAKE_LISTENER) {
|
} else if (listener == EventStateHolder.UNKNOWN_LISTENER) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,59 +23,24 @@
|
|||||||
|
|
||||||
package com.discordsrv.api.event.events.message.send.game;
|
package com.discordsrv.api.event.events.message.send.game;
|
||||||
|
|
||||||
import com.discordsrv.api.channel.GameChannel;
|
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
|
||||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
import com.discordsrv.api.event.events.Event;
|
||||||
import com.discordsrv.api.event.events.Cancellable;
|
|
||||||
import com.discordsrv.api.event.events.Processable;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public abstract class AbstractGameMessageSendEvent implements Cancellable, Processable {
|
public abstract class AbstractGameMessageSentEvent implements Event {
|
||||||
|
|
||||||
private SendableDiscordMessage discordMessage;
|
private final ReceivedDiscordMessageCluster discordMessage;
|
||||||
private GameChannel targetChannel;
|
|
||||||
private boolean cancelled;
|
|
||||||
private boolean processed;
|
|
||||||
|
|
||||||
public AbstractGameMessageSendEvent(@NotNull SendableDiscordMessage discordMessage, @NotNull GameChannel targetChannel) {
|
public AbstractGameMessageSentEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) {
|
||||||
this.discordMessage = discordMessage;
|
this.discordMessage = discordMessage;
|
||||||
this.targetChannel = targetChannel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
/**
|
||||||
public SendableDiscordMessage getDiscordMessage() {
|
* Gets the {@link ReceivedDiscordMessageCluster} containing the sent message(s).
|
||||||
|
* @return the message cluster
|
||||||
|
*/
|
||||||
|
public ReceivedDiscordMessageCluster getDiscordMessage() {
|
||||||
return discordMessage;
|
return discordMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDiscordMessage(@NotNull SendableDiscordMessage discordMessage) {
|
|
||||||
this.discordMessage = discordMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public GameChannel getTargetChannel() {
|
|
||||||
return targetChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTargetChannel(@NotNull GameChannel targetChannel) {
|
|
||||||
this.targetChannel = targetChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return cancelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCancelled(boolean cancelled) {
|
|
||||||
this.cancelled = cancelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isProcessed() {
|
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markAsProcessed() {
|
|
||||||
this.processed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -23,13 +23,12 @@
|
|||||||
|
|
||||||
package com.discordsrv.api.event.events.message.send.game;
|
package com.discordsrv.api.event.events.message.send.game;
|
||||||
|
|
||||||
import com.discordsrv.api.channel.GameChannel;
|
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
|
||||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class ChatMessageSendEvent extends AbstractGameMessageSendEvent {
|
public class ChatMessageSentEvent extends AbstractGameMessageSentEvent {
|
||||||
|
|
||||||
public ChatMessageSendEvent(@NotNull SendableDiscordMessage discordMessage, @NotNull GameChannel targetChannel) {
|
public ChatMessageSentEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) {
|
||||||
super(discordMessage, targetChannel);
|
super(discordMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl;
|
|||||||
import com.discordsrv.common.event.bus.EventBusImpl;
|
import com.discordsrv.common.event.bus.EventBusImpl;
|
||||||
import com.discordsrv.common.function.CheckedRunnable;
|
import com.discordsrv.common.function.CheckedRunnable;
|
||||||
import com.discordsrv.common.listener.DefaultChannelLookupListener;
|
import com.discordsrv.common.listener.DefaultChannelLookupListener;
|
||||||
import com.discordsrv.common.listener.DefaultChatListener;
|
import com.discordsrv.common.listener.DefaultGameChatListener;
|
||||||
import com.discordsrv.common.logging.DependencyLoggingFilter;
|
import com.discordsrv.common.logging.DependencyLoggingFilter;
|
||||||
import com.discordsrv.common.logging.logger.backend.LoggingBackend;
|
import com.discordsrv.common.logging.logger.backend.LoggingBackend;
|
||||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||||
@ -249,7 +249,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
|||||||
// Register listeners
|
// Register listeners
|
||||||
// Chat
|
// Chat
|
||||||
eventBus().subscribe(new DefaultChannelLookupListener(this));
|
eventBus().subscribe(new DefaultChannelLookupListener(this));
|
||||||
eventBus().subscribe(new DefaultChatListener(this));
|
eventBus().subscribe(new DefaultGameChatListener(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@OverridingMethodsMustInvokeSuper
|
@OverridingMethodsMustInvokeSuper
|
||||||
|
@ -39,8 +39,11 @@ public class ChannelConfig {
|
|||||||
public ChannelConfig(DiscordSRV discordSRV) {
|
public ChannelConfig(DiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
this.discordSRV = discordSRV;
|
||||||
this.channels = discordSRV.caffeineBuilder()
|
this.channels = discordSRV.caffeineBuilder()
|
||||||
.expireAfterWrite(30, TimeUnit.SECONDS)
|
.expireAfterWrite(60, TimeUnit.SECONDS)
|
||||||
|
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||||
|
.refreshAfterWrite(10, TimeUnit.SECONDS)
|
||||||
.build(new CacheLoader<String, GameChannel>() {
|
.build(new CacheLoader<String, GameChannel>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable GameChannel load(@NonNull String channelName) {
|
public @Nullable GameChannel load(@NonNull String channelName) {
|
||||||
GameChannelLookupEvent event = new GameChannelLookupEvent(null, channelName);
|
GameChannelLookupEvent event = new GameChannelLookupEvent(null, channelName);
|
||||||
|
@ -28,5 +28,9 @@ public interface Console extends ICommandSender {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the logging backend for the server/proxy.
|
||||||
|
* @return the {@link LoggingBackend}
|
||||||
|
*/
|
||||||
LoggingBackend loggingBackend();
|
LoggingBackend loggingBackend();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.discordsrv.common.discord.api.message;
|
||||||
|
|
||||||
|
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;
|
||||||
|
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster;
|
||||||
|
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class ReceivedDiscordMessageClusterImpl implements ReceivedDiscordMessageCluster {
|
||||||
|
|
||||||
|
private final List<ReceivedDiscordMessage> messages;
|
||||||
|
|
||||||
|
public ReceivedDiscordMessageClusterImpl(List<ReceivedDiscordMessage> messages) {
|
||||||
|
this.messages = messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReceivedDiscordMessage> getMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> deleteAll() {
|
||||||
|
CompletableFuture<Void>[] futures = new CompletableFuture[messages.size()];
|
||||||
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
|
futures[i] = messages.get(i).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletableFuture.allOf(futures);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<ReceivedDiscordMessageCluster> editAll(SendableDiscordMessage newMessage) {
|
||||||
|
CompletableFuture<ReceivedDiscordMessage>[] futures = new CompletableFuture[messages.size()];
|
||||||
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
|
futures[i] = messages.get(i).edit(newMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletableFuture.allOf(futures)
|
||||||
|
.thenApply(v -> {
|
||||||
|
List<ReceivedDiscordMessage> messages = new ArrayList<>();
|
||||||
|
for (CompletableFuture<ReceivedDiscordMessage> future : futures) {
|
||||||
|
// All the futures are done, so we're just going to get the results from all of them
|
||||||
|
messages.add(
|
||||||
|
future.join());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReceivedDiscordMessageClusterImpl(messages);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -150,6 +150,18 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
|||||||
return textChannel;
|
return textChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> delete() {
|
||||||
|
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null);
|
||||||
|
if (textChannel == null) {
|
||||||
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
future.completeExceptionally(new UnknownChannelException());
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textChannel.deleteMessageById(getId());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message) {
|
public @NotNull CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message) {
|
||||||
if (!isWebhookMessage() && message.isWebhookMessage()) {
|
if (!isWebhookMessage() && message.isWebhookMessage()) {
|
||||||
@ -163,6 +175,6 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
return textChannel.editMessageById(textChannel.getId(), message);
|
return textChannel.editMessageById(getId(), message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.discordsrv.common.discord.connection.jda;
|
package com.discordsrv.common.discord.connection.jda;
|
||||||
|
|
||||||
|
import com.discordsrv.api.discord.api.entity.user.DiscordUser;
|
||||||
import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
|
import com.discordsrv.api.discord.connection.DiscordConnectionDetails;
|
||||||
import com.discordsrv.api.event.bus.EventPriority;
|
import com.discordsrv.api.event.bus.EventPriority;
|
||||||
import com.discordsrv.api.event.bus.Subscribe;
|
import com.discordsrv.api.event.bus.Subscribe;
|
||||||
@ -44,7 +45,9 @@ import net.dv8tion.jda.api.entities.*;
|
|||||||
import net.dv8tion.jda.api.events.DisconnectEvent;
|
import net.dv8tion.jda.api.events.DisconnectEvent;
|
||||||
import net.dv8tion.jda.api.events.ShutdownEvent;
|
import net.dv8tion.jda.api.events.ShutdownEvent;
|
||||||
import net.dv8tion.jda.api.events.StatusChangeEvent;
|
import net.dv8tion.jda.api.events.StatusChangeEvent;
|
||||||
|
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
|
||||||
import net.dv8tion.jda.api.requests.CloseCode;
|
import net.dv8tion.jda.api.requests.CloseCode;
|
||||||
|
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
import net.dv8tion.jda.api.requests.RestAction;
|
import net.dv8tion.jda.api.requests.RestAction;
|
||||||
import net.dv8tion.jda.api.utils.AllowedMentions;
|
import net.dv8tion.jda.api.utils.AllowedMentions;
|
||||||
@ -55,13 +58,20 @@ import net.dv8tion.jda.internal.utils.IOUtil;
|
|||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class JDAConnectionManager implements DiscordConnectionManager {
|
public class JDAConnectionManager implements DiscordConnectionManager {
|
||||||
|
|
||||||
|
private static final Map<GatewayIntent, String> PRIVILEGED_INTENTS = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
PRIVILEGED_INTENTS.put(GatewayIntent.GUILD_MEMBERS, "Server Members Intent");
|
||||||
|
PRIVILEGED_INTENTS.put(GatewayIntent.GUILD_PRESENCES, "Presence Intent");
|
||||||
|
}
|
||||||
|
|
||||||
private final DiscordSRV discordSRV;
|
private final DiscordSRV discordSRV;
|
||||||
private final ScheduledExecutorService gatewayPool;
|
private final ScheduledExecutorService gatewayPool;
|
||||||
private final ScheduledExecutorService rateLimitPool;
|
private final ScheduledExecutorService rateLimitPool;
|
||||||
@ -70,6 +80,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
private JDA instance;
|
private JDA instance;
|
||||||
private boolean detailsAccepted = true;
|
private boolean detailsAccepted = true;
|
||||||
|
|
||||||
|
// Bot owner details
|
||||||
|
private final AtomicReference<CompletableFuture<DiscordUser>> botOwnerRequest = new AtomicReference<>();
|
||||||
|
private long botOwnerRetrievalTime;
|
||||||
|
|
||||||
public JDAConnectionManager(DiscordSRV discordSRV) {
|
public JDAConnectionManager(DiscordSRV discordSRV) {
|
||||||
this.discordSRV = discordSRV;
|
this.discordSRV = discordSRV;
|
||||||
this.gatewayPool = new ScheduledThreadPoolExecutor(
|
this.gatewayPool = new ScheduledThreadPoolExecutor(
|
||||||
@ -80,11 +94,58 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
5,
|
5,
|
||||||
new CountingThreadFactory(Scheduler.THREAD_NAME_PREFIX + "JDA RateLimit #%s")
|
new CountingThreadFactory(Scheduler.THREAD_NAME_PREFIX + "JDA RateLimit #%s")
|
||||||
);
|
);
|
||||||
RestAction.setDefaultFailure(t -> discordSRV.logger().error("Callback failed", t));
|
|
||||||
|
// Set default failure handling
|
||||||
|
RestAction.setDefaultFailure(t -> {
|
||||||
|
if (t instanceof ErrorResponseException) {
|
||||||
|
ErrorResponse response = ((ErrorResponseException) t).getErrorResponse();
|
||||||
|
if (response == ErrorResponse.MFA_NOT_ENABLED) {
|
||||||
|
withBotOwner(user -> {
|
||||||
|
discordSRV.logger().error("+----------------------------------------------->");
|
||||||
|
discordSRV.logger().error("| Failed to complete a request:");
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| The Discord bot's owner needs to enable 2FA");
|
||||||
|
discordSRV.logger().error("| on their Discord account due to a Discord");
|
||||||
|
discordSRV.logger().error("| server requiring 2FA for moderation actions");
|
||||||
|
if (user != null) {
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| The Discord bot's owner is " + user.getAsTag());
|
||||||
|
}
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| You can view instructions for enabling 2FA here:");
|
||||||
|
discordSRV.logger().error("| https://support.discord.com/hc/en-us/articles/219576828-Setting-up-Two-Factor-Authentication");
|
||||||
|
discordSRV.logger().error("+----------------------------------------------->");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
discordSRV.logger().error("Callback failed", t);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disable all mentions by default for safety
|
||||||
AllowedMentions.setDefaultMentions(Collections.emptyList());
|
AllowedMentions.setDefaultMentions(Collections.emptyList());
|
||||||
|
|
||||||
discordSRV.eventBus().subscribe(this);
|
discordSRV.eventBus().subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void withBotOwner(Consumer<DiscordUser> botOwnerConsumer) {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
CompletableFuture<DiscordUser> request = botOwnerRequest.get();
|
||||||
|
if (request != null && botOwnerRetrievalTime + TimeUnit.MINUTES.toMillis(5) < currentTime) {
|
||||||
|
request.whenComplete((user, t) -> botOwnerConsumer.accept(t != null ? null : user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botOwnerRetrievalTime = currentTime;
|
||||||
|
CompletableFuture<DiscordUser> future = instance.retrieveApplicationInfo()
|
||||||
|
.timeout(10, TimeUnit.SECONDS)
|
||||||
|
.map(applicationInfo -> (DiscordUser) new DiscordUserImpl(applicationInfo.getOwner()))
|
||||||
|
.submit();
|
||||||
|
|
||||||
|
botOwnerRequest.set(future);
|
||||||
|
future.whenComplete((user, t) -> botOwnerConsumer.accept(t != null ? null : user));
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe(priority = EventPriority.LATE)
|
@Subscribe(priority = EventPriority.LATE)
|
||||||
public void onDSRVShuttingDown(DiscordSRVShuttingDownEvent event) {
|
public void onDSRVShuttingDown(DiscordSRVShuttingDownEvent event) {
|
||||||
// This has a timeout
|
// This has a timeout
|
||||||
@ -93,6 +154,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
|
|
||||||
@Subscribe(priority = EventPriority.EARLIEST)
|
@Subscribe(priority = EventPriority.EARLIEST)
|
||||||
public void onPlaceholderLookup(PlaceholderLookupEvent event) {
|
public void onPlaceholderLookup(PlaceholderLookupEvent event) {
|
||||||
|
if (event.isProcessed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Set<Object> newContext = new HashSet<>();
|
Set<Object> newContext = new HashSet<>();
|
||||||
for (Object o : event.getContext()) {
|
for (Object o : event.getContext()) {
|
||||||
Object converted;
|
Object converted;
|
||||||
@ -142,7 +207,8 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onDisconnect(DisconnectEvent event) {
|
public void onDisconnect(DisconnectEvent event) {
|
||||||
if (checkCode(event.getCloseCode())) {
|
CloseCode closeCode = event.getCloseCode();
|
||||||
|
if (checkCode(closeCode)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,19 +218,25 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
throw new IllegalStateException("Could not get the close frame for a disconnect");
|
throw new IllegalStateException("Could not get the close frame for a disconnect");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String message;
|
||||||
if (closedByServer) {
|
if (closedByServer) {
|
||||||
CloseCode closeCode = event.getCloseCode();
|
|
||||||
String closeReason = frame.getCloseReason();
|
String closeReason = frame.getCloseReason();
|
||||||
|
|
||||||
discordSRV.logger().debug("[JDA] [Server] Disconnected due to "
|
message = "[JDA] [Server] Disconnected due to "
|
||||||
+ frame.getCloseCode() + ": "
|
+ frame.getCloseCode() + ": "
|
||||||
+ (closeCode != null
|
+ (closeCode != null
|
||||||
? closeCode.getMeaning()
|
? closeCode.getMeaning()
|
||||||
: (closeReason != null ? closeReason : "(Unknown close reason)")));
|
: (closeReason != null ? closeReason : "(Unknown close reason)"));
|
||||||
} else {
|
} else {
|
||||||
discordSRV.logger().debug("[JDA] [Client] Disconnected due to "
|
message = "[JDA] [Client] Disconnected due to "
|
||||||
+ frame.getCloseCode() + ": "
|
+ frame.getCloseCode() + ": "
|
||||||
+ frame.getCloseReason());
|
+ frame.getCloseReason();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeCode != null && !closeCode.isReconnect()) {
|
||||||
|
discordSRV.logger().error(message);
|
||||||
|
} else {
|
||||||
|
discordSRV.logger().debug(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,10 +249,32 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
if (closeCode == null) {
|
if (closeCode == null) {
|
||||||
return false;
|
return false;
|
||||||
} else if (closeCode == CloseCode.DISALLOWED_INTENTS) {
|
} else if (closeCode == CloseCode.DISALLOWED_INTENTS) {
|
||||||
// TODO
|
Set<GatewayIntent> intents = discordSRV.discordConnectionDetails().getGatewayIntents();
|
||||||
|
discordSRV.logger().error("+-------------------------------------->");
|
||||||
|
discordSRV.logger().error("| Failed to connect to Discord:");
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| The Discord bot is lacking one or more");
|
||||||
|
discordSRV.logger().error("| privileged intents listed below");
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
for (GatewayIntent intent : intents) {
|
||||||
|
String displayName = PRIVILEGED_INTENTS.get(intent);
|
||||||
|
if (displayName != null) {
|
||||||
|
discordSRV.logger().error("| " + displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| Instructions for enabling privileged gateway intents:");
|
||||||
|
discordSRV.logger().error("| 1. Go to https://discord.com/developers/applications");
|
||||||
|
discordSRV.logger().error("| 2. Choose the bot you are using for DiscordSRV");
|
||||||
|
discordSRV.logger().error("| - Keep in mind it will only be visible to the ");
|
||||||
|
discordSRV.logger().error("| Discord user who created the bot");
|
||||||
|
discordSRV.logger().error("| 3. Go to the \"Bot\" tab");
|
||||||
|
discordSRV.logger().error("| 4. Make sure the intents listed above are all enabled");
|
||||||
|
discordSRV.logger().error("| 5. "); // TODO
|
||||||
|
discordSRV.logger().error("+-------------------------------------->");
|
||||||
return true;
|
return true;
|
||||||
} else if (closeCode.isReconnect()) {
|
} else if (closeCode == CloseCode.AUTHENTICATION_FAILED) {
|
||||||
// TODO
|
invalidToken();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -245,12 +339,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
instance = jdaBuilder.build();
|
instance = jdaBuilder.build();
|
||||||
break;
|
break;
|
||||||
} catch (LoginException ignored) {
|
} catch (LoginException ignored) {
|
||||||
discordSRV.logger().error("+-------------------------------+");
|
invalidToken();
|
||||||
discordSRV.logger().error("| Failed to connect to Discord: |");
|
|
||||||
discordSRV.logger().error("| |");
|
|
||||||
discordSRV.logger().error("| The token provided in the |");
|
|
||||||
discordSRV.logger().error("| " + ConnectionConfig.FILE_NAME + " is invalid |");
|
|
||||||
discordSRV.logger().error("+-------------------------------+");
|
|
||||||
break;
|
break;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
discordSRV.logger().error(t);
|
discordSRV.logger().error(t);
|
||||||
@ -267,6 +356,20 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void invalidToken() {
|
||||||
|
discordSRV.logger().error("+------------------------------>");
|
||||||
|
discordSRV.logger().error("| Failed to connect to Discord:");
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| The token provided in the");
|
||||||
|
discordSRV.logger().error("| " + ConnectionConfig.FILE_NAME + " is invalid");
|
||||||
|
discordSRV.logger().error("|");
|
||||||
|
discordSRV.logger().error("| You can get the token for your bot from:");
|
||||||
|
discordSRV.logger().error("| https://discord.com/developers/applications");
|
||||||
|
discordSRV.logger().error("| - Keep in mind the bot is only visible to");
|
||||||
|
discordSRV.logger().error("| the Discord user that created the bot");
|
||||||
|
discordSRV.logger().error("+------------------------------>");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> reconnect() {
|
public CompletableFuture<Void> reconnect() {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
|
@ -166,8 +166,9 @@ public class EventBusImpl implements EventBus {
|
|||||||
List<Boolean> states = new ArrayList<>(STATES.size());
|
List<Boolean> states = new ArrayList<>(STATES.size());
|
||||||
for (Pair<Function<Object, Boolean>, ThreadLocal<EventListener>> entry : STATES) {
|
for (Pair<Function<Object, Boolean>, ThreadLocal<EventListener>> entry : STATES) {
|
||||||
if (entry.getKey().apply(event)) {
|
if (entry.getKey().apply(event)) {
|
||||||
|
// If the state is already set before listeners, we mark it as being changed by a 'unknown' event listener
|
||||||
states.add(true);
|
states.add(true);
|
||||||
entry.getValue().set(EventStateHolder.FAKE_LISTENER);
|
entry.getValue().set(EventStateHolder.UNKNOWN_LISTENER);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
states.add(false);
|
states.add(false);
|
||||||
|
@ -19,32 +19,35 @@
|
|||||||
package com.discordsrv.common.listener;
|
package com.discordsrv.common.listener;
|
||||||
|
|
||||||
import com.discordsrv.api.channel.GameChannel;
|
import com.discordsrv.api.channel.GameChannel;
|
||||||
|
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;
|
||||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||||
import com.discordsrv.api.event.bus.EventPriority;
|
import com.discordsrv.api.event.bus.EventPriority;
|
||||||
import com.discordsrv.api.event.bus.Subscribe;
|
import com.discordsrv.api.event.bus.Subscribe;
|
||||||
import com.discordsrv.api.event.events.message.receive.game.ChatMessageReceiveEvent;
|
import com.discordsrv.api.event.events.message.receive.game.ChatMessageReceiveEvent;
|
||||||
import com.discordsrv.api.event.events.message.send.game.ChatMessageSendEvent;
|
import com.discordsrv.api.event.events.message.send.game.ChatMessageSentEvent;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.component.util.ComponentUtil;
|
import com.discordsrv.common.component.util.ComponentUtil;
|
||||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||||
import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig;
|
import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig;
|
||||||
|
import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageClusterImpl;
|
||||||
import com.discordsrv.common.function.OrDefault;
|
import com.discordsrv.common.function.OrDefault;
|
||||||
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
|
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class DefaultChatListener extends AbstractListener {
|
public class DefaultGameChatListener extends AbstractListener {
|
||||||
|
|
||||||
public DefaultChatListener(DiscordSRV discordSRV) {
|
public DefaultGameChatListener(DiscordSRV discordSRV) {
|
||||||
super(discordSRV);
|
super(discordSRV);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(priority = EventPriority.LAST)
|
@Subscribe(priority = EventPriority.LAST)
|
||||||
public void onChatReceive(ChatMessageReceiveEvent event) {
|
public void onChatReceive(ChatMessageReceiveEvent event) {
|
||||||
if (checkProcessor(event) || checkCancellation(event)) {
|
if (checkProcessor(event) || checkCancellation(event) || !discordSRV.isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,30 +67,33 @@ public class DefaultChatListener extends AbstractListener {
|
|||||||
.addReplacement("%message%", DiscordSerializer.INSTANCE.serialize(message))
|
.addReplacement("%message%", DiscordSerializer.INSTANCE.serialize(message))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
discordSRV.eventBus().publish(
|
List<String> channelIds = channelConfig.get(cfg -> cfg instanceof ChannelConfig ? ((ChannelConfig) cfg).channelIds : null);
|
||||||
new ChatMessageSendEvent(
|
if (channelIds == null || channelIds.isEmpty()) {
|
||||||
discordMessage,
|
|
||||||
gameChannel
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(priority = EventPriority.LAST)
|
|
||||||
public void onChatSend(ChatMessageSendEvent event) {
|
|
||||||
if (checkProcessor(event) || checkCancellation(event) || !discordSRV.isReady()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameChannel channel = event.getTargetChannel();
|
|
||||||
BaseChannelConfig channelConfig = discordSRV.channelConfig().get(channel);
|
|
||||||
List<String> channelIds = channelConfig instanceof ChannelConfig ? ((ChannelConfig) channelConfig).channelIds : Collections.emptyList();
|
|
||||||
if (channelIds.isEmpty()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<CompletableFuture<ReceivedDiscordMessage>> futures = new ArrayList<>();
|
||||||
for (String channelId : channelIds) {
|
for (String channelId : channelIds) {
|
||||||
discordSRV.discordAPI().getTextChannelById(channelId).ifPresent(textChannel ->
|
discordSRV.discordAPI().getTextChannelById(channelId).ifPresent(textChannel ->
|
||||||
textChannel.sendMessage(event.getDiscordMessage()));
|
futures.add(textChannel.sendMessage(discordMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
|
.whenComplete((v, t) -> {
|
||||||
|
if (t != null) {
|
||||||
|
discordSRV.logger().error("Failed to deliver message to Discord", t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReceivedDiscordMessage> messages = new ArrayList<>();
|
||||||
|
for (CompletableFuture<ReceivedDiscordMessage> future : futures) {
|
||||||
|
messages.add(future.join());
|
||||||
|
}
|
||||||
|
|
||||||
|
discordSRV.eventBus().publish(
|
||||||
|
new ChatMessageSentEvent(
|
||||||
|
new ReceivedDiscordMessageClusterImpl(messages)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user