diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index a860fdb99f0..ff4ce284f00 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -27,7 +27,7 @@ jobs: with: arguments: build - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Artifacts path: build/libs/*.jar diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1777c7a1907..b12cfa7edd2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,7 +34,7 @@ jobs: with: arguments: javadoc - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: './build/docs/javadoc' - name: Deploy to GitHub Pages diff --git a/build.gradle.kts b/build.gradle.kts index 0d3d18f3da4..f7745dfc224 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,7 +40,7 @@ plugins { } val javaVersion = JavaVersion.current() -val versionObj = Version(major = "5", minor = "0", revision = "0", classifier = "beta.17") +val versionObj = Version(major = "5", minor = "0", revision = "0", classifier = "beta.20") val isCI = System.getProperty("BUILD_NUMBER") != null // jenkins || System.getenv("BUILD_NUMBER") != null || System.getProperty("GIT_COMMIT") != null // jitpack @@ -105,7 +105,7 @@ dependencies { //Web Connection Support api("com.neovisionaries:nv-websocket-client:2.14") - api("com.squareup.okhttp3:okhttp:4.10.0") + api("com.squareup.okhttp3:okhttp:4.12.0") //Opus library support api("club.minnced:opus-java:1.1.1") @@ -122,9 +122,8 @@ dependencies { //General Utility implementation("net.sf.trove4j:trove4j:3.0.3") - // Match the minor version of lavaplayers jackson dependency - implementation("com.fasterxml.jackson.core:jackson-core:2.14.1") - implementation("com.fasterxml.jackson.core:jackson-databind:2.14.1") + implementation("com.fasterxml.jackson.core:jackson-core:2.16.0") + implementation("com.fasterxml.jackson.core:jackson-databind:2.16.0") //Sets the dependencies for the examples configurations["examplesImplementation"].withDependencies { @@ -133,8 +132,9 @@ dependencies { addAll(configurations["compileOnly"].allDependencies) } - testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") testImplementation("org.reflections:reflections:0.10.2") + testImplementation("org.mockito:mockito-core:5.8.0") } val compileJava: JavaCompile by tasks diff --git a/src/main/java/net/dv8tion/jda/api/EmbedBuilder.java b/src/main/java/net/dv8tion/jda/api/EmbedBuilder.java index 16b83d98c85..7943e975f22 100644 --- a/src/main/java/net/dv8tion/jda/api/EmbedBuilder.java +++ b/src/main/java/net/dv8tion/jda/api/EmbedBuilder.java @@ -155,7 +155,15 @@ public static EmbedBuilder fromData(@Nonnull DataObject data) * that has been checked as being valid for sending. * * @throws java.lang.IllegalStateException - * If the embed is empty. Can be checked with {@link #isEmpty()}. + * * * @return the built, sendable {@link net.dv8tion.jda.api.entities.MessageEmbed} */ @@ -167,7 +175,9 @@ public MessageEmbed build() if (description.length() > MessageEmbed.DESCRIPTION_MAX_LENGTH) throw new IllegalStateException(Helpers.format("Description is longer than %d! Please limit your input!", MessageEmbed.DESCRIPTION_MAX_LENGTH)); if (length() > MessageEmbed.EMBED_MAX_LENGTH_BOT) - throw new IllegalStateException("Cannot build an embed with more than " + MessageEmbed.EMBED_MAX_LENGTH_BOT + " characters!"); + throw new IllegalStateException(Helpers.format("Cannot build an embed with more than %d characters!", MessageEmbed.EMBED_MAX_LENGTH_BOT)); + if (fields.size() > MessageEmbed.MAX_FIELD_AMOUNT) + throw new IllegalStateException(Helpers.format("Cannot build an embed with more than %d embed fields set!", MessageEmbed.MAX_FIELD_AMOUNT)); final String description = this.description.length() < 1 ? null : this.description.toString(); return EntityBuilder.createMessageEmbed(url, title, description, EmbedType.RICH, timestamp, diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index 30b18a5bcac..48581126ae5 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -71,7 +71,7 @@ * * @see JDABuilder */ -public interface JDA extends IGuildChannelContainer +public interface JDA extends IGuildChannelContainer { /** * Represents the connection status of JDA and its Main WebSocket. @@ -1478,17 +1478,6 @@ default List getScheduledEventsByName(@Nonnull String name, bool return getScheduledEventCache().getElementsByName(name, ignoreCase); } - @Nullable - @Override - default T getChannelById(@Nonnull Class type, long id) - { - Checks.notNull(type, "Class"); - Channel channel = getPrivateChannelById(id); - if (channel != null) - return type.isInstance(channel) ? type.cast(channel) : null; - return IGuildChannelContainer.super.getChannelById(type, id); - } - /** * {@link net.dv8tion.jda.api.utils.cache.SnowflakeCacheView SnowflakeCacheView} of * all cached {@link PrivateChannel PrivateChannels} visible to this JDA session. diff --git a/src/main/java/net/dv8tion/jda/api/JDABuilder.java b/src/main/java/net/dv8tion/jda/api/JDABuilder.java index fea73990d17..170ee796077 100644 --- a/src/main/java/net/dv8tion/jda/api/JDABuilder.java +++ b/src/main/java/net/dv8tion/jda/api/JDABuilder.java @@ -63,8 +63,10 @@ public class JDABuilder protected final List listeners = new LinkedList<>(); protected final EnumSet automaticallyDisabled = EnumSet.noneOf(CacheFlag.class); - protected ScheduledExecutorService rateLimitPool = null; - protected boolean shutdownRateLimitPool = true; + protected ScheduledExecutorService rateLimitScheduler = null; + protected boolean shutdownRateLimitScheduler = true; + protected ExecutorService rateLimitElastic = null; + protected boolean shutdownRateLimitElastic = true; protected ScheduledExecutorService mainWsPool = null; protected boolean shutdownMainWsPool = true; protected ExecutorService callbackPool = null; @@ -913,8 +915,13 @@ public JDABuilder setWebsocketFactory(@Nullable WebSocketFactory factory) * The thread-pool to use for rate-limit handling * * @return The JDABuilder instance. Useful for chaining. + * + * @deprecated This pool is now split into two pools. + * You should use {@link #setRateLimitScheduler(ScheduledExecutorService)} and {@link #setRateLimitElastic(ExecutorService)} instead. */ @Nonnull + @Deprecated + @ReplaceWith("setRateLimitScheduler(pool)") public JDABuilder setRateLimitPool(@Nullable ScheduledExecutorService pool) { return setRateLimitPool(pool, pool == null); @@ -937,12 +944,113 @@ public JDABuilder setRateLimitPool(@Nullable ScheduledExecutorService pool) * Whether {@link JDA#shutdown()} should shutdown this pool * * @return The JDABuilder instance. Useful for chaining. + * + * @deprecated This pool is now split into two pools. + * You should use {@link #setRateLimitScheduler(ScheduledExecutorService, boolean)} and {@link #setRateLimitElastic(ExecutorService, boolean)} instead. */ @Nonnull + @Deprecated + @ReplaceWith("setRateLimitScheduler(pool, automaticShutdown)") public JDABuilder setRateLimitPool(@Nullable ScheduledExecutorService pool, boolean automaticShutdown) { - this.rateLimitPool = pool; - this.shutdownRateLimitPool = automaticShutdown; + this.rateLimitScheduler = pool; + this.shutdownRateLimitScheduler = automaticShutdown; + return this; + } + + /** + * Sets the {@link ScheduledExecutorService ScheduledExecutorService} that should be used in + * the JDA rate-limit handler. Changing this can drastically change the JDA behavior for RestAction execution + * and should be handled carefully. Only change this pool if you know what you're doing. + *
This automatically disables the automatic shutdown of the rate-limit pool, you can enable + * it using {@link #setRateLimitPool(ScheduledExecutorService, boolean) setRateLimitPool(executor, true)} + * + *

This is used mostly by the Rate-Limiter to handle backoff delays by using scheduled executions. + * Besides that it is also used by planned execution for {@link net.dv8tion.jda.api.requests.RestAction#queueAfter(long, TimeUnit)} + * and similar methods. Requests are handed off to the {@link #setRateLimitElastic(ExecutorService) elastic pool} for blocking execution. + * + *

Default: {@link ScheduledThreadPoolExecutor} with 2 threads. + * + * @param pool + * The thread-pool to use for rate-limit handling + * + * @return The JDABuilder instance. Useful for chaining. + */ + @Nonnull + public JDABuilder setRateLimitScheduler(@Nullable ScheduledExecutorService pool) + { + return setRateLimitScheduler(pool, pool == null); + } + + /** + * Sets the {@link ScheduledExecutorService ScheduledExecutorService} that should be used in + * the JDA rate-limit handler. Changing this can drastically change the JDA behavior for RestAction execution + * and should be handled carefully. Only change this pool if you know what you're doing. + * + *

This is used mostly by the Rate-Limiter to handle backoff delays by using scheduled executions. + * Besides that it is also used by planned execution for {@link net.dv8tion.jda.api.requests.RestAction#queueAfter(long, TimeUnit)} + * and similar methods. Requests are handed off to the {@link #setRateLimitElastic(ExecutorService) elastic pool} for blocking execution. + * + *

Default: {@link ScheduledThreadPoolExecutor} with 2 threads. + * + * @param pool + * The thread-pool to use for rate-limit handling + * @param automaticShutdown + * Whether {@link JDA#shutdown()} should shutdown this pool + * + * @return The JDABuilder instance. Useful for chaining. + */ + @Nonnull + public JDABuilder setRateLimitScheduler(@Nullable ScheduledExecutorService pool, boolean automaticShutdown) + { + this.rateLimitScheduler = pool; + this.shutdownRateLimitScheduler = automaticShutdown; + return this; + } + + /** + * Sets the {@link ExecutorService ExecutorService} that should be used in + * the JDA request handler. Changing this can drastically change the JDA behavior for RestAction execution + * and should be handled carefully. Only change this pool if you know what you're doing. + *
This automatically disables the automatic shutdown of the rate-limit elastic pool, you can enable + * it using {@link #setRateLimitElastic(ExecutorService, boolean) setRateLimitElastic(executor, true)} + * + *

This is used mostly by the Rate-Limiter to execute the blocking HTTP requests at runtime. + * + *

Default: {@link Executors#newCachedThreadPool()}. + * + * @param pool + * The thread-pool to use for executing http requests + * + * @return The JDABuilder instance. Useful for chaining. + */ + @Nonnull + public JDABuilder setRateLimitElastic(@Nullable ExecutorService pool) + { + return setRateLimitElastic(pool, pool == null); + } + + /** + * Sets the {@link ExecutorService ExecutorService} that should be used in + * the JDA request handler. Changing this can drastically change the JDA behavior for RestAction execution + * and should be handled carefully. Only change this pool if you know what you're doing. + * + *

This is used mostly by the Rate-Limiter to execute the blocking HTTP requests at runtime. + * + *

Default: {@link Executors#newCachedThreadPool()}. + * + * @param pool + * The thread-pool to use for executing http requests + * @param automaticShutdown + * Whether {@link JDA#shutdown()} should shutdown this pool + * + * @return The JDABuilder instance. Useful for chaining. + */ + @Nonnull + public JDABuilder setRateLimitElastic(@Nullable ExecutorService pool, boolean automaticShutdown) + { + this.rateLimitElastic = pool; + this.shutdownRateLimitElastic = automaticShutdown; return this; } @@ -1797,7 +1905,8 @@ public JDA build() ThreadingConfig threadingConfig = new ThreadingConfig(); threadingConfig.setCallbackPool(callbackPool, shutdownCallbackPool); threadingConfig.setGatewayPool(mainWsPool, shutdownMainWsPool); - threadingConfig.setRateLimitPool(rateLimitPool, shutdownRateLimitPool); + threadingConfig.setRateLimitScheduler(rateLimitScheduler, shutdownRateLimitScheduler); + threadingConfig.setRateLimitElastic(rateLimitElastic, shutdownRateLimitElastic); threadingConfig.setEventPool(eventPool, shutdownEventPool); threadingConfig.setAudioPool(audioPool, shutdownAudioPool); SessionConfig sessionConfig = new SessionConfig(controller, httpClient, wsFactory, voiceDispatchInterceptor, flags, maxReconnectDelay, largeThreshold); diff --git a/src/main/java/net/dv8tion/jda/api/Permission.java b/src/main/java/net/dv8tion/jda/api/Permission.java index 313a4fbd3da..22e99f903d7 100644 --- a/src/main/java/net/dv8tion/jda/api/Permission.java +++ b/src/main/java/net/dv8tion/jda/api/Permission.java @@ -87,6 +87,7 @@ public enum Permission VOICE_START_ACTIVITIES( 39, true, true, "Use Activities"), VOICE_USE_SOUNDBOARD( 42, true, true, "Use Soundboard"), VOICE_USE_EXTERNAL_SOUNDS(45, true, true, "Use External Sounds"), + VOICE_SET_STATUS( 48, true, true, "Set Voice Channel Status"), // Stage Channel Permissions REQUEST_TO_SPEAK( 32, true, true, "Request to Speak"), diff --git a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java index 32efb8933ed..ba216cc69c8 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/ActionType.java +++ b/src/main/java/net/dv8tion/jda/api/audit/ActionType.java @@ -18,6 +18,7 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.ScheduledEvent; +import net.dv8tion.jda.api.entities.channel.attribute.IVoiceStatusChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; @@ -635,6 +636,27 @@ public enum ActionType */ AUTO_MODERATION_MEMBER_TIMEOUT( 145, TargetType.MEMBER), + /** + * A user updated the {@link IVoiceStatusChannel#getStatus() status} of a voice channel. + * + *

Possible Keys
+ *

    + *
  • {@link AuditLogKey#CHANNEL_VOICE_STATUS CHANNEL_VOICE_STATUS}
  • + *
  • {@link AuditLogKey#CHANNEL_ID CHANNEL_ID}
  • + *
+ */ + VOICE_CHANNEL_STATUS_UPDATE(192, TargetType.CHANNEL), + + /** + * A user removed the {@link IVoiceStatusChannel#getStatus() status} of a voice channel. + * + *

Possible Keys
+ *

    + *
  • {@link AuditLogKey#CHANNEL_ID CHANNEL_ID}
  • + *
+ */ + VOICE_CHANNEL_STATUS_DELETE(193, TargetType.CHANNEL), + UNKNOWN(-1, TargetType.UNKNOWN); private final int key; diff --git a/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java b/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java index e4e6831ed62..7b1522aac92 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java +++ b/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java @@ -240,6 +240,14 @@ public enum AuditLogKey */ CHANNEL_TOPIC("topic"), + /** + * Change of the {@link VoiceChannel#getStatus() VoiceChannel.getStatus()} value. + *
Only for {@link ChannelType#VOICE ChannelType.VOICE} + * + *

Expected type: String + */ + CHANNEL_VOICE_STATUS("status"), + /** * Change of the {@link ISlowmodeChannel#getSlowmode()} value. * diff --git a/src/main/java/net/dv8tion/jda/api/entities/ApplicationInfo.java b/src/main/java/net/dv8tion/jda/api/entities/ApplicationInfo.java index 2b03ba1c309..46befd6c4c7 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/ApplicationInfo.java +++ b/src/main/java/net/dv8tion/jda/api/entities/ApplicationInfo.java @@ -315,6 +315,39 @@ default String getInviteUrl(long guildId, @Nullable Permission... permissions) @Nonnull List getTags(); + /** + * A {@link java.util.List} containing the OAuth2 redirect URIs of this bot's application. + * + *

This List is empty if no redirect URIs are set in the Developer Portal. + * + * @return Immutable list containing the OAuth2 redirect URIs of this bot's application + */ + @Nonnull + List getRedirectUris(); + + /** + * The interaction endpoint URL of this bot's application. + * + *

This returns {@code null} if no interaction endpoint URL is set in the Developer Portal. + * + *

A non-null value means your bot will no longer receive {@link net.dv8tion.jda.api.interactions.Interaction interactions} + * through JDA, such as slash commands, components and modals. + * + * @return Interaction endpoint URL of this bot's application, or {@code null} if it has not been set + */ + @Nullable + String getInteractionsEndpointUrl(); + + /** + * The role connections (linked roles) verification URL of this bot's application. + * + *

This returns {@code null} if no role connection verification URL is set in the Developer Portal. + * + * @return Role connections verification URL of this bot's application, or {@code null} if it has not been set + */ + @Nullable + String getRoleConnectionsVerificationUrl(); + /** * The custom Authorization URL of this bot's application. * diff --git a/src/main/java/net/dv8tion/jda/api/entities/Guild.java b/src/main/java/net/dv8tion/jda/api/entities/Guild.java index 10af96368ba..c409d0ab6ee 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -27,6 +27,7 @@ import net.dv8tion.jda.api.entities.automod.AutoModTriggerType; import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData; import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.ICopyableChannel; import net.dv8tion.jda.api.entities.channel.attribute.IGuildChannelContainer; import net.dv8tion.jda.api.entities.channel.attribute.IInviteContainer; @@ -59,10 +60,7 @@ import net.dv8tion.jda.api.utils.FileUpload; import net.dv8tion.jda.api.utils.ImageProxy; import net.dv8tion.jda.api.utils.MiscUtil; -import net.dv8tion.jda.api.utils.cache.CacheFlag; -import net.dv8tion.jda.api.utils.cache.MemberCacheView; -import net.dv8tion.jda.api.utils.cache.SnowflakeCacheView; -import net.dv8tion.jda.api.utils.cache.SortedSnowflakeCacheView; +import net.dv8tion.jda.api.utils.cache.*; import net.dv8tion.jda.api.utils.concurrent.Task; import net.dv8tion.jda.internal.interactions.CommandDataImpl; import net.dv8tion.jda.internal.requests.DeferredRestAction; @@ -93,7 +91,7 @@ * @see JDA#getGuildsByName(String, boolean) * @see JDA#getGuilds() */ -public interface Guild extends IGuildChannelContainer, ISnowflake +public interface Guild extends IGuildChannelContainer, ISnowflake { /** Template for {@link #getIconUrl()}. */ String ICON_URL = "https://cdn.discordapp.com/icons/%s/%s.%s"; @@ -906,7 +904,7 @@ default ImageProxy getBanner() *

This will only check cached members! *
See {@link net.dv8tion.jda.api.utils.MemberCachePolicy MemberCachePolicy} * - * @return Possibly-immutable list of members who boost this guild + * @return Immutable list of members who boost this guild */ @Nonnull List getBoosters(); @@ -1561,6 +1559,23 @@ default List getScheduledEvents() @Override SortedSnowflakeCacheView getForumChannelCache(); + /** + * {@link SortedChannelCacheView SortedChannelCacheView} of {@link GuildChannel}. + * + *

Provides cache access to all channels of this guild, including thread channels (unlike {@link #getChannels()}). + * The cache view attempts to provide a sorted list, based on how channels are displayed in the client. + * Various methods like {@link SortedChannelCacheView#forEachUnordered(Consumer)} or {@link SortedChannelCacheView#lockedIterator()} + * bypass sorting for optimization reasons. + * + *

It is possible to filter the channels to more specific types using + * {@link ChannelCacheView#getElementById(ChannelType, long)} or {@link SortedChannelCacheView#ofType(Class)}. + * + * @return {@link SortedChannelCacheView SortedChannelCacheView} + */ + @Nonnull + @Override + SortedChannelCacheView getChannelCache(); + /** * Populated list of {@link GuildChannel channels} for this guild. *
This includes all types of channels, except for threads. diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index b0062f494fe..d10b6cdd8d4 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -60,6 +60,7 @@ import net.dv8tion.jda.internal.entities.ReceivedMessage; import net.dv8tion.jda.internal.requests.FunctionalCallback; import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; import net.dv8tion.jda.internal.utils.IOUtil; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; @@ -698,7 +699,7 @@ default List getActionRows() .stream() .filter(ActionRow.class::isInstance) .map(ActionRow.class::cast) - .collect(Collectors.toList()); + .collect(Helpers.toUnmodifiableList()); } /** @@ -714,7 +715,7 @@ default List