mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-10-31 08:32:18 +01:00
Specifying intents, cache flags and member caching policies in modules
This commit is contained in:
parent
1e8ce58b79
commit
7a9c21c56d
@ -25,7 +25,7 @@ package com.discordsrv.api;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponentFactory;
|
||||
import com.discordsrv.api.discord.DiscordAPI;
|
||||
import com.discordsrv.api.discord.connection.jda.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.event.bus.EventBus;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.player.DiscordSRVPlayer;
|
||||
@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The DiscordSRV API.
|
||||
@ -261,7 +262,7 @@ public interface DiscordSRVApi {
|
||||
CONFIG(false),
|
||||
LINKED_ACCOUNT_PROVIDER(false),
|
||||
STORAGE(true),
|
||||
DISCORD_CONNECTION(true),
|
||||
DISCORD_CONNECTION(DiscordSRVApi::isReady),
|
||||
MODULES(false),
|
||||
|
||||
;
|
||||
@ -269,14 +270,31 @@ public interface DiscordSRVApi {
|
||||
public static final Set<ReloadFlag> ALL = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(values())));
|
||||
public static final Set<ReloadFlag> DEFAULT_FLAGS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(CONFIG, MODULES)));
|
||||
|
||||
private final boolean requiresConfirm;
|
||||
private final Predicate<DiscordSRVApi> requiresConfirm;
|
||||
|
||||
ReloadFlag(boolean requiresConfirm) {
|
||||
this(__ -> requiresConfirm);
|
||||
}
|
||||
|
||||
ReloadFlag(Predicate<DiscordSRVApi> requiresConfirm) {
|
||||
this.requiresConfirm = requiresConfirm;
|
||||
}
|
||||
|
||||
public boolean requiresConfirm() {
|
||||
return requiresConfirm;
|
||||
public boolean requiresConfirm(DiscordSRVApi discordSRV) {
|
||||
return requiresConfirm.test(discordSRV);
|
||||
}
|
||||
}
|
||||
|
||||
interface ReloadResult {
|
||||
|
||||
ReloadResult RESTART_REQUIRED = Results.RESTART_REQUIRED;
|
||||
|
||||
String name();
|
||||
|
||||
enum Results implements ReloadResult {
|
||||
|
||||
RESTART_REQUIRED
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.discord.connection.details;
|
||||
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||
|
||||
public enum DiscordCacheFlag implements JDAEntity<CacheFlag> {
|
||||
|
||||
ACTIVITY(CacheFlag.ACTIVITY),
|
||||
VOICE_STATE(CacheFlag.VOICE_STATE),
|
||||
EMOJI(CacheFlag.EMOJI),
|
||||
STICKER(CacheFlag.STICKER),
|
||||
CLIENT_STATUS(CacheFlag.CLIENT_STATUS),
|
||||
MEMBER_OVERRIDES(CacheFlag.MEMBER_OVERRIDES),
|
||||
ROLE_TAGS(CacheFlag.ROLE_TAGS),
|
||||
FORUM_TAGS(CacheFlag.FORUM_TAGS),
|
||||
ONLINE_STATUS(CacheFlag.ONLINE_STATUS),
|
||||
SCHEDULED_EVENTS(CacheFlag.SCHEDULED_EVENTS),
|
||||
|
||||
;
|
||||
|
||||
private final CacheFlag jda;
|
||||
|
||||
DiscordCacheFlag(CacheFlag jda) {
|
||||
this.jda = jda;
|
||||
}
|
||||
|
||||
public DiscordGatewayIntent requiredIntent() {
|
||||
GatewayIntent intent = jda.getRequiredIntent();
|
||||
if (intent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DiscordGatewayIntent.getByJda(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheFlag asJDA() {
|
||||
return jda;
|
||||
}
|
||||
}
|
@ -21,61 +21,46 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.discord.connection.jda;
|
||||
package com.discordsrv.api.discord.connection.details;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A helper class to provide {@link GatewayIntent}s and {@link CacheFlag}s to the {@link net.dv8tion.jda.api.JDA} instance created by DiscordSRV during startup.
|
||||
* A helper class to provide {@link DiscordGatewayIntent}s and {@link DiscordCacheFlag}s for the Discord connection.
|
||||
* @see DiscordSRVApi#discordConnectionDetails()
|
||||
*/
|
||||
@SuppressWarnings("unused") // API
|
||||
public interface DiscordConnectionDetails {
|
||||
|
||||
/**
|
||||
* If {@link #requestGatewayIntent(GatewayIntent, GatewayIntent...)}} and {@link #requestCacheFlag(CacheFlag, CacheFlag...)} can be used.
|
||||
* @return true, if {@link GatewayIntent}s and {@link CacheFlag} will be accepted
|
||||
*/
|
||||
boolean readyToTakeDetails();
|
||||
|
||||
/**
|
||||
* The current gateway intents.
|
||||
* @return the current set of gateway intents
|
||||
*/
|
||||
@NotNull
|
||||
Set<GatewayIntent> getGatewayIntents();
|
||||
|
||||
/**
|
||||
* Requests that the provided {@link GatewayIntent}s be passed to {@link net.dv8tion.jda.api.JDA}.
|
||||
* Requests that the provided {@link DiscordGatewayIntent}s be passed to the Discord connection.
|
||||
*
|
||||
* @param gatewayIntent the first gateway intent to add
|
||||
* @param gatewayIntents more gateway intents
|
||||
* @throws IllegalStateException if DiscordSRV is already connecting/connected to Discord
|
||||
* @see #readyToTakeDetails()
|
||||
* @return {@code true} if the Discord connection is yet to be created and the intent will become active once it is
|
||||
*/
|
||||
void requestGatewayIntent(@NotNull GatewayIntent gatewayIntent, @NotNull GatewayIntent... gatewayIntents);
|
||||
boolean requestGatewayIntent(@NotNull DiscordGatewayIntent gatewayIntent, @NotNull DiscordGatewayIntent... gatewayIntents);
|
||||
|
||||
/**
|
||||
* The current cache flags.
|
||||
* @return the current set of cache flags
|
||||
*/
|
||||
@NotNull
|
||||
Set<CacheFlag> getCacheFlags();
|
||||
|
||||
/**
|
||||
* Requests that the provided {@link CacheFlag} be passed to {@link net.dv8tion.jda.api.JDA}.
|
||||
* Requests that the provided {@link DiscordCacheFlag}s be passed to the Discord connection.
|
||||
*
|
||||
* @param cacheFlag the first cache flag
|
||||
* @param cacheFlags more cache flags
|
||||
* @throws IllegalStateException if DiscordSRV is already connecting/connected to Discord
|
||||
* @return {@code true} if the Discord connection is yet to be created and the intent will become active once it is
|
||||
* @throws IllegalArgumentException if one of the requested {@link CacheFlag}s requires a {@link GatewayIntent} that hasn't been requested
|
||||
* @see #readyToTakeDetails()
|
||||
*/
|
||||
void requestCacheFlag(@NotNull CacheFlag cacheFlag, @NotNull CacheFlag... cacheFlags);
|
||||
boolean requestCacheFlag(@NotNull DiscordCacheFlag cacheFlag, @NotNull DiscordCacheFlag... cacheFlags);
|
||||
|
||||
/**
|
||||
* Requests that the provided {@link DiscordMemberCachePolicy}s be passed to the Discord connection.
|
||||
*
|
||||
* @param memberCachePolicy the first member cache policy
|
||||
* @param memberCachePolicies more member cache policies
|
||||
* @return {@code true} if the Discord connection is yet to be created and the intent will become active once it is
|
||||
*/
|
||||
boolean requestMemberCachePolicy(@NotNull DiscordMemberCachePolicy memberCachePolicy, @NotNull DiscordMemberCachePolicy... memberCachePolicies);
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.discord.connection.details;
|
||||
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
|
||||
public enum DiscordGatewayIntent implements JDAEntity<GatewayIntent> {
|
||||
|
||||
GUILD_MEMBERS(GatewayIntent.GUILD_MEMBERS, "Server Members Intent"),
|
||||
GUILD_BANS(GatewayIntent.GUILD_BANS),
|
||||
GUILD_EMOJIS_AND_STICKERS(GatewayIntent.GUILD_EMOJIS_AND_STICKERS),
|
||||
GUILD_WEBHOOKS(GatewayIntent.GUILD_WEBHOOKS),
|
||||
GUILD_INVITES(GatewayIntent.GUILD_INVITES),
|
||||
GUILD_VOICE_STATES(GatewayIntent.GUILD_VOICE_STATES),
|
||||
GUILD_PRESENCES(GatewayIntent.GUILD_PRESENCES, "Presence Intent"),
|
||||
GUILD_MESSAGES(GatewayIntent.GUILD_MESSAGES),
|
||||
GUILD_MESSAGE_REACTIONS(GatewayIntent.GUILD_MESSAGE_REACTIONS),
|
||||
GUILD_MESSAGE_TYPING(GatewayIntent.GUILD_MESSAGE_TYPING),
|
||||
DIRECT_MESSAGES(GatewayIntent.DIRECT_MESSAGES),
|
||||
DIRECT_MESSAGE_REACTIONS(GatewayIntent.DIRECT_MESSAGE_REACTIONS),
|
||||
DIRECT_MESSAGE_TYPING(GatewayIntent.DIRECT_MESSAGE_TYPING),
|
||||
MESSAGE_CONTENT(GatewayIntent.MESSAGE_CONTENT, "Message Content Intent"),
|
||||
SCHEDULED_EVENTS(GatewayIntent.SCHEDULED_EVENTS),
|
||||
|
||||
;
|
||||
|
||||
static DiscordGatewayIntent getByJda(GatewayIntent jda) {
|
||||
for (DiscordGatewayIntent value : values()) {
|
||||
if (value.asJDA() == jda) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("This intent does not have a ");
|
||||
}
|
||||
|
||||
private final GatewayIntent jda;
|
||||
private final String portalName;
|
||||
private final boolean privileged;
|
||||
|
||||
DiscordGatewayIntent(GatewayIntent jda) {
|
||||
this(jda, null, false);
|
||||
}
|
||||
|
||||
DiscordGatewayIntent(GatewayIntent jda, String portalName) {
|
||||
this(jda, portalName, true);
|
||||
}
|
||||
|
||||
DiscordGatewayIntent(GatewayIntent jda, String portalName, boolean privileged) {
|
||||
this.jda = jda;
|
||||
this.portalName = portalName;
|
||||
this.privileged = privileged;
|
||||
}
|
||||
|
||||
public String portalName() {
|
||||
return portalName;
|
||||
}
|
||||
|
||||
public boolean privileged() {
|
||||
return privileged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayIntent asJDA() {
|
||||
return jda;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the DiscordSRV API, licensed under the MIT License
|
||||
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.discord.connection.details;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.profile.IProfile;
|
||||
|
||||
/**
|
||||
* Represents a Discord member caching policy, a function which dictates if a given {@link DiscordGuildMember} should be cached.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DiscordMemberCachePolicy {
|
||||
|
||||
DiscordMemberCachePolicy ALL = member -> true;
|
||||
DiscordMemberCachePolicy LINKED = member -> DiscordSRVApi.optional()
|
||||
.map(api -> api.profileManager().getProfile(member.getUser().getId()))
|
||||
.map(IProfile::isLinked).orElse(false);
|
||||
DiscordMemberCachePolicy VOICE = member -> member.asJDA().getVoiceState() != null;
|
||||
DiscordMemberCachePolicy OWNER = member -> member.asJDA().isOwner();
|
||||
|
||||
boolean isCached(DiscordGuildMember member);
|
||||
}
|
@ -23,12 +23,57 @@
|
||||
|
||||
package com.discordsrv.api.module.type;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public interface Module {
|
||||
|
||||
/**
|
||||
* Determines if this {@link Module} should be enabled at the instant this method is called, this will be used
|
||||
* to determine when modules should be enabled or disabled when DiscordSRV enabled, disables and reloads.
|
||||
* @return the current enabled status the module should be in currently
|
||||
*/
|
||||
default boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link Collection} of {@link DiscordGatewayIntent}s that are required for this {@link Module}.
|
||||
* @return the collection of gateway intents required by this module at the time this method is called
|
||||
*/
|
||||
@NotNull
|
||||
default Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link Collection} of {@link DiscordCacheFlag}s that are required for this {@link Module}.
|
||||
* {@link DiscordGatewayIntent}s required by the cache flags will be required automatically.
|
||||
* @return the collection of cache flags required by this module at the time this method is called
|
||||
*/
|
||||
@NotNull
|
||||
default Collection<DiscordCacheFlag> requiredCacheFlags() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link Collection} of {@link DiscordMemberCachePolicy DiscordMemberCachePolicies} that are required for this {@link Module},
|
||||
* if a policy other than {@link DiscordMemberCachePolicy#OWNER} or {@link DiscordMemberCachePolicy#VOICE} is provided the {@link DiscordGatewayIntent#GUILD_MEMBERS} intent will be required automatically.
|
||||
* @return the collection of member caching policies required by this module at the time this method is called
|
||||
*/
|
||||
@NotNull
|
||||
default Collection<DiscordMemberCachePolicy> requiredMemberCachingPolicies() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of this Module given the lookup type.
|
||||
* @param type the type being looked up this could be an interface
|
||||
@ -39,14 +84,39 @@ public interface Module {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the order which this module should shut down in compared to other modules.
|
||||
* @return the shutdown order of this module, higher values will be shut down first. The default is the same as {@link #priority(Class)} with the type of the class.
|
||||
*/
|
||||
default int shutdownOrder() {
|
||||
return priority(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by DiscordSRV to enable this module. Calls {@link #reload()} if not implemented.
|
||||
*/
|
||||
default void enable() {
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by DiscordSRV to disable this module.
|
||||
*/
|
||||
default void disable() {}
|
||||
default void reload() {}
|
||||
|
||||
/**
|
||||
* Called by DiscordSRV to reload this module. This is called to enable the module as well unless {@link #enable()} is overridden and does not call super.
|
||||
* Use {@link #reloadNoResult()} if you don't wish to provide any result.
|
||||
* @return the result(s) that occurred during this reload, if any. May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
default Set<DiscordSRVApi.ReloadResult> reload() {
|
||||
reloadNoResult();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative to {@link #reload()}, which returns {@code void} instead of results. This method will <b>not</b> be called if {@link #reload()} is overridden!
|
||||
*/
|
||||
default void reloadNoResult() {}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.api.discord.connection.jda.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVConnectedEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReadyEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadedEvent;
|
||||
@ -92,11 +91,8 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.jar.Attributes;
|
||||
@ -120,7 +116,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
private PlaceholderServiceImpl placeholderService;
|
||||
private ComponentFactory componentFactory;
|
||||
private DiscordAPIImpl discordAPI;
|
||||
private DiscordConnectionDetails discordConnectionDetails;
|
||||
private DiscordConnectionDetailsImpl discordConnectionDetails;
|
||||
|
||||
// DiscordSRV
|
||||
protected final B bootstrap;
|
||||
@ -189,9 +185,9 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
|| host.endsWith("discord.gg");
|
||||
|
||||
String userAgent = isDiscord
|
||||
? "DiscordBot (https://github.com/DiscordSRV/DiscordSRV, " + versionInfo() + ")"
|
||||
? "DiscordBot (https://github.com/DiscordSRV/DiscordSRV, " + versionInfo().version() + ")"
|
||||
+ " (" + JDAInfo.GITHUB + ", " + JDAInfo.VERSION + ")"
|
||||
: "DiscordSRV/" + versionInfo();
|
||||
: "DiscordSRV/" + versionInfo().version();
|
||||
|
||||
return chain.proceed(
|
||||
original.newBuilder()
|
||||
@ -283,7 +279,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @NotNull DiscordConnectionDetails discordConnectionDetails() {
|
||||
public final @NotNull DiscordConnectionDetailsImpl discordConnectionDetails() {
|
||||
return discordConnectionDetails;
|
||||
}
|
||||
|
||||
@ -385,7 +381,11 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
Constructor<?> constructor = clazz.getConstructors()[0];
|
||||
module = constructor.newInstance(this);
|
||||
} catch (Throwable e) {
|
||||
moduleManager.logger().debug("Failed to load integration: " + className, e);
|
||||
String suffix = "";
|
||||
if (e instanceof LinkageError || e instanceof ClassNotFoundException) {
|
||||
suffix = " (Integration likely not installed or using wrong version)";
|
||||
}
|
||||
moduleManager.logger().debug("Failed to load integration: " + className + suffix, e);
|
||||
return;
|
||||
}
|
||||
moduleManager.registerModule(this, d -> (AbstractModule<?>) module);
|
||||
@ -396,6 +396,11 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
moduleManager.unregister(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleManager moduleManager() {
|
||||
return moduleManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale locale() {
|
||||
// TODO: config
|
||||
@ -446,7 +451,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
|
||||
// Lifecycle
|
||||
|
||||
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable runnable) {
|
||||
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable<?> runnable) {
|
||||
return invokeLifecycle(() -> {
|
||||
try {
|
||||
lifecycleLock.lock();
|
||||
@ -454,21 +459,22 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
} finally {
|
||||
lifecycleLock.unlock();
|
||||
}
|
||||
return null;
|
||||
}, "Failed to enable", true);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> invokeLifecycle(CheckedRunnable runnable, String message, boolean enable) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
protected <T> CompletableFuture<T> invokeLifecycle(CheckedRunnable<T> runnable, String message, boolean enable) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
if (status().isShutdown()) {
|
||||
// Already shutdown/shutting down, don't bother
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
runnable.run();
|
||||
return runnable.run();
|
||||
} catch (Throwable t) {
|
||||
if (status().isShutdown() && t instanceof NoClassDefFoundError) {
|
||||
// Already shutdown, ignore errors for classes that already got unloaded
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (enable) {
|
||||
setStatus(Status.FAILED_TO_START);
|
||||
@ -476,6 +482,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
logger().error(message, t);
|
||||
}
|
||||
return null;
|
||||
}, scheduler().executorService());
|
||||
}
|
||||
|
||||
@ -485,6 +492,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
this.enable();
|
||||
waitForStatus(Status.CONNECTED);
|
||||
eventBus().publish(new DiscordSRVReadyEvent());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -498,7 +506,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<Void> invokeReload(Set<ReloadFlag> flags, boolean silent) {
|
||||
public final CompletableFuture<List<ReloadResult>> invokeReload(Set<ReloadFlag> flags, boolean silent) {
|
||||
return invokeLifecycle(() -> reload(flags, silent), "Failed to reload", false);
|
||||
}
|
||||
|
||||
@ -573,7 +581,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
protected void reload(Set<ReloadFlag> flags, boolean initial) throws Throwable {
|
||||
protected List<ReloadResult> reload(Set<ReloadFlag> flags, boolean initial) throws Throwable {
|
||||
if (!initial) {
|
||||
logger().info("Reloading DiscordSRV...");
|
||||
}
|
||||
@ -595,13 +603,13 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
if (updateConfig.security.enabled) {
|
||||
if (updateChecker.isSecurityFailed()) {
|
||||
// Security has already failed
|
||||
return;
|
||||
return Collections.singletonList(ReloadResults.SECURITY_FAILED);
|
||||
}
|
||||
|
||||
if (initial && !updateChecker.check(true)) {
|
||||
// Security failed cancel startup & shutdown
|
||||
invokeDisable();
|
||||
return;
|
||||
return Collections.singletonList(ReloadResults.SECURITY_FAILED);
|
||||
}
|
||||
} else if (initial) {
|
||||
// Not using security, run update check off thread
|
||||
@ -670,7 +678,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
e.log(this);
|
||||
logger().error("Failed to connect to storage");
|
||||
setStatus(Status.FAILED_TO_START);
|
||||
return;
|
||||
return Collections.singletonList(ReloadResults.STORAGE_CONNECTION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,6 +691,9 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
if (!initial) {
|
||||
waitForStatus(Status.CONNECTED, 20, TimeUnit.SECONDS);
|
||||
if (status() != Status.CONNECTED) {
|
||||
return Collections.singletonList(ReloadResults.DISCORD_CONNECTION_FAILED);
|
||||
}
|
||||
} else {
|
||||
JDA jda = jda();
|
||||
if (jda != null) {
|
||||
@ -690,7 +701,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
jda.awaitReady();
|
||||
} catch (IllegalStateException ignored) {
|
||||
// JDA shutdown -> don't continue
|
||||
return;
|
||||
return Collections.singletonList(ReloadResults.DISCORD_CONNECTION_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -699,13 +710,17 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
|
||||
}
|
||||
}
|
||||
|
||||
List<ReloadResult> results = new ArrayList<>();
|
||||
if (flags.contains(ReloadFlag.MODULES)) {
|
||||
moduleManager.reload();
|
||||
results.addAll(moduleManager.reload());
|
||||
}
|
||||
|
||||
if (!initial) {
|
||||
eventBus().publish(new DiscordSRVReloadedEvent(flags));
|
||||
logger().info("Reload complete.");
|
||||
}
|
||||
|
||||
results.add(ReloadResults.SUCCESS);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,11 @@ import com.discordsrv.common.debug.data.VersionInfo;
|
||||
import com.discordsrv.common.dependency.DiscordSRVDependencyManager;
|
||||
import com.discordsrv.common.discord.api.DiscordAPIImpl;
|
||||
import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;
|
||||
import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl;
|
||||
import com.discordsrv.common.linking.LinkProvider;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.impl.DiscordSRVLogger;
|
||||
import com.discordsrv.common.module.ModuleManager;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.player.provider.AbstractPlayerProvider;
|
||||
@ -52,6 +54,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -112,11 +115,14 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
// Internal
|
||||
JDAConnectionManager discordConnectionManager();
|
||||
|
||||
@NotNull DiscordConnectionDetailsImpl discordConnectionDetails();
|
||||
|
||||
// Modules
|
||||
@Nullable
|
||||
<T extends Module> T getModule(Class<T> moduleType);
|
||||
void registerModule(AbstractModule<?> module);
|
||||
void unregisterModule(AbstractModule<?> module);
|
||||
ModuleManager moduleManager();
|
||||
|
||||
Locale locale();
|
||||
|
||||
@ -139,6 +145,17 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
// Lifecycle
|
||||
CompletableFuture<Void> invokeEnable();
|
||||
CompletableFuture<Void> invokeDisable();
|
||||
CompletableFuture<Void> invokeReload(Set<ReloadFlag> flags, boolean silent);
|
||||
CompletableFuture<List<ReloadResult>> invokeReload(Set<ReloadFlag> flags, boolean silent);
|
||||
|
||||
enum ReloadResults implements ReloadResult {
|
||||
|
||||
// Internal reasons
|
||||
SUCCESS,
|
||||
SECURITY_FAILED,
|
||||
STORAGE_CONNECTION_FAILED,
|
||||
DISCORD_CONNECTION_RELOAD_REQUIRED,
|
||||
DISCORD_CONNECTION_FAILED
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,9 +51,10 @@ public abstract class ServerDiscordSRV<B extends IBootstrap, C extends MainConfi
|
||||
public final CompletableFuture<Void> invokeServerStarted() {
|
||||
return invokeLifecycle(() -> {
|
||||
if (status().isShutdown()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
this.serverStarted();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class ChannelUpdaterModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
public void reloadNoResult() {
|
||||
futures.forEach(future -> future.cancel(false));
|
||||
futures.clear();
|
||||
|
||||
|
@ -47,7 +47,7 @@ public class GameCommandModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
public void reloadNoResult() {
|
||||
CommandConfig config = discordSRV.config().command;
|
||||
if (config == null) {
|
||||
return;
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.common.debug.DebugReport;
|
||||
import com.discordsrv.common.paste.Paste;
|
||||
import com.discordsrv.common.paste.PasteService;
|
||||
import com.discordsrv.common.paste.service.AESEncryptedPasteService;
|
||||
import com.discordsrv.common.paste.service.BytebinPasteService;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
@ -67,7 +68,7 @@ public class DebugCommand implements GameCommandExecutor {
|
||||
|
||||
public DebugCommand(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.pasteService = new AESEncryptedPasteService(null /* TODO: tbd */, 128);
|
||||
this.pasteService = new AESEncryptedPasteService(new BytebinPasteService(discordSRV, "https://bytebin.lucko.me") /* TODO: final store tbd */, 128);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,13 +18,17 @@
|
||||
|
||||
package com.discordsrv.common.command.game.command.subcommand;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommand;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
|
||||
import com.discordsrv.common.command.game.abstraction.GameCommandSuggester;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
@ -75,7 +79,7 @@ public class ReloadCommand implements GameCommandExecutor, GameCommandSuggester
|
||||
return;
|
||||
}
|
||||
|
||||
discordSRV.invokeReload(flags, false).whenComplete((v, t) -> {
|
||||
discordSRV.invokeReload(flags, false).whenComplete((results, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to reload", t);
|
||||
sender.sendMessage(
|
||||
@ -83,8 +87,42 @@ public class ReloadCommand implements GameCommandExecutor, GameCommandSuggester
|
||||
.append(Component.text("Reload failed.", NamedTextColor.DARK_RED, TextDecoration.BOLD))
|
||||
.append(Component.text("Please check the server console/log for more details."))
|
||||
);
|
||||
} else {
|
||||
sender.sendMessage(Component.text("Reload successful", NamedTextColor.GRAY));
|
||||
return;
|
||||
}
|
||||
|
||||
for (DiscordSRV.ReloadResult result : results) {
|
||||
String res = result.name();
|
||||
if (res.equals(DiscordSRV.ReloadResults.SECURITY_FAILED.name())) {
|
||||
sender.sendMessage(Component.text(
|
||||
"DiscordSRV is disabled due to a security check failure. "
|
||||
+ "Please check console for more details", NamedTextColor.DARK_RED));
|
||||
} else if (res.equals(DiscordSRV.ReloadResults.SUCCESS.name())) {
|
||||
sender.sendMessage(Component.text("Reload successful", NamedTextColor.GRAY));
|
||||
} else if (res.equals(DiscordSRV.ReloadResults.RESTART_REQUIRED.name())) {
|
||||
sender.sendMessage(Component.text("Some changes require a server restart"));
|
||||
} else if (res.equals(DiscordSRV.ReloadResults.STORAGE_CONNECTION_FAILED.name())) {
|
||||
sender.sendMessage(Component.text("Storage connection failed, please check console for details.", NamedTextColor.RED));
|
||||
} else if (res.equals(DiscordSRV.ReloadResults.DISCORD_CONNECTION_FAILED.name())) {
|
||||
sender.sendMessage(Component.text("Discord connection failed, please check console for details.", NamedTextColor.RED));
|
||||
} else if (res.equals(DiscordSRV.ReloadResults.DISCORD_CONNECTION_RELOAD_REQUIRED.name())) {
|
||||
String command = "discordsrv reload " + DiscordSRVApi.ReloadFlag.DISCORD_CONNECTION.name().toLowerCase(Locale.ROOT) + " -confirm";
|
||||
Component child;
|
||||
if (sender instanceof IPlayer) {
|
||||
child = Component.text("[Click to reload Discord connection]", NamedTextColor.DARK_RED)
|
||||
.clickEvent(ClickEvent.runCommand("/" + command))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("/" + command)));
|
||||
} else {
|
||||
child = Component.text("Run ", NamedTextColor.DARK_RED)
|
||||
.append(Component.text(command, NamedTextColor.GRAY))
|
||||
.append(Component.text(" to reload the Discord connection"));
|
||||
}
|
||||
|
||||
sender.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Some changes require a Discord connection reload. ", NamedTextColor.GRAY))
|
||||
.append(child)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -103,10 +141,16 @@ public class ReloadCommand implements GameCommandExecutor, GameCommandSuggester
|
||||
boolean confirm = parts.remove("-confirm");
|
||||
|
||||
Set<DiscordSRV.ReloadFlag> flags = new LinkedHashSet<>();
|
||||
if (discordSRV.status().isStartupError()) {
|
||||
// If startup error, use all flags
|
||||
parts.clear();
|
||||
flags.addAll(DiscordSRVApi.ReloadFlag.ALL);
|
||||
}
|
||||
|
||||
for (String part : parts) {
|
||||
try {
|
||||
DiscordSRV.ReloadFlag flag = DiscordSRV.ReloadFlag.valueOf(part.toUpperCase(Locale.ROOT));
|
||||
if (flag.requiresConfirm() && !confirm) {
|
||||
if (flag.requiresConfirm(discordSRV) && !confirm) {
|
||||
dangerousFlags.set(true);
|
||||
sender.sendMessage(
|
||||
Component.text("Reloading ", NamedTextColor.RED)
|
||||
|
@ -45,6 +45,8 @@ public class MainConfig implements Config {
|
||||
|
||||
public LinkedAccountConfig linkedAccounts = new LinkedAccountConfig();
|
||||
|
||||
public MemberCachingConfig memberCaching = new MemberCachingConfig();
|
||||
|
||||
public List<ChannelUpdaterConfig> channelUpdaters = new ArrayList<>(Collections.singletonList(new ChannelUpdaterConfig()));
|
||||
|
||||
@Comment("Configuration options for group-role synchronization")
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of DiscordSRV, licensed under the GPLv3 License
|
||||
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.config.main;
|
||||
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ConfigSerializable
|
||||
public class MemberCachingConfig {
|
||||
|
||||
@Comment("If linked users' members should be cached, this requires the \"Server Members Intent\"")
|
||||
public boolean linkedUsers = true;
|
||||
|
||||
@Comment("If all members should be cached")
|
||||
public boolean all = false;
|
||||
|
||||
@Comment("If members should be cached at startup, this requires the \"Server Members Intent\"")
|
||||
public boolean chunk = false;
|
||||
|
||||
@Comment("Filter for which servers should be chunked")
|
||||
public GuildFilter chunkingServerFilter = new GuildFilter();
|
||||
|
||||
@ConfigSerializable
|
||||
public static class GuildFilter {
|
||||
|
||||
@Comment("If the ids option acts as a blacklist, otherwise it is a whitelist")
|
||||
public boolean blacklist = true;
|
||||
public List<Long> ids = new ArrayList<>();
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ package com.discordsrv.common.discord.api;
|
||||
import club.minnced.discord.webhook.WebhookClient;
|
||||
import club.minnced.discord.webhook.WebhookClientBuilder;
|
||||
import com.discordsrv.api.discord.DiscordAPI;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.*;
|
||||
@ -54,7 +55,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -444,9 +444,8 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
|
||||
@Override
|
||||
public boolean isUserCachingEnabled() {
|
||||
return discordSRV.discordConnectionDetails()
|
||||
.getGatewayIntents()
|
||||
.contains(GatewayIntent.GUILD_MEMBERS);
|
||||
return discordSRV.discordConnectionManager().getIntents()
|
||||
.contains(DiscordGatewayIntent.GUILD_MEMBERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,12 +38,6 @@ public interface DiscordConnectionManager {
|
||||
@Nullable
|
||||
JDA instance();
|
||||
|
||||
/**
|
||||
* Are gateway intents and cache flags accepted.
|
||||
* @return true for yes
|
||||
*/
|
||||
boolean areDetailsAccepted();
|
||||
|
||||
/**
|
||||
* Attempts to connect to Discord.
|
||||
* @return a {@link CompletableFuture}
|
||||
|
@ -18,9 +18,13 @@
|
||||
|
||||
package com.discordsrv.common.discord.connection.jda;
|
||||
|
||||
import com.discordsrv.api.discord.connection.jda.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy;
|
||||
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
@ -29,9 +33,13 @@ import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.connection.BotConfig;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MemberCachingConfig;
|
||||
import com.discordsrv.common.debug.DebugGenerateEvent;
|
||||
import com.discordsrv.common.debug.file.TextDebugFile;
|
||||
import com.discordsrv.common.discord.api.DiscordAPIImpl;
|
||||
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl;
|
||||
import com.discordsrv.common.discord.connection.DiscordConnectionManager;
|
||||
import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.scheduler.Scheduler;
|
||||
@ -52,28 +60,24 @@ import net.dv8tion.jda.api.exceptions.InvalidTokenException;
|
||||
import net.dv8tion.jda.api.exceptions.RateLimitedException;
|
||||
import net.dv8tion.jda.api.requests.*;
|
||||
import net.dv8tion.jda.api.utils.ChunkingFilter;
|
||||
import net.dv8tion.jda.api.utils.MemberCachePolicy;
|
||||
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageRequest;
|
||||
import net.dv8tion.jda.internal.entities.ReceivedMessage;
|
||||
import net.dv8tion.jda.internal.hooks.EventManagerProxy;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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");
|
||||
PRIVILEGED_INTENTS.put(GatewayIntent.MESSAGE_CONTENT, "Message Content Intent");
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final FailureCallback failureCallback;
|
||||
private Future<?> failureCallbackFuture;
|
||||
@ -82,7 +86,11 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
|
||||
private CompletableFuture<Void> connectionFuture;
|
||||
private JDA instance;
|
||||
private boolean detailsAccepted = true;
|
||||
|
||||
// Currently used intents & cache flags
|
||||
private final Set<DiscordGatewayIntent> intents = new HashSet<>();
|
||||
private final Set<DiscordCacheFlag> cacheFlags = new HashSet<>();
|
||||
private final Set<DiscordMemberCachePolicy> memberCachePolicies = new HashSet<>();
|
||||
|
||||
// Bot owner details
|
||||
private final Timeout botOwnerTimeout = new Timeout(5, TimeUnit.MINUTES);
|
||||
@ -108,14 +116,21 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA instance() {
|
||||
return instance;
|
||||
public Set<DiscordGatewayIntent> getIntents() {
|
||||
return intents;
|
||||
}
|
||||
|
||||
public Set<DiscordCacheFlag> getCacheFlags() {
|
||||
return cacheFlags;
|
||||
}
|
||||
|
||||
public Set<DiscordMemberCachePolicy> getMemberCachePolicies() {
|
||||
return memberCachePolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areDetailsAccepted() {
|
||||
return detailsAccepted;
|
||||
public JDA instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void checkDefaultFailureCallback() {
|
||||
@ -141,10 +156,10 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
DiscordSRV.Status newStatus;
|
||||
if (ordinal < JDA.Status.CONNECTED.ordinal()) {
|
||||
newStatus = DiscordSRV.Status.ATTEMPTING_TO_CONNECT;
|
||||
} else if (ordinal < JDA.Status.SHUTTING_DOWN.ordinal()) {
|
||||
newStatus = DiscordSRV.Status.CONNECTED;
|
||||
} else {
|
||||
} else if (status == JDA.Status.DISCONNECTED || ordinal >= JDA.Status.SHUTTING_DOWN.ordinal()) {
|
||||
newStatus = DiscordSRV.Status.FAILED_TO_CONNECT;
|
||||
} else {
|
||||
newStatus = DiscordSRV.Status.CONNECTED;
|
||||
}
|
||||
discordSRV.setStatus(newStatus);
|
||||
}
|
||||
@ -174,6 +189,37 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
return discordSRV.discordAPI();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDebugGenerate(DebugGenerateEvent event) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Intents: ").append(intents);
|
||||
builder.append("\nCache Flags: ").append(cacheFlags);
|
||||
builder.append("\nMember Caching Policies: ").append(memberCachePolicies.size());
|
||||
|
||||
if (instance != null) {
|
||||
CompletableFuture<Long> restPingFuture = instance.getRestPing().timeout(5, TimeUnit.SECONDS).submit();
|
||||
builder.append("\nGateway Ping: ").append(instance.getGatewayPing()).append("ms");
|
||||
|
||||
String restPing;
|
||||
try {
|
||||
restPing = restPingFuture.get() + "ms";
|
||||
} catch (ExecutionException e) {
|
||||
if (e.getCause() instanceof TimeoutException) {
|
||||
restPing = ">5s";
|
||||
} else {
|
||||
restPing = ExceptionUtils.getMessage(e);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
restPing = ExceptionUtils.getMessage(t);
|
||||
}
|
||||
|
||||
builder.append("\nRest Ping: ").append(restPing);
|
||||
}
|
||||
|
||||
event.addFile(new TextDebugFile("jda_connection_manager.txt", builder));
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.EARLIEST)
|
||||
public void onPlaceholderLookup(PlaceholderLookupEvent event) {
|
||||
if (event.isProcessed()) {
|
||||
@ -232,10 +278,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
}
|
||||
|
||||
private void connectInternal() {
|
||||
discordSRV.discordConnectionDetails().requestGatewayIntent(GatewayIntent.GUILD_MESSAGES); // TODO: figure out how DiscordSRV required intents are going to work
|
||||
discordSRV.discordConnectionDetails().requestGatewayIntent(GatewayIntent.GUILD_MEMBERS); // TODO: figure out how DiscordSRV required intents are going to work
|
||||
detailsAccepted = false;
|
||||
|
||||
discordSRV.setStatus(DiscordSRVApi.Status.ATTEMPTING_TO_CONNECT);
|
||||
this.gatewayPool = new ScheduledThreadPoolExecutor(
|
||||
1,
|
||||
r -> new Thread(r, Scheduler.THREAD_NAME_PREFIX + "JDA Gateway")
|
||||
@ -252,15 +295,70 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
);
|
||||
|
||||
BotConfig botConfig = discordSRV.connectionConfig().bot;
|
||||
DiscordConnectionDetails connectionDetails = discordSRV.discordConnectionDetails();
|
||||
Set<GatewayIntent> intents = connectionDetails.getGatewayIntents();
|
||||
boolean membersIntent = intents.contains(GatewayIntent.GUILD_MEMBERS);
|
||||
MemberCachingConfig memberCachingConfig = discordSRV.config().memberCaching;
|
||||
DiscordConnectionDetailsImpl connectionDetails = discordSRV.discordConnectionDetails();
|
||||
|
||||
Set<GatewayIntent> intents = new LinkedHashSet<>();
|
||||
this.intents.clear();
|
||||
this.intents.addAll(connectionDetails.getGatewayIntents());
|
||||
this.intents.forEach(intent -> intents.add(intent.asJDA()));
|
||||
|
||||
Set<CacheFlag> cacheFlags = new LinkedHashSet<>();
|
||||
this.cacheFlags.clear();
|
||||
this.cacheFlags.addAll(connectionDetails.getCacheFlags());
|
||||
this.cacheFlags.forEach(flag -> {
|
||||
cacheFlags.add(flag.asJDA());
|
||||
DiscordGatewayIntent intent = flag.requiredIntent();
|
||||
if (intent != null) {
|
||||
intents.add(intent.asJDA());
|
||||
}
|
||||
});
|
||||
|
||||
this.memberCachePolicies.clear();
|
||||
this.memberCachePolicies.addAll(connectionDetails.getMemberCachePolicies());
|
||||
if (memberCachingConfig.all || this.memberCachePolicies.contains(DiscordMemberCachePolicy.ALL)) {
|
||||
this.memberCachePolicies.clear();
|
||||
this.memberCachePolicies.add(DiscordMemberCachePolicy.ALL);
|
||||
} else if (memberCachingConfig.linkedUsers) {
|
||||
this.memberCachePolicies.add(DiscordMemberCachePolicy.LINKED);
|
||||
}
|
||||
for (DiscordMemberCachePolicy policy : this.memberCachePolicies) {
|
||||
if (policy != DiscordMemberCachePolicy.OWNER && policy != DiscordMemberCachePolicy.VOICE) {
|
||||
this.intents.add(DiscordGatewayIntent.GUILD_MEMBERS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkingFilter chunkingFilter;
|
||||
if (memberCachingConfig.chunk) {
|
||||
MemberCachingConfig.GuildFilter servers = memberCachingConfig.chunkingServerFilter;
|
||||
long[] ids = servers.ids.stream().mapToLong(l -> l).toArray();
|
||||
if (servers.blacklist) {
|
||||
chunkingFilter = ChunkingFilter.exclude(ids);
|
||||
} else {
|
||||
chunkingFilter = ChunkingFilter.include(ids);
|
||||
}
|
||||
} else {
|
||||
chunkingFilter = ChunkingFilter.NONE;
|
||||
}
|
||||
|
||||
// Start with everything disabled & enable stuff that we actually need
|
||||
JDABuilder jdaBuilder = JDABuilder.createLight(botConfig.token, intents);
|
||||
jdaBuilder.enableCache(connectionDetails.getCacheFlags());
|
||||
jdaBuilder.setMemberCachePolicy(membersIntent ? MemberCachePolicy.ALL : MemberCachePolicy.OWNER);
|
||||
jdaBuilder.setChunkingFilter(membersIntent ? ChunkingFilter.ALL : ChunkingFilter.NONE);
|
||||
jdaBuilder.enableCache(cacheFlags);
|
||||
jdaBuilder.setMemberCachePolicy(member -> {
|
||||
if (this.memberCachePolicies.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DiscordGuildMember guildMember = api().getGuildMember(member);
|
||||
for (DiscordMemberCachePolicy memberCachePolicy : this.memberCachePolicies) {
|
||||
if (memberCachePolicy.isCached(guildMember)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
jdaBuilder.setChunkingFilter(chunkingFilter);
|
||||
|
||||
// We shut down JDA ourselves. Doing it at the JVM's shutdown may cause errors due to classloading
|
||||
jdaBuilder.setEnableShutdownHook(false);
|
||||
@ -310,7 +408,6 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
|
||||
@SuppressWarnings("BusyWait")
|
||||
private void shutdownInternal(long timeoutMillis) {
|
||||
detailsAccepted = true;
|
||||
if (instance == null) {
|
||||
shutdownExecutors();
|
||||
return;
|
||||
@ -413,15 +510,15 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
if (closeCode == null) {
|
||||
return false;
|
||||
} else if (closeCode == CloseCode.DISALLOWED_INTENTS) {
|
||||
Set<GatewayIntent> intents = discordSRV.discordConnectionDetails().getGatewayIntents();
|
||||
Set<DiscordGatewayIntent> intents = getIntents();
|
||||
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);
|
||||
for (DiscordGatewayIntent intent : intents) {
|
||||
String displayName = intent.portalName();
|
||||
if (displayName != null) {
|
||||
discordSRV.logger().error("| " + displayName);
|
||||
}
|
||||
@ -434,8 +531,9 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
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("| 5. Run the \"/discordsrv reload config discord_connection\" command");
|
||||
discordSRV.logger().error("+-------------------------------------->");
|
||||
discordSRV.setStatus(DiscordSRVApi.Status.FAILED_TO_CONNECT);
|
||||
return true;
|
||||
} else if (closeCode == CloseCode.AUTHENTICATION_FAILED) {
|
||||
invalidToken();
|
||||
@ -453,9 +551,15 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
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("| by selecting the application, going to the \"Bot\" tab");
|
||||
discordSRV.logger().error("| and clicking on \"Reset Token\"");
|
||||
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("|");
|
||||
discordSRV.logger().error("| Once the token is corrected in the " + ConnectionConfig.FILE_NAME);
|
||||
discordSRV.logger().error("| Run the \"/discordsrv reload config discord_connection\" command");
|
||||
discordSRV.logger().error("+------------------------------>");
|
||||
discordSRV.setStatus(DiscordSRVApi.Status.FAILED_TO_CONNECT);
|
||||
}
|
||||
|
||||
private class FailureCallback implements Consumer<Throwable> {
|
||||
|
@ -18,11 +18,12 @@
|
||||
|
||||
package com.discordsrv.common.discord.details;
|
||||
|
||||
import com.discordsrv.api.discord.connection.jda.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordConnectionDetails;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.exception.util.ExceptionUtil;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
@ -30,58 +31,52 @@ import java.util.*;
|
||||
public class DiscordConnectionDetailsImpl implements DiscordConnectionDetails {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final Set<GatewayIntent> gatewayIntents = new HashSet<>();
|
||||
private final Set<CacheFlag> cacheFlags = new HashSet<>();
|
||||
private final Set<DiscordGatewayIntent> gatewayIntents = new HashSet<>();
|
||||
private final Set<DiscordCacheFlag> cacheFlags = new HashSet<>();
|
||||
private final Set<DiscordMemberCachePolicy> memberCachePolicies = new HashSet<>();
|
||||
|
||||
public DiscordConnectionDetailsImpl(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.memberCachePolicies.add(DiscordMemberCachePolicy.OWNER);
|
||||
}
|
||||
|
||||
private boolean isStatus() {
|
||||
return discordSRV.status() == DiscordSRV.Status.INITIALIZED
|
||||
|| discordSRV.status() == DiscordSRV.Status.ATTEMPTING_TO_CONNECT;
|
||||
}
|
||||
|
||||
public @NotNull Set<DiscordGatewayIntent> getGatewayIntents() {
|
||||
Set<DiscordGatewayIntent> intents = new HashSet<>(gatewayIntents);
|
||||
intents.addAll(discordSRV.moduleManager().requiredIntents());
|
||||
return intents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readyToTakeDetails() {
|
||||
return discordSRV.discordConnectionManager().areDetailsAccepted();
|
||||
}
|
||||
|
||||
private void check() {
|
||||
if (!readyToTakeDetails()) {
|
||||
throw new IllegalStateException("Too late. Please use DiscordConnectionDetails#readyToTakeDetails " +
|
||||
"to check if the method can be used");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<GatewayIntent> getGatewayIntents() {
|
||||
return gatewayIntents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestGatewayIntent(@NotNull GatewayIntent gatewayIntent, GatewayIntent... gatewayIntents) {
|
||||
check();
|
||||
|
||||
List<GatewayIntent> intents = new ArrayList<>(Collections.singleton(gatewayIntent));
|
||||
public boolean requestGatewayIntent(@NotNull DiscordGatewayIntent gatewayIntent, DiscordGatewayIntent... gatewayIntents) {
|
||||
List<DiscordGatewayIntent> intents = new ArrayList<>(Collections.singleton(gatewayIntent));
|
||||
intents.addAll(Arrays.asList(gatewayIntents));
|
||||
|
||||
this.gatewayIntents.addAll(intents);
|
||||
return isStatus();
|
||||
}
|
||||
|
||||
public @NotNull Set<DiscordCacheFlag> getCacheFlags() {
|
||||
Set<DiscordCacheFlag> flags = new HashSet<>(cacheFlags);
|
||||
flags.addAll(discordSRV.moduleManager().requiredCacheFlags());
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<CacheFlag> getCacheFlags() {
|
||||
return cacheFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCacheFlag(@NotNull CacheFlag cacheFlag, CacheFlag... cacheFlags) {
|
||||
check();
|
||||
|
||||
List<CacheFlag> flags = new ArrayList<>(Collections.singleton(cacheFlag));
|
||||
public boolean requestCacheFlag(@NotNull DiscordCacheFlag cacheFlag, DiscordCacheFlag... cacheFlags) {
|
||||
List<DiscordCacheFlag> flags = new ArrayList<>(Collections.singleton(cacheFlag));
|
||||
flags.addAll(Arrays.asList(cacheFlags));
|
||||
|
||||
List<Throwable> suppressed = new ArrayList<>();
|
||||
for (CacheFlag flag : flags) {
|
||||
GatewayIntent requiredIntent = flag.getRequiredIntent();
|
||||
for (DiscordCacheFlag flag : flags) {
|
||||
DiscordGatewayIntent requiredIntent = flag.requiredIntent();
|
||||
if (requiredIntent != null && !gatewayIntents.contains(requiredIntent)) {
|
||||
suppressed.add(ExceptionUtil.minifyException(new IllegalArgumentException("CacheFlag "
|
||||
+ flag.getRequiredIntent().name() + " requires GatewayIntent " + requiredIntent.name())));
|
||||
+ requiredIntent.name() + " requires GatewayIntent " + requiredIntent.name())));
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,5 +87,21 @@ public class DiscordConnectionDetailsImpl implements DiscordConnectionDetails {
|
||||
}
|
||||
|
||||
this.cacheFlags.addAll(flags);
|
||||
return isStatus();
|
||||
}
|
||||
|
||||
public @NotNull Set<DiscordMemberCachePolicy> getMemberCachePolicies() {
|
||||
Set<DiscordMemberCachePolicy> policies = new HashSet<>(memberCachePolicies);
|
||||
policies.addAll(discordSRV.moduleManager().requiredMemberCachePolicies());
|
||||
return policies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestMemberCachePolicy(@NotNull DiscordMemberCachePolicy memberCachePolicy, @NotNull DiscordMemberCachePolicy... memberCachePolicies) {
|
||||
List<DiscordMemberCachePolicy> policies = new ArrayList<>(Collections.singleton(memberCachePolicy));
|
||||
policies.addAll(Arrays.asList(memberCachePolicies));
|
||||
|
||||
this.memberCachePolicies.addAll(policies);
|
||||
return isStatus();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
package com.discordsrv.common.function;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CheckedRunnable {
|
||||
public interface CheckedRunnable<T> {
|
||||
|
||||
void run() throws Throwable;
|
||||
T run() throws Throwable;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ public class GroupSyncModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
public void reloadNoResult() {
|
||||
synchronized (pairs) {
|
||||
pairs.values().forEach(future -> {
|
||||
if (future != null) {
|
||||
|
@ -18,7 +18,9 @@
|
||||
|
||||
package com.discordsrv.common.invite;
|
||||
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.placeholder.FormattedText;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
@ -29,7 +31,13 @@ import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.attribute.IInviteContainer;
|
||||
import net.dv8tion.jda.api.events.guild.invite.GuildInviteDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.guild.update.GuildUpdateVanityCodeEvent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DiscordInviteModule extends AbstractModule<DiscordSRV> {
|
||||
@ -43,7 +51,29 @@ public class DiscordInviteModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
public @NotNull Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
DiscordInviteConfig config = discordSRV.config().invite;
|
||||
if (StringUtils.isNotEmpty(config.inviteUrl)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.singleton(DiscordGatewayIntent.GUILD_INVITES);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGuildInviteDelete(GuildInviteDeleteEvent event) {
|
||||
if (invite.equals(event.getUrl())) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGuildUpdateVanityCode(GuildUpdateVanityCodeEvent event) {
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadNoResult() {
|
||||
JDA jda = discordSRV.jda();
|
||||
if (jda == null) {
|
||||
return;
|
||||
@ -53,7 +83,7 @@ public class DiscordInviteModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
// Manual
|
||||
String invite = config.inviteUrl;
|
||||
if (invite != null && !invite.isEmpty()) {
|
||||
if (StringUtils.isNotEmpty(invite)) {
|
||||
this.invite = invite;
|
||||
return;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface LinkStore {
|
||||
public interface LinkStore extends LinkProvider {
|
||||
|
||||
CompletableFuture<Void> createLink(@NotNull UUID playerUUID, long userId);
|
||||
|
||||
|
@ -21,6 +21,7 @@ package com.discordsrv.common.messageforwarding.discord;
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.component.GameTextBuilder;
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
@ -39,7 +40,10 @@ import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
|
||||
@ -48,6 +52,21 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
|
||||
super(discordSRV, new NamedLogger(discordSRV, "DISCORD_TO_MINECRAFT"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
for (OrDefault<BaseChannelConfig> config : discordSRV.channelConfig().getAllChannels()) {
|
||||
if (config.map(cfg -> cfg.discordToMinecraft).get(cfg -> cfg.enabled, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
return Arrays.asList(DiscordGatewayIntent.GUILD_MESSAGES, DiscordGatewayIntent.MESSAGE_CONTENT);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDiscordMessageReceived(DiscordMessageReceiveEvent event) {
|
||||
if (!discordSRV.isReady() || event.getMessage().isFromSelf()
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common.messageforwarding.discord;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
@ -47,6 +48,7 @@ import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@ -67,6 +69,21 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
for (OrDefault<BaseChannelConfig> config : discordSRV.channelConfig().getAllChannels()) {
|
||||
if (config.map(cfg -> cfg.mirroring).get(cfg -> cfg.enabled, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
return Arrays.asList(DiscordGatewayIntent.GUILD_MESSAGES, DiscordGatewayIntent.MESSAGE_CONTENT);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDiscordChatMessageProcessing(DiscordChatMessageProcessingEvent event) {
|
||||
if (checkCancellation(event)) {
|
||||
|
@ -55,6 +55,16 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
|
||||
super(discordSRV, new NamedLogger(discordSRV, loggerName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
for (OrDefault<BaseChannelConfig> channelConfig : discordSRV.channelConfig().getAllChannels()) {
|
||||
if (mapConfig(channelConfig).get(IMessageConfig::enabled, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public OrDefault<T> mapConfig(E event, OrDefault<BaseChannelConfig> channelConfig) {
|
||||
return mapConfig(channelConfig);
|
||||
}
|
||||
|
@ -18,12 +18,21 @@
|
||||
|
||||
package com.discordsrv.common.messageforwarding.game;
|
||||
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
|
||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
|
||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageReceiveEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.channels.StartMessageConfig;
|
||||
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class StartMessageModule extends AbstractGameMessageModule<StartMessageConfig, AbstractGameMessageReceiveEvent> {
|
||||
|
||||
@ -31,6 +40,11 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
|
||||
super(discordSRV, "START_MESSAGE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrDefault<StartMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
|
||||
return channelConfig.map(cfg -> cfg.startMessage);
|
||||
@ -39,6 +53,21 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
|
||||
@Override
|
||||
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
||||
|
||||
@Override
|
||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
|
||||
OrDefault<StartMessageConfig> config,
|
||||
SendableDiscordMessage.Builder format,
|
||||
List<DiscordMessageChannel> channels,
|
||||
String message,
|
||||
IPlayer player,
|
||||
Object... context
|
||||
) {
|
||||
if (!config.get(cfg -> cfg.enabled, false)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return super.sendMessageToChannels(config, format, channels, message, player, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
process(null, null, null);
|
||||
|
@ -18,13 +18,21 @@
|
||||
|
||||
package com.discordsrv.common.messageforwarding.game;
|
||||
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
|
||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
|
||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageReceiveEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.channels.StopMessageConfig;
|
||||
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@ -35,6 +43,11 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
|
||||
super(discordSRV, "START_MESSAGE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrDefault<StopMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
|
||||
return channelConfig.map(cfg -> cfg.stopMessage);
|
||||
@ -43,6 +56,21 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
|
||||
@Override
|
||||
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
||||
|
||||
@Override
|
||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
|
||||
OrDefault<StopMessageConfig> config,
|
||||
SendableDiscordMessage.Builder format,
|
||||
List<DiscordMessageChannel> channels,
|
||||
String message,
|
||||
IPlayer player,
|
||||
Object... context
|
||||
) {
|
||||
if (!config.get(cfg -> cfg.enabled, false)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return super.sendMessageToChannels(config, format, channels, message, player, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
try {
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
package com.discordsrv.common.module;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy;
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
@ -25,18 +29,18 @@ import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.debug.DebugGenerateEvent;
|
||||
import com.discordsrv.common.debug.file.TextDebugFile;
|
||||
import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;
|
||||
import com.discordsrv.common.function.CheckedFunction;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import com.discordsrv.common.module.type.ModuleDelegate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ModuleManager {
|
||||
|
||||
@ -56,6 +60,37 @@ public class ModuleManager {
|
||||
return logger;
|
||||
}
|
||||
|
||||
private <T> Set<T> getModuleDetails(Function<Module, Collection<T>> detailFunction, BiConsumer<AbstractModule<?>, Collection<T>> setRequested) {
|
||||
Set<T> details = new HashSet<>();
|
||||
for (Module module : modules) {
|
||||
try {
|
||||
if (!module.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Collection<T> values = detailFunction.apply(module);
|
||||
details.addAll(values);
|
||||
|
||||
setRequested.accept(getAbstract(module), values);
|
||||
} catch (Throwable t) {
|
||||
logger.debug("Failed to get details from " + module.getClass(), t);
|
||||
}
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
public Set<DiscordGatewayIntent> requiredIntents() {
|
||||
return getModuleDetails(Module::requiredIntents, AbstractModule::setRequestedIntents);
|
||||
}
|
||||
|
||||
public Set<DiscordCacheFlag> requiredCacheFlags() {
|
||||
return getModuleDetails(Module::requiredCacheFlags, AbstractModule::setRequestedCacheFlags);
|
||||
}
|
||||
|
||||
public Set<DiscordMemberCachePolicy> requiredMemberCachePolicies() {
|
||||
return getModuleDetails(Module::requiredMemberCachingPolicies, (mod, result) -> mod.setRequestedMemberCachePolicies(result.size()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Module> T getModule(Class<T> moduleType) {
|
||||
return (T) moduleLookupTable.computeIfAbsent(moduleType.getName(), key -> {
|
||||
@ -94,18 +129,32 @@ public class ModuleManager {
|
||||
throw new IllegalArgumentException("Cannot register a delegate");
|
||||
}
|
||||
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
logger.debug(module + " registered");
|
||||
this.modules.add(module);
|
||||
this.moduleLookupTable.put(module.getClass().getName(), module);
|
||||
|
||||
if (discordSRV.config() != null) {
|
||||
// Check if config is ready, if it is already we'll enable the module
|
||||
enable(abstractModule);
|
||||
logger.debug(module + " registered");
|
||||
|
||||
if (discordSRV.isReady()) {
|
||||
// Check if Discord connection is ready, if it is already we'll enable the module
|
||||
enable(getAbstract(module));
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Module module) {
|
||||
if (module instanceof ModuleDelegate) {
|
||||
throw new IllegalArgumentException("Cannot unregister a delegate");
|
||||
}
|
||||
|
||||
// Disable if needed
|
||||
disable(getAbstract(module));
|
||||
|
||||
this.modules.remove(module);
|
||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||
this.delegates.remove(module);
|
||||
|
||||
logger.debug(module + " unregistered");
|
||||
}
|
||||
|
||||
private void enable(AbstractModule<?> module) {
|
||||
try {
|
||||
if (module.enableModule()) {
|
||||
@ -116,28 +165,13 @@ public class ModuleManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Module module) {
|
||||
if (module instanceof ModuleDelegate) {
|
||||
throw new IllegalArgumentException("Cannot unregister a delegate");
|
||||
}
|
||||
|
||||
if (getAbstract(module).hasBeenEnabled()) {
|
||||
disable(module);
|
||||
}
|
||||
|
||||
this.modules.remove(module);
|
||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||
this.delegates.remove(module);
|
||||
}
|
||||
|
||||
private void disable(Module module) {
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
private void disable(AbstractModule<?> module) {
|
||||
try {
|
||||
logger.debug(module + " disabling");
|
||||
discordSRV.eventBus().unsubscribe(abstractModule);
|
||||
abstractModule.disable();
|
||||
if (module.disableModule()) {
|
||||
logger.debug(module + " disabled");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to disable " + abstractModule.toString(), t);
|
||||
discordSRV.logger().error("Failed to disable " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,22 +179,64 @@ public class ModuleManager {
|
||||
public void onShuttingDown(DiscordSRVShuttingDownEvent event) {
|
||||
modules.stream()
|
||||
.sorted((m1, m2) -> Integer.compare(m2.shutdownOrder(), m1.shutdownOrder()))
|
||||
.forEachOrdered(this::disable);
|
||||
.forEachOrdered(module -> disable(getAbstract(module)));
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
public List<DiscordSRV.ReloadResult> reload() {
|
||||
JDAConnectionManager connectionManager = discordSRV.discordConnectionManager();
|
||||
|
||||
Set<DiscordSRVApi.ReloadResult> reloadResults = new HashSet<>();
|
||||
for (Module module : modules) {
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
// Check if the module needs to be enabled due to reload
|
||||
enable(abstractModule);
|
||||
boolean fail = false;
|
||||
if (abstractModule.isEnabled()) {
|
||||
for (DiscordGatewayIntent requiredIntent : abstractModule.getRequestedIntents()) {
|
||||
if (!connectionManager.getIntents().contains(requiredIntent)) {
|
||||
fail = true;
|
||||
logger().warning("Missing gateway intent " + requiredIntent.name() + " for module " + module.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
for (DiscordCacheFlag requiredCacheFlag : abstractModule.getRequestedCacheFlags()) {
|
||||
if (!connectionManager.getCacheFlags().contains(requiredCacheFlag)) {
|
||||
fail = true;
|
||||
logger().warning("Missing cache flag " + requiredCacheFlag.name() + " for module " + module.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
reloadResults.add(DiscordSRV.ReloadResults.DISCORD_CONNECTION_RELOAD_REQUIRED);
|
||||
}
|
||||
|
||||
// Check if the module needs to be enabled or disabled
|
||||
if (!fail) {
|
||||
enable(abstractModule);
|
||||
}
|
||||
if (!abstractModule.isEnabled()) {
|
||||
disable(abstractModule);
|
||||
}
|
||||
|
||||
try {
|
||||
abstractModule.reload();
|
||||
Set<DiscordSRVApi.ReloadResult> results = abstractModule.reload();
|
||||
if (results != null) {
|
||||
reloadResults.addAll(results);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to reload " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
List<DiscordSRVApi.ReloadResult> results = new ArrayList<>();
|
||||
|
||||
List<DiscordSRV.ReloadResult> validResults = Arrays.asList(DiscordSRVApi.ReloadResult.Results.values());
|
||||
for (DiscordSRVApi.ReloadResult reloadResult : reloadResults) {
|
||||
if (validResults.contains(reloadResult)) {
|
||||
results.add(reloadResult);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -170,24 +246,44 @@ public class ModuleManager {
|
||||
builder.append("Enabled modules:");
|
||||
List<Module> disabled = new ArrayList<>();
|
||||
for (Module module : modules) {
|
||||
AbstractModule<?> abstractModule = getAbstract(module);
|
||||
|
||||
if (!abstractModule.isEnabled()) {
|
||||
disabled.add(abstractModule);
|
||||
if (!getAbstract(module).isEnabled()) {
|
||||
disabled.add(module);
|
||||
continue;
|
||||
}
|
||||
appendModule(builder, abstractModule);
|
||||
|
||||
appendModule(builder, module, true);
|
||||
}
|
||||
|
||||
builder.append("\n\nDisabled modules:");
|
||||
for (Module module : disabled) {
|
||||
appendModule(builder, module);
|
||||
appendModule(builder, module, false);
|
||||
}
|
||||
|
||||
event.addFile(new TextDebugFile("modules.txt", builder));
|
||||
}
|
||||
|
||||
private void appendModule(StringBuilder builder, Module module) {
|
||||
private void appendModule(StringBuilder builder, Module module, boolean extra) {
|
||||
builder.append('\n').append(module.getClass().getName());
|
||||
|
||||
if (!extra) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractModule<?> mod = getAbstract(module);
|
||||
|
||||
List<DiscordGatewayIntent> intents = mod.getRequestedIntents();
|
||||
if (!intents.isEmpty()) {
|
||||
builder.append("\n Intents: ").append(intents);
|
||||
}
|
||||
|
||||
List<DiscordCacheFlag> cacheFlags = mod.getRequestedCacheFlags();
|
||||
if (!cacheFlags.isEmpty()) {
|
||||
builder.append("\n Cache Flags: ").append(cacheFlags);
|
||||
}
|
||||
|
||||
int memberCachePolicies = mod.getRequestedMemberCachePolicies();
|
||||
if (memberCachePolicies != 0) {
|
||||
builder.append("\n Member Cache Policies: ").append(memberCachePolicies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
package com.discordsrv.common.module.type;
|
||||
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.event.events.Cancellable;
|
||||
import com.discordsrv.api.event.events.Processable;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
@ -25,12 +27,20 @@ import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.event.util.EventUtil;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
||||
|
||||
protected final DT discordSRV;
|
||||
private final Logger logger;
|
||||
private boolean hasBeenEnabled = false;
|
||||
|
||||
private final List<DiscordGatewayIntent> requestedIntents = new ArrayList<>();
|
||||
private final List<DiscordCacheFlag> requestedCacheFlags = new ArrayList<>();
|
||||
private int requestedMemberCachePolicies = 0;
|
||||
|
||||
public AbstractModule(DT discordSRV) {
|
||||
this(discordSRV, discordSRV.logger());
|
||||
}
|
||||
@ -40,14 +50,27 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
// Utility
|
||||
|
||||
public final Logger logger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public final boolean hasBeenEnabled() {
|
||||
return hasBeenEnabled;
|
||||
protected final boolean checkProcessor(Processable event) {
|
||||
return EventUtil.checkProcessor(discordSRV, event, logger());
|
||||
}
|
||||
|
||||
protected final boolean checkCancellation(Cancellable event) {
|
||||
return EventUtil.checkCancellation(discordSRV, event, logger());
|
||||
}
|
||||
|
||||
// Internal
|
||||
|
||||
public final boolean enableModule() {
|
||||
if (hasBeenEnabled || !isEnabled()) {
|
||||
return false;
|
||||
@ -63,17 +86,44 @@ public abstract class AbstractModule<DT extends DiscordSRV> implements Module {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName();
|
||||
public final boolean disableModule() {
|
||||
if (!hasBeenEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
disable();
|
||||
hasBeenEnabled = false;
|
||||
|
||||
try {
|
||||
discordSRV.eventBus().unsubscribe(this);
|
||||
// Ignore not having listener methods exception
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Utility
|
||||
protected final boolean checkProcessor(Processable event) {
|
||||
return EventUtil.checkProcessor(discordSRV, event, logger());
|
||||
public final void setRequestedIntents(Collection<DiscordGatewayIntent> intents) {
|
||||
this.requestedIntents.clear();
|
||||
this.requestedIntents.addAll(intents);
|
||||
}
|
||||
|
||||
protected final boolean checkCancellation(Cancellable event) {
|
||||
return EventUtil.checkCancellation(discordSRV, event, logger());
|
||||
public final List<DiscordGatewayIntent> getRequestedIntents() {
|
||||
return requestedIntents;
|
||||
}
|
||||
|
||||
public final void setRequestedCacheFlags(Collection<DiscordCacheFlag> cacheFlags) {
|
||||
this.requestedCacheFlags.clear();
|
||||
this.requestedCacheFlags.addAll(cacheFlags);
|
||||
}
|
||||
|
||||
public final List<DiscordCacheFlag> getRequestedCacheFlags() {
|
||||
return requestedCacheFlags;
|
||||
}
|
||||
|
||||
public final void setRequestedMemberCachePolicies(int amount) {
|
||||
this.requestedMemberCachePolicies = amount;
|
||||
}
|
||||
|
||||
public final int getRequestedMemberCachePolicies() {
|
||||
return requestedMemberCachePolicies;
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,17 @@
|
||||
|
||||
package com.discordsrv.common.module.type;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordCacheFlag;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
|
||||
import com.discordsrv.api.discord.connection.details.DiscordMemberCachePolicy;
|
||||
import com.discordsrv.api.module.type.Module;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModuleDelegate extends AbstractModule<DiscordSRV> {
|
||||
|
||||
@ -36,14 +44,39 @@ public class ModuleDelegate extends AbstractModule<DiscordSRV> {
|
||||
return module.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<DiscordGatewayIntent> requiredIntents() {
|
||||
return module.requiredIntents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<DiscordCacheFlag> requiredCacheFlags() {
|
||||
return module.requiredCacheFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<DiscordMemberCachePolicy> requiredMemberCachingPolicies() {
|
||||
return module.requiredMemberCachingPolicies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int priority(Class<?> type) {
|
||||
return module.priority(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int shutdownOrder() {
|
||||
return module.shutdownOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
module.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
module.reload();
|
||||
public Set<DiscordSRVApi.ReloadResult> reload() {
|
||||
return module.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,7 +86,6 @@ public class ModuleDelegate extends AbstractModule<DiscordSRV> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String original = super.toString();
|
||||
return original.substring(0, original.length() - 1) + ",module=" + module.getClass().getName() + "(" + module + ")}";
|
||||
return super.toString() + "{module=" + module.getClass().getName() + "(" + module + ")}";
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common.module.type;
|
||||
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||
|
||||
@ -28,6 +29,10 @@ public abstract class PluginIntegration<DT extends DiscordSRV> extends AbstractM
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
public PluginIntegration(DT discordSRV, Logger logger) {
|
||||
super(discordSRV, logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
public boolean isEnabled() {
|
||||
|
@ -33,7 +33,7 @@ dependencyResolutionManagement {
|
||||
library('velocity', 'com.velocitypowered', 'velocity-api').version('3.0.0')
|
||||
|
||||
// DependencyDownload
|
||||
version('dependencydownload', '1.2.2-SNAPSHOT')
|
||||
version('dependencydownload', '1.3.1-SNAPSHOT')
|
||||
plugin('dependencydownload-plugin', 'dev.vankka.dependencydownload.plugin').versionRef('dependencydownload')
|
||||
library('dependencydownload-runtime', 'dev.vankka', 'dependencydownload-runtime').versionRef('dependencydownload')
|
||||
library('dependencydownload-jarinjar-bootstrap', 'dev.vankka', 'dependencydownload-jarinjar-bootstrap').versionRef('dependencydownload')
|
||||
|
Loading…
Reference in New Issue
Block a user