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 848a42eb88..e4e6831ed6 100644 --- a/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java +++ b/src/main/java/net/dv8tion/jda/api/audit/AuditLogKey.java @@ -321,13 +321,13 @@ public enum AuditLogKey */ CHANNEL_ID("channel_id"), -// /** -// * The {@link ForumChannel#getDefaultSortOrder()} value. -// *
Only for {@link ChannelType#FORUM}. -// * -// *

Expected type: Integer -// */ -// CHANNEL_DEFAULT_SORT_ORDER("default_sort_order"), + /** + * The {@link ForumChannel#getDefaultSortOrder()} value. + *
Only for {@link ChannelType#FORUM} and {@link ChannelType#MEDIA}. + * + *

Expected type: Integer + */ + CHANNEL_DEFAULT_SORT_ORDER("default_sort_order"), /** * The {@link ForumChannel#getDefaultLayout()} value. 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 1775542b6e..10af96368b 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -4539,6 +4539,70 @@ default ChannelAction createForumChannel(@Nonnull String name) @CheckReturnValue ChannelAction createForumChannel(@Nonnull String name, @Nullable Category parent); + /** + * Creates a new {@link MediaChannel} in this Guild. + * For this to be successful, the logged in account has to have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL MANAGE_CHANNEL} Permission. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by + * the returned {@link RestAction RestAction} include the following: + *

+ * + * @param name + * The name of the MediaChannel to create (up to {@value Channel#MAX_NAME_LENGTH} characters) + * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL} permission + * @throws IllegalArgumentException + * If the provided name is {@code null}, blank, or longer than {@value Channel#MAX_NAME_LENGTH} characters + * + * @return A specific {@link ChannelAction ChannelAction} + *
This action allows to set fields for the new MediaChannel before creating it + */ + @Nonnull + @CheckReturnValue + default ChannelAction createMediaChannel(@Nonnull String name) + { + return createMediaChannel(name, null); + } + + /** + * Creates a new {@link MediaChannel} in this Guild. + * For this to be successful, the logged in account has to have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL MANAGE_CHANNEL} Permission. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by + * the returned {@link RestAction RestAction} include the following: + *

+ * + * @param name + * The name of the MediaChannel to create (up to {@value Channel#MAX_NAME_LENGTH} characters) + * @param parent + * The optional parent category for this channel, or null + * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL} permission + * @throws IllegalArgumentException + * If the provided name is {@code null}, blank, or longer than {@value Channel#MAX_NAME_LENGTH} characters; + * or the provided parent is not in the same guild. + * + * @return A specific {@link ChannelAction ChannelAction} + *
This action allows to set fields for the new MediaChannel before creating it + */ + @Nonnull + @CheckReturnValue + ChannelAction createMediaChannel(@Nonnull String name, @Nullable Category parent); + /** * Creates a new {@link Category Category} in this Guild. * For this to be successful, the logged in account has to have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL MANAGE_CHANNEL} Permission. diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelField.java b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelField.java index 73ad561ebc..9b9ee49afa 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelField.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelField.java @@ -255,14 +255,14 @@ public enum ChannelField */ DEFAULT_FORUM_LAYOUT("default_forum_layout", AuditLogKey.DEFAULT_FORUM_LAYOUT), -// /** -// * The default sort order of a forum channel. -// * -// *

Limited to {@link ForumChannel Forum Channels}. -// * -// * @see ForumChannel#getDefaultSortOrder() -// */ -// DEFAULT_SORT_ORDER("default_sort_order", AuditLogKey.CHANNEL_DEFAULT_SORT_ORDER) + /** + * The default sort order of a forum channel. + * + *

Limited to {@link ForumChannel Forum Channels} and {@link MediaChannel Media Channels}. + * + * @see ForumChannel#getDefaultSortOrder() + */ + DEFAULT_SORT_ORDER("default_sort_order", AuditLogKey.CHANNEL_DEFAULT_SORT_ORDER) ; private final String fieldName; diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelFlag.java b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelFlag.java index a807d7174e..e889205b93 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelFlag.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelFlag.java @@ -36,7 +36,12 @@ public enum ChannelFlag /** * This is a {@link ForumChannel} which requires all new post threads to have at least one applied tag. */ - REQUIRE_TAG(1 << 4); + REQUIRE_TAG(1 << 4), + + /** + * This is a {@link net.dv8tion.jda.api.entities.channel.concrete.MediaChannel MediaChannel} which hides the copy embed option. + */ + HIDE_MEDIA_DOWNLOAD_OPTIONS(1 << 15); private final int value; diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java index 56d01dbaa8..8e3c5d76fe 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java @@ -63,6 +63,11 @@ public enum ChannelType */ FORUM(15, 0, true), + /** + * A {@link MediaChannel}, Guild-Only. + */ + MEDIA(16, 0, true), + /** * Unknown Discord channel type. Should never happen and would only possibly happen if Discord implemented a new * channel type and JDA had yet to implement support for it. diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IGuildChannelContainer.java b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IGuildChannelContainer.java index fed544b0f0..ec5a15e0c8 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IGuildChannelContainer.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IGuildChannelContainer.java @@ -178,6 +178,8 @@ default GuildChannel getGuildChannelById(long id) channel = getThreadChannelById(id); if (channel == null) channel = getForumChannelById(id); + if (channel == null) + channel = getMediaChannelById(id); return channel; } @@ -273,6 +275,8 @@ default GuildChannel getGuildChannelById(@Nonnull ChannelType type, long id) return getCategoryById(id); case FORUM: return getForumChannelById(id); + case MEDIA: + return getMediaChannelById(id); } if (type.isThread()) @@ -1101,4 +1105,118 @@ default List getForumChannels() { return getForumChannelCache().asList(); } + + + // MediaChannels + + + /** + * {@link SnowflakeCacheView SnowflakeCacheView} of {@link MediaChannel}. + * + *

This getter exists on any instance of {@link IGuildChannelContainer} and only checks the caches with the relevant scoping. + * For {@link Guild}, {@link JDA}, or {@link ShardManager}, + * this returns the relevant channel with respect to the cache within each of those objects. + * For a guild, this would mean it only returns channels within the same guild. + *
If this is called on {@link JDA} or {@link ShardManager}, this may return null immediately after building, because the cache isn't initialized yet. + * To make sure the cache is initialized after building your {@link JDA} instance, you can use {@link JDA#awaitReady()}. + * + * @return {@link SnowflakeCacheView SnowflakeCacheView} + */ + @Nonnull + SnowflakeCacheView getMediaChannelCache(); + + /** + * Gets a list of all {@link MediaChannel MediaChannels} + * in this Guild that have the same name as the one provided. + *
If there are no channels with the provided name, then this returns an empty list. + * + *

This getter exists on any instance of {@link IGuildChannelContainer} and only checks the caches with the relevant scoping. + * For {@link Guild}, {@link JDA}, or {@link ShardManager}, + * this returns the relevant channel with respect to the cache within each of those objects. + * For a guild, this would mean it only returns channels within the same guild. + *
If this is called on {@link JDA} or {@link ShardManager}, this may return null immediately after building, because the cache isn't initialized yet. + * To make sure the cache is initialized after building your {@link JDA} instance, you can use {@link JDA#awaitReady()}. + * + * @param name + * The name used to filter the returned {@link MediaChannel MediaChannels}. + * @param ignoreCase + * Determines if the comparison ignores case when comparing. True - case insensitive. + * + * @return Possibly-empty immutable list of all ForumChannel names that match the provided name. + */ + @Nonnull + default List getMediaChannelsByName(@Nonnull String name, boolean ignoreCase) + { + return getMediaChannelCache().getElementsByName(name, ignoreCase); + } + + /** + * Gets a {@link MediaChannel} that has the same id as the one provided. + *
If there is no channel with an id that matches the provided one, then this returns {@code null}. + * + *

This getter exists on any instance of {@link IGuildChannelContainer} and only checks the caches with the relevant scoping. + * For {@link Guild}, {@link JDA}, or {@link ShardManager}, + * this returns the relevant channel with respect to the cache within each of those objects. + * For a guild, this would mean it only returns channels within the same guild. + *
If this is called on {@link JDA} or {@link ShardManager}, this may return null immediately after building, because the cache isn't initialized yet. + * To make sure the cache is initialized after building your {@link JDA} instance, you can use {@link JDA#awaitReady()}. + * + * @param id + * The id of the {@link MediaChannel}. + * + * @throws java.lang.NumberFormatException + * If the provided {@code id} cannot be parsed by {@link Long#parseLong(String)} + * + * @return Possibly-null {@link MediaChannel} with matching id. + */ + @Nullable + default MediaChannel getMediaChannelById(@Nonnull String id) + { + return getMediaChannelCache().getElementById(id); + } + + /** + * Gets a {@link MediaChannel} that has the same id as the one provided. + *
If there is no channel with an id that matches the provided one, then this returns {@code null}. + * + *

This getter exists on any instance of {@link IGuildChannelContainer} and only checks the caches with the relevant scoping. + * For {@link Guild}, {@link JDA}, or {@link ShardManager}, + * this returns the relevant channel with respect to the cache within each of those objects. + * For a guild, this would mean it only returns channels within the same guild. + *
If this is called on {@link JDA} or {@link ShardManager}, this may return null immediately after building, because the cache isn't initialized yet. + * To make sure the cache is initialized after building your {@link JDA} instance, you can use {@link JDA#awaitReady()}. + * + * @param id + * The id of the {@link MediaChannel}. + * + * @return Possibly-null {@link MediaChannel} with matching id. + */ + @Nullable + default MediaChannel getMediaChannelById(long id) + { + return getMediaChannelCache().getElementById(id); + } + + /** + * Gets all {@link MediaChannel} in the cache. + * + *

This copies the backing store into a list. This means every call + * creates a new list with O(n) complexity. It is recommended to store this into + * a local variable or use {@link #getForumChannelCache()} and use its more efficient + * versions of handling these values. + * + *

This getter exists on any instance of {@link IGuildChannelContainer} and only checks the caches with the relevant scoping. + * For {@link Guild}, {@link JDA}, or {@link ShardManager}, + * this returns the relevant channel with respect to the cache within each of those objects. + * For a guild, this would mean it only returns channels within the same guild. + *
If this is called on {@link JDA} or {@link ShardManager}, this may return null immediately after building, because the cache isn't initialized yet. + * To make sure the cache is initialized after building your {@link JDA} instance, you can use {@link JDA#awaitReady()}. + * + * @return An immutable List of {@link MediaChannel}. + */ + @Nonnull + default List getMediaChannels() + { + return getMediaChannelCache().asList(); + } } diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IPostContainer.java b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IPostContainer.java new file mode 100644 index 0000000000..6cacb9c2d5 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IPostContainer.java @@ -0,0 +1,292 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.channel.attribute; + +import net.dv8tion.jda.annotations.Incubating; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.entities.channel.ChannelFlag; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.MediaChannel; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.managers.channel.attribute.IPostContainerManager; +import net.dv8tion.jda.api.requests.restaction.ForumPostAction; +import net.dv8tion.jda.api.utils.cache.SortedSnowflakeCacheView; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.List; + +/** + * A channel which contains {@link #createForumPost(String, MessageCreateData) Forum Posts}. + *
Forum posts are simply {@link ThreadChannel ThreadChannels} of type {@link ChannelType#GUILD_PUBLIC_THREAD}. + * + *

The {@code CREATE POSTS} permission that is shown in the official Discord Client, is an alias for {@link net.dv8tion.jda.api.Permission#MESSAGE_SEND Permission.MESSAGE_SEND}. + * {@link net.dv8tion.jda.api.Permission#CREATE_PUBLIC_THREADS Permission.CREATE_PUBLIC_THREADS} is ignored for creating forum posts. + * + * @see ForumChannel + * @see MediaChannel + * @see #createForumPost(String, MessageCreateData) + */ +public interface IPostContainer extends IThreadContainer +{ + /** + * The maximum length of a forum / media channel topic ({@value}) + */ + int MAX_POST_CONTAINER_TOPIC_LENGTH = 4096; + /** + * The maximum number of {@link ForumPostAction#setTags(Collection) tags} that can be applied to a forum post. ({@value}) + */ + int MAX_POST_TAGS = 5; + + @Nonnull + @Override + IPostContainerManager getManager(); + + /** + * The available {@link ForumTag ForumTags} for this forum channel. + *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. + * + *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. + * + * @return {@link SortedSnowflakeCacheView} of {@link ForumTag} + */ + @Nonnull + SortedSnowflakeCacheView getAvailableTagCache(); + + /** + * The available {@link ForumTag ForumTags} for this channel. + *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. + * + *

This is a shortcut for {@link #getAvailableTagCache() getAvailableTagCache().asList()}. + * This method will copy the underlying cache into the list, running in {@code O(n)} time. + * + *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. + * + * @return Immutable {@link List} of {@link ForumTag} + */ + @Nonnull + default List getAvailableTags() + { + return getAvailableTagCache().asList(); + } + + /** + * The available {@link ForumTag ForumTags} for this channel. + *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. + * + *

This is a shortcut for {@link #getAvailableTagCache() getAvailableTagCache().getElementsByName(name, ignoreCase)}. + * This method will copy the underlying cache into the list, running in {@code O(n)} time. + * + *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. + * + * @param name + * The name of the tag + * @param ignoreCase + * Whether to use {@link String#equalsIgnoreCase(String)} + * + * @throws IllegalArgumentException + * If the name is {@code null} + * + * @return Immutable {@link List} of {@link ForumTag} with the given name + */ + @Nonnull + default List getAvailableTagsByName(@Nonnull String name, boolean ignoreCase) + { + return getAvailableTagCache().getElementsByName(name, ignoreCase); + } + + /** + * Retrieves the tag for the provided id. + * + *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. + * + * @param id + * The tag id + * + * @return The tag for the provided id, or {@code null} if no tag with that id exists + * + * @see net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake#fromId(long) + */ + @Nullable + default ForumTag getAvailableTagById(long id) + { + return getAvailableTagCache().getElementById(id); + } + + /** + * Retrieves the tag for the provided id. + * + *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. + * + * @param id + * The tag id + * + * @throws IllegalArgumentException + * If the provided id is null + * @throws NumberFormatException + * If the provided id is not a valid snowflake + * + * @return The tag for the provided id, or {@code null} if no tag with that id exists + * + * @see net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake#fromId(String) + */ + @Nullable + default ForumTag getAvailableTagById(@Nonnull String id) + { + return getAvailableTagCache().getElementById(id); + } + + /** + * The topic set for this channel, this is referred to as Guidelines in the official Discord client. + *
If no topic has been set, this returns null. + * + * @return Possibly-null String containing the topic of this channel. + */ + @Nullable + String getTopic(); + + /** + * Whether all new forum posts must have a tag. + * + * @return True, if all new posts must have a tag. + */ + default boolean isTagRequired() + { + return getFlags().contains(ChannelFlag.REQUIRE_TAG); + } + + /** + * The emoji which will show up on new forum posts as default reaction. + * + * @return The default reaction for new forum posts. + */ + @Nullable + EmojiUnion getDefaultReaction(); + + /** + * The default order used to show threads. + * + * @return The default order used to show threads. + */ + @Nonnull + SortOrder getDefaultSortOrder(); + + /** + * Creates a new forum/media post (thread) in this channel. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: + *

+ * + * @param name + * The name of the post (up to {@value Channel#MAX_NAME_LENGTH} characters) + * @param message + * The starting message of the post (see {@link net.dv8tion.jda.api.utils.messages.MessageCreateBuilder MessageCreateBuilder}) + * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the bot does not have {@link net.dv8tion.jda.api.Permission#MESSAGE_SEND Permission.MESSAGE_SEND} in the channel + * @throws IllegalArgumentException + * + * + * @return {@link ForumPostAction} + */ + @Nonnull + @Incubating + @CheckReturnValue + ForumPostAction createForumPost(@Nonnull String name, @Nonnull MessageCreateData message); + + /** + * The order used to sort forum posts. + */ + enum SortOrder + { + /** + * Sort by recent activity, including unarchive, message, reaction, and thread creation. + */ + RECENT_ACTIVITY(0), + /** + * Sort by the time the post was originally created. + */ + CREATION_TIME(1), + /** + * Placeholder for possible future order modes. + */ + UNKNOWN(-1), + ; + + private final int order; + + SortOrder(int order) + { + this.order = order; + } + + /** + * The underlying value as used by Discord. + * + * @return The raw order key + */ + public int getKey() + { + return order; + } + + /** + * The {@link SortOrder} for the provided key. + * + * @param key + * The key to get the {@link SortOrder} for + * + * @return The {@link SortOrder} for the provided key, or {@link #UNKNOWN} if the key is not known + */ + @Nonnull + public static SortOrder fromKey(int key) + { + for (SortOrder order : values()) + { + if (order.order == key) + return order; + } + + return UNKNOWN; + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IThreadContainer.java b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IThreadContainer.java index 3f0eccc224..ff9db5180d 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IThreadContainer.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/attribute/IThreadContainer.java @@ -56,6 +56,7 @@ public interface IThreadContainer extends GuildChannel, IPermissionContainer * * @return Immutable list of all ThreadChannel children. */ + @Nonnull default List getThreadChannels() { return Collections.unmodifiableList( diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/Category.java b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/Category.java index 1a9e78ce21..5ff2369865 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/Category.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/Category.java @@ -72,6 +72,7 @@ default List getChannels() channels.addAll(getStageChannels()); channels.addAll(getNewsChannels()); channels.addAll(getForumChannels()); + channels.addAll(getMediaChannels()); Collections.sort(channels); return Collections.unmodifiableList(channels); @@ -124,6 +125,21 @@ default List getForumChannels() )); } + /** + * All {@link net.dv8tion.jda.api.entities.channel.concrete.MediaChannel MediaChannels} listed for this Category + * + * @return Immutable list of all child ForumChannels + */ + @Nonnull + default List getMediaChannels() + { + return Collections.unmodifiableList(getGuild().getMediaChannelCache().applyStream(stream -> + stream.filter(channel -> equals(channel.getParentCategory())) + .sorted() + .collect(Collectors.toList()) + )); + } + /** * All {@link VoiceChannel VoiceChannels} * listed for this Category @@ -341,6 +357,43 @@ default List getStageChannels() @CheckReturnValue ChannelAction createForumChannel(@Nonnull String name); + /** + * Creates a new {@link MediaChannel} with this Category as parent. + * For this to be successful, the logged in account has to have the + * {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL MANAGE_CHANNEL} Permission in this Category. + * + *

This will copy all {@link net.dv8tion.jda.api.entities.PermissionOverride PermissionOverrides} of this Category! + * Unless the bot is unable to sync it with this category due to permission escalation. + * See {@link IPermissionHolder#canSync(IPermissionContainer, IPermissionContainer)} for details. + * + *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by + * the returned {@link net.dv8tion.jda.api.requests.RestAction RestAction} include the following: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS} + *
    The channel could not be created due to a permission discrepancy
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS} + *
    The {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL VIEW_CHANNEL} permission was removed
  • + * + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MAX_CHANNELS MAX_CHANNELS} + *
    The maximum number of channels were exceeded
  • + *
+ * + * @param name + * The name of the MediaChannel to create (up to {@value Channel#MAX_NAME_LENGTH} characters) + * + * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the logged in account does not have the {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL} permission + * @throws IllegalArgumentException + * If the provided name is {@code null}, empty, or longer than {@value Channel#MAX_NAME_LENGTH} characters + * + * @return A specific {@link ChannelAction ChannelAction} + *
This action allows to set fields for the new MediaChannel before creating it + */ + @Nonnull + @CheckReturnValue + ChannelAction createMediaChannel(@Nonnull String name); + /** * Modifies the positional order of this Category's nested {@link #getTextChannels() TextChannels} and {@link #getNewsChannels() NewsChannels}. *
This uses an extension of {@link ChannelOrderAction ChannelOrderAction} diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java index 3d151591e4..bf860d3672 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/ForumChannel.java @@ -16,29 +16,20 @@ package net.dv8tion.jda.api.entities.channel.concrete; -import net.dv8tion.jda.annotations.Incubating; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.channel.Channel; -import net.dv8tion.jda.api.entities.channel.ChannelFlag; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.IAgeRestrictedChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; -import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.attribute.IWebhookContainer; -import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildChannel; -import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.managers.channel.concrete.ForumChannelManager; import net.dv8tion.jda.api.requests.restaction.ChannelAction; import net.dv8tion.jda.api.requests.restaction.ForumPostAction; -import net.dv8tion.jda.api.utils.cache.SortedSnowflakeCacheView; import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.Collection; -import java.util.List; /** * A Forum Channel which contains {@link #createForumPost(String, MessageCreateData) Forum Posts}. @@ -50,16 +41,16 @@ * @see Guild#createForumChannel(String, Category) * @see #createForumPost(String, MessageCreateData) */ -public interface ForumChannel extends StandardGuildChannel, IThreadContainer, IWebhookContainer, IAgeRestrictedChannel, ISlowmodeChannel +public interface ForumChannel extends StandardGuildChannel, IPostContainer, IWebhookContainer, IAgeRestrictedChannel, ISlowmodeChannel { /** * The maximum length of a forum topic ({@value #MAX_FORUM_TOPIC_LENGTH}) */ - int MAX_FORUM_TOPIC_LENGTH = 4096; + int MAX_FORUM_TOPIC_LENGTH = IPostContainer.MAX_POST_CONTAINER_TOPIC_LENGTH; /** * The maximum number of {@link ForumPostAction#setTags(Collection) tags} that can be applied to a forum post. ({@value #MAX_POST_TAGS}) */ - int MAX_POST_TAGS = 5; + int MAX_POST_TAGS = IPostContainer.MAX_POST_TAGS; @Nonnull @Override @@ -83,135 +74,6 @@ default ChannelAction createCopy() return createCopy(getGuild()); } - /** - * The available {@link ForumTag ForumTags} for this forum channel. - *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. - * - *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. - * - * @return {@link SortedSnowflakeCacheView} of {@link ForumTag} - */ - @Nonnull - SortedSnowflakeCacheView getAvailableTagCache(); - - /** - * The available {@link ForumTag ForumTags} for this forum channel. - *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. - * - *

This is a shortcut for {@link #getAvailableTagCache() getAvailableTagCache().asList()}. - * This method will copy the underlying cache into the list, running in {@code O(n)} time. - * - *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. - * - * @return Immutable {@link List} of {@link ForumTag} - */ - @Nonnull - default List getAvailableTags() - { - return getAvailableTagCache().asList(); - } - - /** - * The available {@link ForumTag ForumTags} for this forum channel. - *
Tags are sorted by their {@link ForumTag#getPosition() position} ascending. - * - *

This is a shortcut for {@link #getAvailableTagCache() getAvailableTagCache().getElementsByName(name, ignoreCase)}. - * This method will copy the underlying cache into the list, running in {@code O(n)} time. - * - *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. - * - * @param name - * The name of the tag - * @param ignoreCase - * Whether to use {@link String#equalsIgnoreCase(String)} - * - * @throws IllegalArgumentException - * If the name is {@code null} - * - * @return Immutable {@link List} of {@link ForumTag} with the given name - */ - @Nonnull - default List getAvailableTagsByName(@Nonnull String name, boolean ignoreCase) - { - return getAvailableTagCache().getElementsByName(name, ignoreCase); - } - - /** - * Retrieves the tag for the provided id. - * - *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. - * - * @param id - * The tag id - * - * @return The tag for the provided id, or {@code null} if no tag with that id exists - * - * @see net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake#fromId(long) - */ - @Nullable - default ForumTag getAvailableTagById(long id) - { - return getAvailableTagCache().getElementById(id); - } - - /** - * Retrieves the tag for the provided id. - * - *

This requires {@link net.dv8tion.jda.api.utils.cache.CacheFlag#FORUM_TAGS CacheFlag.FORUM_TAGS} to be enabled. - * - * @param id - * The tag id - * - * @throws IllegalArgumentException - * If the provided id is null - * @throws NumberFormatException - * If the provided id is not a valid snowflake - * - * @return The tag for the provided id, or {@code null} if no tag with that id exists - * - * @see net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake#fromId(String) - */ - @Nullable - default ForumTag getAvailableTagById(@Nonnull String id) - { - return getAvailableTagCache().getElementById(id); - } - - /** - * The topic set for this channel, this is referred to as Guidelines in the official Discord client. - *
If no topic has been set, this returns null. - * - * @return Possibly-null String containing the topic of this channel. - */ - @Nullable - String getTopic(); - - /** - * Whether all new forum posts must have a tag. - * - * @return True, if all new posts must have a tag. - */ - default boolean isTagRequired() - { - return getFlags().contains(ChannelFlag.REQUIRE_TAG); - } - - /** - * The emoji which will show up on new forum posts as default reaction. - * - * @return The default reaction for new forum posts. - */ - @Nullable - EmojiUnion getDefaultReaction(); - -// /** -// * The default order used to show threads. -// * -// * @return The default order used to show threads. -// */ -// @Nonnull -// SortOrder getDefaultSortOrder(); - /** * The default layout used to show threads. * @@ -220,47 +82,6 @@ default boolean isTagRequired() @Nonnull Layout getDefaultLayout(); - /** - * Creates a new forum post (thread) in this forum. - * - *

Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include: - *

    - *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL} - *
    If the forum channel was deleted
  • - * - *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD} - *
    If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • - * - *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER} - *
    If this message was blocked by the harmful link filter
  • - * - *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE} - *
    If the total sum of uploaded bytes exceeds the guild's {@link Guild#getMaxFileSize() upload limit}
  • - * - *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#TITLE_BLOCKED_BY_AUTOMOD TITLE_BLOCKED_BY_AUTOMOD} - *
    If the forum post name was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
  • - *
- * - * @param name - * The name of the post (up to {@value Channel#MAX_NAME_LENGTH} characters) - * @param message - * The starting message of the post (see {@link net.dv8tion.jda.api.utils.messages.MessageCreateBuilder MessageCreateBuilder}) - * - * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException - * If the bot does not have {@link net.dv8tion.jda.api.Permission#MESSAGE_SEND Permission.MESSAGE_SEND} in the channel - * @throws IllegalArgumentException - *
    - *
  • If null is provided
  • - *
  • If the name is empty or longer than {@value Channel#MAX_NAME_LENGTH} characters
  • - *
- * - * @return {@link ForumPostAction} - */ - @Nonnull - @Incubating - @CheckReturnValue - ForumPostAction createForumPost(@Nonnull String name, @Nonnull MessageCreateData message); - /** * The layout used to sort forum posts. */ @@ -321,61 +142,4 @@ public static Layout fromKey(int key) return UNKNOWN; } } - -// /** -// * The order used to sort forum posts. -// */ -// enum SortOrder -// { -// /** -// * Sort by recent activity, including unarchive, message, reaction, and thread creation. -// */ -// RECENT_ACTIVITY(0), -// /** -// * Sort by the time the post was originally created. -// */ -// CREATION_TIME(1), -// /** -// * Placeholder for possible future order modes. -// */ -// UNKNOWN(-1), -// ; -// -// private final int order; -// -// SortOrder(int order) -// { -// this.order = order; -// } -// -// /** -// * The underlying value as used by Discord. -// * -// * @return The raw order key -// */ -// public int getKey() -// { -// return order; -// } -// -// /** -// * The {@link SortOrder} for the provided key. -// * -// * @param key -// * The key to get the {@link SortOrder} for -// * -// * @return The {@link SortOrder} for the provided key, or {@link #UNKNOWN} if the key is not known -// */ -// @Nonnull -// public static SortOrder fromKey(int key) -// { -// for (SortOrder order : values()) -// { -// if (order.order == key) -// return order; -// } -// -// return UNKNOWN; -// } -// } } diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/MediaChannel.java b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/MediaChannel.java new file mode 100644 index 0000000000..3b7ba72e3f --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/concrete/MediaChannel.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities.channel.concrete; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.ChannelFlag; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IAgeRestrictedChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; +import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IWebhookContainer; +import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildChannel; +import net.dv8tion.jda.api.managers.channel.concrete.MediaChannelManager; +import net.dv8tion.jda.api.requests.restaction.ChannelAction; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; + +import javax.annotation.Nonnull; + +/** + * A Media Channel which contains {@link #createForumPost(String, MessageCreateData) Forum Posts}. + *
Forum posts are simply {@link ThreadChannel ThreadChannels} of type {@link ChannelType#GUILD_PUBLIC_THREAD}. + * + *

The {@code CREATE POSTS} permission that is shown in the official Discord Client, is an alias for {@link net.dv8tion.jda.api.Permission#MESSAGE_SEND Permission.MESSAGE_SEND}. + * {@link net.dv8tion.jda.api.Permission#CREATE_PUBLIC_THREADS Permission.CREATE_PUBLIC_THREADS} is ignored for creating forum posts. + * + * @see Guild#createMediaChannel(String, Category) + * @see #createForumPost(String, MessageCreateData) + */ +public interface MediaChannel extends StandardGuildChannel, IPostContainer, IWebhookContainer, IAgeRestrictedChannel, ISlowmodeChannel +{ + @Nonnull + @Override + MediaChannelManager getManager(); + + @Nonnull + @Override + ChannelAction createCopy(@Nonnull Guild guild); + + @Nonnull + @Override + default ChannelAction createCopy() + { + return createCopy(getGuild()); + } + + @Nonnull + @Override + default ChannelType getType() + { + return ChannelType.MEDIA; + } + + /** + * Whether this media channel hides the download option for embeds. + * + * @return True, if download option is hidden + */ + default boolean isMediaDownloadHidden() + { + return getFlags().contains(ChannelFlag.HIDE_MEDIA_DOWNLOAD_OPTIONS); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/ChannelUnion.java b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/ChannelUnion.java index 7fb7089a3f..9bb7ba1821 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/ChannelUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/ChannelUnion.java @@ -38,6 +38,7 @@ *

  • {@link VoiceChannel}
  • *
  • {@link StageChannel}
  • *
  • {@link ForumChannel}
  • + *
  • {@link MediaChannel}
  • *
  • {@link Category}
  • * */ @@ -198,6 +199,28 @@ public interface ChannelUnion extends Channel @Nonnull ForumChannel asForumChannel(); + /** + * Casts this union to a {@link MediaChannel}. + * This method exists for developer discoverability. + * + *

    Note: This is effectively equivalent to using the cast operator: + *

    
    +     * //These are the same!
    +     * MediaChannel channel = union.asMediaChannel();
    +     * MediaChannel channel2 = (MediaChannel) union;
    +     * 
    + * + * You can use {@link #getType()} to see if the channel is of type {@link ChannelType#MEDIA} to validate + * whether you can call this method in addition to normal instanceof checks: channel instanceof MediaChannel + * + * @throws IllegalStateException + * If the channel represented by this union is not actually a {@link MediaChannel}. + * + * @return The channel as a {@link MediaChannel} + */ + @Nonnull + MediaChannel asMediaChannel(); + /** * Casts this union to a {@link Category}. * This method exists for developer discoverability. diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/DefaultGuildChannelUnion.java b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/DefaultGuildChannelUnion.java index 6d52184c92..34ab88534b 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/DefaultGuildChannelUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/DefaultGuildChannelUnion.java @@ -110,6 +110,7 @@ public interface DefaultGuildChannelUnion extends StandardGuildChannel * * @return The channel as a {@link net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer} */ + @Nonnull IThreadContainer asThreadContainer(); /** diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/GuildChannelUnion.java b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/GuildChannelUnion.java index 4278af799a..4f52569650 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/GuildChannelUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/GuildChannelUnion.java @@ -36,6 +36,7 @@ *
  • {@link VoiceChannel}
  • *
  • {@link StageChannel}
  • *
  • {@link ForumChannel}
  • + *
  • {@link MediaChannel}
  • *
  • {@link Category}
  • * */ @@ -195,6 +196,28 @@ public interface GuildChannelUnion extends GuildChannel @Nonnull ForumChannel asForumChannel(); + /** + * Casts this union to a {@link MediaChannel}. + * This method exists for developer discoverability. + * + *

    Note: This is effectively equivalent to using the cast operator: + *

    
    +     * //These are the same!
    +     * MediaChannel channel = union.asMediaChannel();
    +     * MediaChannel channel2 = (MediaChannel) union;
    +     * 
    + * + * You can use {@link #getType()} to see if the channel is of type {@link ChannelType#MEDIA} to validate + * whether you can call this method in addition to normal instanceof checks: channel instanceof MediaChannel + * + * @throws IllegalStateException + * If the channel represented by this union is not actually a {@link MediaChannel}. + * + * @return The channel as a {@link MediaChannel} + */ + @Nonnull + MediaChannel asMediaChannel(); + /** * Casts this union to a {@link GuildMessageChannel}. * This method exists for developer discoverability. diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IThreadContainerUnion.java b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IThreadContainerUnion.java index 1f23ab3f67..0b76e80cd0 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IThreadContainerUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IThreadContainerUnion.java @@ -19,6 +19,7 @@ import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.MediaChannel; import net.dv8tion.jda.api.entities.channel.concrete.NewsChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; @@ -37,6 +38,7 @@ *
  • {@link TextChannel}
  • *
  • {@link NewsChannel}
  • *
  • {@link ForumChannel}
  • + *
  • {@link MediaChannel}
  • * */ public interface IThreadContainerUnion extends IThreadContainer @@ -107,6 +109,28 @@ public interface IThreadContainerUnion extends IThreadContainer @Nonnull ForumChannel asForumChannel(); + /** + * Casts this union to a {@link MediaChannel}. + * This method exists for developer discoverability. + * + *

    Note: This is effectively equivalent to using the cast operator: + *

    
    +     * //These are the same!
    +     * MediaChannel channel = union.asMediaChannel();
    +     * MediaChannel channel2 = (MediaChannel) union;
    +     * 
    + * + * You can use {@link #getType()} to see if the channel is of type {@link ChannelType#MEDIA} to validate + * whether you can call this method in addition to normal instanceof checks: channel instanceof MediaChannel + * + * @throws IllegalStateException + * If the channel represented by this union is not actually a {@link MediaChannel}. + * + * @return The channel as a {@link MediaChannel} + */ + @Nonnull + MediaChannel asMediaChannel(); + /** * Casts this union to a {@link GuildMessageChannel}. *
    This works for the following channel types represented by this union: diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IWebhookContainerUnion.java b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IWebhookContainerUnion.java index 8a4d23c1a9..fca9e8c1d3 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IWebhookContainerUnion.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/unions/IWebhookContainerUnion.java @@ -39,6 +39,7 @@ *
  • {@link VoiceChannel}
  • *
  • {@link StageChannel}
  • *
  • {@link ForumChannel}
  • + *
  • {@link MediaChannel}
  • * */ public interface IWebhookContainerUnion extends IWebhookContainer @@ -109,6 +110,28 @@ public interface IWebhookContainerUnion extends IWebhookContainer @Nonnull ForumChannel asForumChannel(); + /** + * Casts this union to a {@link MediaChannel}. + * This method exists for developer discoverability. + * + *

    Note: This is effectively equivalent to using the cast operator: + *

    
    +     * //These are the same!
    +     * MediaChannel channel = union.asMediaChannel();
    +     * MediaChannel channel2 = (MediaChannel) union;
    +     * 
    + * + * You can use {@link #getType()} to see if the channel is of type {@link ChannelType#MEDIA} to validate + * whether you can call this method in addition to normal instanceof checks: channel instanceof MediaChannel + * + * @throws IllegalStateException + * If the channel represented by this union is not actually a {@link MediaChannel}. + * + * @return The channel as a {@link MediaChannel} + */ + @Nonnull + MediaChannel asMediaChannel(); + /** * Casts this union to a {@link net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer}. * This method exists for developer discoverability. diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagAddEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagAddEvent.java index 9f1129304d..25c2a51ffc 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagAddEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagAddEvent.java @@ -17,14 +17,14 @@ package net.dv8tion.jda.api.events.channel.forum; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import javax.annotation.Nonnull; import java.util.Collection; /** - * Indicates that a new {@link ForumTag} was added to a {@link ForumChannel}. + * Indicates that a new {@link ForumTag} was added to a {@link IPostContainer}. * *

    Use {@link net.dv8tion.jda.api.events.channel.update.ChannelUpdateAppliedTagsEvent ChannelUpdateAppliedTagsEvent} * to detect when a tag is added to a forum post instead. @@ -35,7 +35,7 @@ */ public class ForumTagAddEvent extends GenericForumTagEvent { - public ForumTagAddEvent(@Nonnull JDA api, long responseNumber, @Nonnull ForumChannel channel, @Nonnull ForumTag tag) + public ForumTagAddEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nonnull ForumTag tag) { super(api, responseNumber, channel, tag); } diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagRemoveEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagRemoveEvent.java index 8237153aa1..3b4ee3103e 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagRemoveEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/forum/ForumTagRemoveEvent.java @@ -17,14 +17,14 @@ package net.dv8tion.jda.api.events.channel.forum; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import javax.annotation.Nonnull; import java.util.Collection; /** - * Indicates that a {@link ForumTag} was removed from a {@link ForumChannel}. + * Indicates that a {@link ForumTag} was removed from a {@link IPostContainer}. * *

    Use {@link net.dv8tion.jda.api.events.channel.update.ChannelUpdateAppliedTagsEvent ChannelUpdateAppliedTagsEvent} * to detect when a tag is removed from a forum post instead. @@ -35,7 +35,7 @@ */ public class ForumTagRemoveEvent extends GenericForumTagEvent { - public ForumTagRemoveEvent(@Nonnull JDA api, long responseNumber, @Nonnull ForumChannel channel, @Nonnull ForumTag tag) + public ForumTagRemoveEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nonnull ForumTag tag) { super(api, responseNumber, channel, tag); } diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/forum/GenericForumTagEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/forum/GenericForumTagEvent.java index 64f3ff4cba..95226446d5 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/forum/GenericForumTagEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/forum/GenericForumTagEvent.java @@ -17,7 +17,7 @@ package net.dv8tion.jda.api.events.channel.forum; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.events.Event; @@ -34,10 +34,10 @@ */ public abstract class GenericForumTagEvent extends Event { - protected final ForumChannel channel; + protected final IPostContainer channel; protected final ForumTag tag; - public GenericForumTagEvent(@Nonnull JDA api, long responseNumber, @Nonnull ForumChannel channel, @Nonnull ForumTag tag) + public GenericForumTagEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nonnull ForumTag tag) { super(api, responseNumber); this.channel = channel; @@ -45,12 +45,12 @@ public GenericForumTagEvent(@Nonnull JDA api, long responseNumber, @Nonnull Foru } /** - * The {@link ForumChannel} which has been updated. + * The {@link IPostContainer} which has been updated. * - * @return The {@link ForumChannel} + * @return The {@link IPostContainer} */ @Nonnull - public ForumChannel getChannel() + public IPostContainer getChannel() { return channel; } diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/ForumTagUpdateEmojiEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/ForumTagUpdateEmojiEvent.java index 26b77296cf..22ec23c96d 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/ForumTagUpdateEmojiEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/ForumTagUpdateEmojiEvent.java @@ -17,7 +17,7 @@ package net.dv8tion.jda.api.events.channel.forum.update; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; @@ -38,7 +38,7 @@ public class ForumTagUpdateEmojiEvent extends GenericForumTagUpdateEvent { public static final String IDENTIFIER = "name"; - public ForumTagUpdateNameEvent(@Nonnull JDA api, long responseNumber, @Nonnull ForumChannel channel, @Nonnull ForumTag tag, @Nonnull String previous) + public ForumTagUpdateNameEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nonnull ForumTag tag, @Nonnull String previous) { super(api, responseNumber, channel, tag, previous, tag.getName(), IDENTIFIER); } diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/GenericForumTagUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/GenericForumTagUpdateEvent.java index 0791789050..4c44aabb39 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/GenericForumTagUpdateEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/forum/update/GenericForumTagUpdateEvent.java @@ -17,7 +17,7 @@ package net.dv8tion.jda.api.events.channel.forum.update; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; import net.dv8tion.jda.api.events.UpdateEvent; import net.dv8tion.jda.api.events.channel.forum.GenericForumTagEvent; @@ -41,7 +41,7 @@ public abstract class GenericForumTagUpdateEvent extends GenericForumTagEvent private final T next; private final String identifier; - public GenericForumTagUpdateEvent(@Nonnull JDA api, long responseNumber, @Nonnull ForumChannel channel, @Nonnull ForumTag tag, + public GenericForumTagUpdateEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nonnull ForumTag tag, T previous, T next, @Nonnull String identifier) { super(api, responseNumber, channel, tag); diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateAppliedTagsEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateAppliedTagsEvent.java index 86baf100c4..7d5efe47b2 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateAppliedTagsEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateAppliedTagsEvent.java @@ -39,7 +39,6 @@ */ public class ChannelUpdateAppliedTagsEvent extends GenericChannelUpdateEvent> { - public static final ChannelField FIELD = ChannelField.APPLIED_TAGS; public static final String IDENTIFIER = FIELD.getFieldName(); diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultLayoutEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultLayoutEvent.java index b471987e02..9d043c530a 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultLayoutEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultLayoutEvent.java @@ -33,6 +33,9 @@ @SuppressWarnings("ConstantConditions") public class ChannelUpdateDefaultLayoutEvent extends GenericChannelUpdateEvent { + public static final ChannelField FIELD = ChannelField.DEFAULT_FORUM_LAYOUT; + public static final String IDENTIFIER = FIELD.getFieldName(); + public ChannelUpdateDefaultLayoutEvent(@Nonnull JDA api, long responseNumber, @Nonnull Channel channel, @Nonnull ForumChannel.Layout oldValue, @Nonnull ForumChannel.Layout newValue) { super(api, responseNumber, channel, ChannelField.DEFAULT_FORUM_LAYOUT, oldValue, newValue); diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultReactionEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultReactionEvent.java index caf25d4c1a..0a035ee172 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultReactionEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultReactionEvent.java @@ -17,8 +17,8 @@ package net.dv8tion.jda.api.events.channel.update; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelField; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; @@ -26,7 +26,7 @@ import javax.annotation.Nullable; /** - * Indicates that the {@link ForumChannel#getDefaultReaction() default reaction emoji} of a {@link ForumChannel} changed. + * Indicates that the {@link ForumChannel#getDefaultReaction() default reaction emoji} of a {@link IPostContainer} changed. * *

    Can be used to retrieve the old default reaction and the new one. * @@ -34,11 +34,10 @@ */ public class ChannelUpdateDefaultReactionEvent extends GenericChannelUpdateEvent { - public static final ChannelField FIELD = ChannelField.DEFAULT_REACTION_EMOJI; public static final String IDENTIFIER = FIELD.getFieldName(); - public ChannelUpdateDefaultReactionEvent(@Nonnull JDA api, long responseNumber, @Nonnull Channel channel, @Nullable EmojiUnion oldValue, @Nullable EmojiUnion newValue) + public ChannelUpdateDefaultReactionEvent(@Nonnull JDA api, long responseNumber, @Nonnull IPostContainer channel, @Nullable EmojiUnion oldValue, @Nullable EmojiUnion newValue) { super(api, responseNumber, channel, FIELD, oldValue, newValue); } diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultSortOrderEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultSortOrderEvent.java new file mode 100644 index 0000000000..cd9e0e512c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultSortOrderEvent.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.channel.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.ChannelField; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; + +import javax.annotation.Nonnull; + +/** + * Indicates that the {@link IPostContainer#getDefaultSortOrder() default sort order} of a {@link IPostContainer} changed. + * + *

    Can be used to retrieve the old default sort order and the new one. + * + * @see ChannelField#DEFAULT_SORT_ORDER + */ +@SuppressWarnings("ConstantConditions") +public class ChannelUpdateDefaultSortOrderEvent extends GenericChannelUpdateEvent +{ + public static final ChannelField FIELD = ChannelField.DEFAULT_SORT_ORDER; + public static final String IDENTIFIER = FIELD.getFieldName(); + + public ChannelUpdateDefaultSortOrderEvent(@Nonnull JDA api, long responseNumber, IPostContainer channel, IPostContainer.SortOrder oldValue) + { + super(api, responseNumber, channel, ChannelField.DEFAULT_SORT_ORDER, oldValue, channel.getDefaultSortOrder()); + } + + @Nonnull + @Override + public IPostContainer.SortOrder getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + public IPostContainer.SortOrder getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultThreadSlowmodeEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultThreadSlowmodeEvent.java index 04856f45e2..793301d671 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultThreadSlowmodeEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateDefaultThreadSlowmodeEvent.java @@ -32,7 +32,6 @@ */ public class ChannelUpdateDefaultThreadSlowmodeEvent extends GenericChannelUpdateEvent { - public static final ChannelField FIELD = ChannelField.DEFAULT_THREAD_SLOWMODE; public static final String IDENTIFIER = FIELD.getFieldName(); diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateFlagsEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateFlagsEvent.java index b3630e396e..f07d1a8474 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateFlagsEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateFlagsEvent.java @@ -33,7 +33,6 @@ */ public class ChannelUpdateFlagsEvent extends GenericChannelUpdateEvent> { - public static final ChannelField FIELD = ChannelField.FLAGS; public static final String IDENTIFIER = FIELD.getFieldName(); diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateTypeEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateTypeEvent.java index 96b6bd064d..51a5775e0f 100644 --- a/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateTypeEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/channel/update/ChannelUpdateTypeEvent.java @@ -32,6 +32,7 @@ *

      *
    • of type {@link ChannelType#TEXT} is converted to type {@link ChannelType#NEWS}
    • *
    • of type {@link ChannelType#NEWS} is converted to type {@link ChannelType#TEXT}
    • + *
    • of type {@link ChannelType#FORUM} is converted to type {@link ChannelType#MEDIA}
    • *
    * * @see Channel#getType() diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index 2362720ba2..4da8931a5c 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -214,7 +214,7 @@ public void onChannelUpdateRegion(@Nonnull ChannelUpdateRegionEvent event) {} public void onChannelUpdateSlowmode(@Nonnull ChannelUpdateSlowmodeEvent event) {} public void onChannelUpdateDefaultThreadSlowmode(@Nonnull ChannelUpdateDefaultThreadSlowmodeEvent event) {} public void onChannelUpdateDefaultReaction(@Nonnull ChannelUpdateDefaultReactionEvent event) {} -// public void onChannelUpdateDefaultSortOrder(@Nonnull ChannelUpdateDefaultSortOrderEvent event) {} + public void onChannelUpdateDefaultSortOrder(@Nonnull ChannelUpdateDefaultSortOrderEvent event) {} public void onChannelUpdateDefaultLayout(@Nonnull ChannelUpdateDefaultLayoutEvent event) {} public void onChannelUpdateTopic(@Nonnull ChannelUpdateTopicEvent event) {} public void onChannelUpdateType(@Nonnull ChannelUpdateTypeEvent event) {} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java b/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java index 75750e7f2e..336d58b9f7 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java @@ -29,7 +29,7 @@ import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.interactions.modals.ModalInteraction; -import net.dv8tion.jda.internal.utils.Helpers; +import net.dv8tion.jda.internal.utils.ChannelUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -168,7 +168,7 @@ default ChannelType getChannelType() @Nonnull default GuildChannel getGuildChannel() { - return Helpers.safeChannelCast(getChannel(), GuildChannel.class); + return ChannelUtil.safeChannelCast(getChannel(), GuildChannel.class); } /** @@ -183,7 +183,7 @@ default GuildChannel getGuildChannel() @Nonnull default MessageChannel getMessageChannel() { - return Helpers.safeChannelCast(getChannel(), MessageChannel.class); + return ChannelUtil.safeChannelCast(getChannel(), MessageChannel.class); } /** diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/ChannelManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/ChannelManager.java index 5436d69669..7010b09f20 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/channel/ChannelManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/ChannelManager.java @@ -42,7 +42,6 @@ * * @see GuildChannel#getManager() */ -//TODO-v5: Revisit all usages of IllegalStateException in the setX methods in this class to see if they should be UnsupportedOperationException like in ChannelAction public interface ChannelManager> extends Manager { /** Used to reset the name field */ @@ -79,18 +78,20 @@ public interface ChannelManager{@link #REQUIRE_TAG} *
  • {@link #DEFAULT_REACTION}
  • *
  • {@link #DEFAULT_LAYOUT}
  • + *
  • {@link #DEFAULT_SORT_ORDER}
  • + *
  • {@link #HIDE_MEDIA_DOWNLOAD_OPTIONS}
  • + *
  • {@link #DEFAULT_THREAD_SLOWMODE}
  • * * * @param fields @@ -157,6 +161,9 @@ public interface ChannelManager{@link #REQUIRE_TAG} *
  • {@link #DEFAULT_REACTION}
  • *
  • {@link #DEFAULT_LAYOUT}
  • + *
  • {@link #DEFAULT_SORT_ORDER}
  • + *
  • {@link #HIDE_MEDIA_DOWNLOAD_OPTIONS}
  • + *
  • {@link #DEFAULT_THREAD_SLOWMODE}
  • * * * @param fields diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IPostContainerManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IPostContainerManager.java new file mode 100644 index 0000000000..f9c1004973 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IPostContainerManager.java @@ -0,0 +1,115 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.managers.channel.attribute; + +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer.SortOrder; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.forums.BaseForumTag; +import net.dv8tion.jda.api.entities.channel.forums.ForumTagData; +import net.dv8tion.jda.api.entities.emoji.Emoji; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +/** + * Manager abstraction to configure settings related to forum post containers, such as {@link ForumChannel}. + * + * @param The channel type + * @param The manager type + */ +public interface IPostContainerManager> extends IThreadContainerManager, IPermissionContainerManager +{ + /** + * Sets the tag requirement state of this {@link IPostContainer}. + *
    If true, all new posts must have at least one tag. + * + * @param requireTag + * The new tag requirement state for the selected {@link IPostContainer} + * + * @return ChannelManager for chaining convenience. + * + * @see IPostContainer#isTagRequired() + */ + @Nonnull + @CheckReturnValue + M setTagRequired(boolean requireTag); + + /** + * Sets the available tags of the selected {@link IPostContainer}. + *
    Tags will be ordered based on the provided list order. + * + *

    This is a full replacement of the tags list, all missing tags will be removed. + * You can use {@link ForumTagData} to create new tags or update existing ones. + * + *

    Example + *

    {@code
    +     * List tags = new ArrayList<>(channel.getAvailableTags());
    +     * tags.add(new ForumTagData("question").setModerated(true)); // add a new tag
    +     * tags.set(0, ForumTagData.from(tags.get(0)).setName("bug report")); // update an existing tag
    +     * // Update the tag list
    +     * channel.getManager().setAvailableTags(tags).queue();
    +     * }
    + * + * @param tags + * The new available tags in the desired order. + * + * @throws IllegalArgumentException + * If the provided list is null or contains null elements + * + * @return ChannelManager for chaining convenience + * + * @see IPostContainer#getAvailableTags() + */ + @Nonnull + @CheckReturnValue + M setAvailableTags(@Nonnull List tags); + + /** + * Sets the default reaction emoji of the selected {@link IPostContainer}. + *
    This does not support custom emoji from other guilds. + * + * @param emoji + * The new default reaction emoji, or null to unset. + * + * @return ChannelManager for chaining convenience + * + * @see IPostContainer#getDefaultReaction() + */ + @Nonnull + @CheckReturnValue + M setDefaultReaction(@Nullable Emoji emoji); + + /** + * Sets the default sort order of the selected {@link IPostContainer}. + * + * @param sortOrder + * The new {@link SortOrder} + * + * @throws IllegalArgumentException + * If null or {@link SortOrder#UNKNOWN} is provided + * + * @return ChannelManager for chaining convenience + * + * @see IPostContainer#getDefaultSortOrder() + */ + @Nonnull + @CheckReturnValue + M setDefaultSortOrder(@Nonnull SortOrder sortOrder); +} diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IThreadContainerManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IThreadContainerManager.java new file mode 100644 index 0000000000..c8bae1a70b --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/attribute/IThreadContainerManager.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.managers.channel.attribute; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.managers.channel.ChannelManager; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * Manager abstraction to configure settings related to thread channel containers, such as {@link ForumChannel}. + * + * @param The channel type + * @param The manager type + */ +public interface IThreadContainerManager> extends ChannelManager +{ + /** + * Sets the default thread slowmode of the selected channel. + * This is applied to newly created threads by default. + *
    Provide {@code 0} to disable slowmode. + * + *

    A channel default thread slowmode must not be negative nor greater than {@link ISlowmodeChannel#MAX_SLOWMODE}! + * + *

    Note: Bots are unaffected by this. + *
    Having {@link Permission#MESSAGE_MANAGE MESSAGE_MANAGE} or + * {@link Permission#MANAGE_CHANNEL MANAGE_CHANNEL} permission also + * grants immunity to slowmode. + * + * @param slowmode + * The new default thread slowmode (in seconds) + * + * @throws IllegalArgumentException + * If the provided slowmode is negative or greater than {@value ISlowmodeChannel#MAX_SLOWMODE} + * + * @return ChannelManager for chaining convenience + * + * @see IThreadContainer#getDefaultThreadSlowmode() + */ + @Nonnull + @CheckReturnValue + M setDefaultThreadSlowmode(int slowmode); +} diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/ForumChannelManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/ForumChannelManager.java index 0434a7a125..b19e489a5c 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/ForumChannelManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/ForumChannelManager.java @@ -17,17 +17,13 @@ package net.dv8tion.jda.api.managers.channel.concrete; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; -import net.dv8tion.jda.api.entities.channel.forums.BaseForumTag; -import net.dv8tion.jda.api.entities.channel.forums.ForumTagData; -import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.managers.channel.attribute.IAgeRestrictedChannelManager; +import net.dv8tion.jda.api.managers.channel.attribute.IPostContainerManager; import net.dv8tion.jda.api.managers.channel.attribute.ISlowmodeChannelManager; import net.dv8tion.jda.api.managers.channel.middleman.StandardGuildChannelManager; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.List; /** * Manager providing functionality to modify a {@link ForumChannel ForumChannel}. @@ -46,69 +42,10 @@ */ public interface ForumChannelManager extends StandardGuildChannelManager, + IPostContainerManager, IAgeRestrictedChannelManager, ISlowmodeChannelManager { - /** - * Sets the tag requirement state of this {@link ForumChannel}. - *
    If true, all new posts must have at least one tag. - * - * @param requireTag - * The new tag requirement state for the selected {@link ForumChannel} - * - * @return ChannelManager for chaining convenience. - * - * @see ForumChannel#isTagRequired() - */ - @Nonnull - @CheckReturnValue - ForumChannelManager setTagRequired(boolean requireTag); - - /** - * Sets the available tags of the selected {@link ForumChannel}. - *
    Tags will be ordered based on the provided list order. - * - *

    This is a full replacement of the tags list, all missing tags will be removed. - * You can use {@link ForumTagData} to create new tags or update existing ones. - * - *

    Example - *

    {@code
    -     * List tags = new ArrayList<>(channel.getAvailableTags());
    -     * tags.add(new ForumTagData("question").setModerated(true)); // add a new tag
    -     * tags.set(0, ForumTagData.from(tags.get(0)).setName("bug report")); // update an existing tag
    -     * // Update the tag list
    -     * channel.getManager().setAvailableTags(tags).queue();
    -     * }
    - * - * @param tags - * The new available tags in the desired order. - * - * @throws IllegalArgumentException - * If the provided list is null or contains null elements - * - * @return ChannelManager for chaining convenience - * - * @see ForumChannel#getAvailableTags() - */ - @Nonnull - @CheckReturnValue - ForumChannelManager setAvailableTags(@Nonnull List tags); - - /** - * Sets the default reaction emoji of the selected {@link ForumChannel}. - *
    This does not support custom emoji from other guilds. - * - * @param emoji - * The new default reaction emoji, or null to unset. - * - * @return ChannelManager for chaining convenience - * - * @see ForumChannel#getDefaultReaction() - */ - @Nonnull - @CheckReturnValue - ForumChannelManager setDefaultReaction(@Nullable Emoji emoji); - /** * Sets the default layout of the selected {@link ForumChannel}. * diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/MediaChannelManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/MediaChannelManager.java new file mode 100644 index 0000000000..b215cd306a --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/concrete/MediaChannelManager.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.managers.channel.concrete; + +import net.dv8tion.jda.api.entities.channel.concrete.MediaChannel; +import net.dv8tion.jda.api.managers.channel.attribute.IAgeRestrictedChannelManager; +import net.dv8tion.jda.api.managers.channel.attribute.IPostContainerManager; +import net.dv8tion.jda.api.managers.channel.attribute.ISlowmodeChannelManager; +import net.dv8tion.jda.api.managers.channel.middleman.StandardGuildChannelManager; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + +/** + * Manager providing functionality to modify a {@link MediaChannel}. + * + *

    Example + *

    {@code
    + * manager.setName("Art Showcase")
    + *  .setSlowmode(10)
    + *  .setTopic("Showcase your art creations here.")
    + *  .queue();
    + * manager.reset(ChannelManager.NSFW | ChannelManager.NAME)
    + *  .setName("NSFW Art Showcase")
    + *  .setNSFW(true)
    + *  .queue();
    + * }
    + */ +public interface MediaChannelManager extends + StandardGuildChannelManager, + IPostContainerManager, + IAgeRestrictedChannelManager, + ISlowmodeChannelManager +{ + /** + * Sets whether to hide the download media option on this channel. + * + * @param hideOption + * Whether to hide the download option + * + * @return ChannelManager for chaining convenience. + * + * @see MediaChannel#isMediaDownloadHidden() + */ + @Nonnull + @CheckReturnValue + MediaChannelManager setHideMediaDownloadOption(boolean hideOption); +} diff --git a/src/main/java/net/dv8tion/jda/api/managers/channel/middleman/StandardGuildMessageChannelManager.java b/src/main/java/net/dv8tion/jda/api/managers/channel/middleman/StandardGuildMessageChannelManager.java index 4c283e357a..1fdd865a7b 100644 --- a/src/main/java/net/dv8tion/jda/api/managers/channel/middleman/StandardGuildMessageChannelManager.java +++ b/src/main/java/net/dv8tion/jda/api/managers/channel/middleman/StandardGuildMessageChannelManager.java @@ -18,6 +18,7 @@ import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildMessageChannel; import net.dv8tion.jda.api.managers.channel.attribute.IAgeRestrictedChannelManager; +import net.dv8tion.jda.api.managers.channel.attribute.IThreadContainerManager; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; @@ -40,7 +41,7 @@ * @see StandardGuildMessageChannel#getManager() */ public interface StandardGuildMessageChannelManager> - extends StandardGuildChannelManager, IAgeRestrictedChannelManager + extends StandardGuildChannelManager, IAgeRestrictedChannelManager, IThreadContainerManager { /** * Sets the topic of the selected {@link StandardGuildMessageChannel channel}. diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/ChannelAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/ChannelAction.java index 86d7504aa5..5c94ecc372 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/ChannelAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/ChannelAction.java @@ -24,7 +24,9 @@ import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.Category; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.StageChannel; @@ -142,7 +144,7 @@ public interface ChannelAction extends FluentAuditableRe ChannelAction setPosition(@Nullable Integer position); /** - * Sets the topic for the new TextChannel + * Sets the topic for the channel * * @param topic * The topic for the new GuildChannel @@ -151,8 +153,8 @@ public interface ChannelAction extends FluentAuditableRe * If this ChannelAction is not for a TextChannel * @throws IllegalArgumentException * If the provided topic is greater than {@value StandardGuildMessageChannel#MAX_TOPIC_LENGTH} in length. - * For {@link net.dv8tion.jda.api.entities.channel.concrete.ForumChannel ForumChannels}, - * this limit is {@value net.dv8tion.jda.api.entities.channel.concrete.ForumChannel#MAX_FORUM_TOPIC_LENGTH} instead. + * For {@link IPostContainer IPostContainers}, + * this limit is {@value IPostContainer#MAX_POST_CONTAINER_TOPIC_LENGTH} instead. * * @return The current ChannelAction, for chaining convenience */ @@ -161,7 +163,7 @@ public interface ChannelAction extends FluentAuditableRe ChannelAction setTopic(@Nullable String topic); /** - * Sets the NSFW flag for the new TextChannel + * Sets the NSFW flag for the channel * * @param nsfw * The NSFW flag for the new GuildChannel @@ -199,7 +201,31 @@ public interface ChannelAction extends FluentAuditableRe ChannelAction setSlowmode(int slowmode); /** - * Sets the default reaction emoji of the new {@link ForumChannel}. + * Sets the slowmode value, which limits the amount of time that individual users must wait + * between sending messages in the new channel. This is measured in seconds. + *
    This is applied to newly created threads by default. + * + *

    Note: Bots are unaffected by this. + *
    Having {@link net.dv8tion.jda.api.Permission#MESSAGE_MANAGE MESSAGE_MANAGE} or + * {@link net.dv8tion.jda.api.Permission#MANAGE_CHANNEL MANAGE_CHANNEL} permission also + * grants immunity to slowmode. + * + * @param slowmode + * The number of seconds required to wait between sending messages in the channel. + * + * @throws UnsupportedOperationException + * If this ChannelAction is not for a {@link IThreadContainer} + * @throws IllegalArgumentException + * If the {@code slowmode} is greater than {@link ISlowmodeChannel#MAX_SLOWMODE ISlowmodeChannel.MAX_SLOWMODE}, or less than 0 + * + * @return The current ChannelAction, for chaining convenience + */ + @Nonnull + @CheckReturnValue + ChannelAction setDefaultThreadSlowmode(int slowmode); + + /** + * Sets the default reaction emoji of the channel. *
    This does not support custom emoji from other guilds. * * @param emoji @@ -207,7 +233,7 @@ public interface ChannelAction extends FluentAuditableRe * * @return The current ChannelAction, for chaining convenience * - * @see ForumChannel#getDefaultReaction() + * @see IPostContainer#getDefaultReaction() */ @Nonnull @CheckReturnValue @@ -219,6 +245,9 @@ public interface ChannelAction extends FluentAuditableRe * @param layout * The new default layout. * + * @throws IllegalArgumentException + * If null or {@link net.dv8tion.jda.api.entities.channel.concrete.ForumChannel.Layout#UNKNOWN UNKNOWN} is provided + * * @return The current ChannelAction, for chaining convenience * * @see ForumChannel#getDefaultLayout() @@ -228,7 +257,24 @@ public interface ChannelAction extends FluentAuditableRe ChannelAction setDefaultLayout(@Nonnull ForumChannel.Layout layout); /** - * Sets the available tags of the new {@link ForumChannel}. + * Sets the default sort order of the channel. + * + * @param sortOrder + * The new default sort order. + * + * @throws IllegalArgumentException + * If null or {@link net.dv8tion.jda.api.entities.channel.attribute.IPostContainer.SortOrder#UNKNOWN UNKNOWN} is provided + * + * @return The current ChannelAction, for chaining convenience + * + * @see IPostContainer#getDefaultSortOrder() + */ + @Nonnull + @CheckReturnValue + ChannelAction setDefaultSortOrder(@Nonnull IPostContainer.SortOrder sortOrder); + + /** + * Sets the available tags of the channel. *
    Tags will be ordered based on the provided list order. * *

    You can use {@link ForumTagData} to create new tags. @@ -241,7 +287,7 @@ public interface ChannelAction extends FluentAuditableRe * * @return The current ChannelAction, for chaining convenience * - * @see ForumChannel#getAvailableTags() + * @see IPostContainer#getAvailableTags() */ @Nonnull @CheckReturnValue diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/ForumPostAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/ForumPostAction.java index e381812d36..3f7ee64ec6 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/ForumPostAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/ForumPostAction.java @@ -16,6 +16,7 @@ package net.dv8tion.jda.api.requests.restaction; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumPost; import net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake; @@ -35,17 +36,17 @@ *

    On success, this provides a {@link ForumPost} object with the {@link ForumPost#getMessage() starter message} * and the {@link ForumPost#getThreadChannel() thread channel} of the post. * - * @see net.dv8tion.jda.api.entities.channel.concrete.ForumChannel#createForumPost(String, MessageCreateData) + * @see IPostContainer#createForumPost(String, MessageCreateData) */ public interface ForumPostAction extends AbstractThreadCreateAction, MessageCreateRequest, FluentRestAction { /** - * The {@link ForumChannel} to create the post in + * The {@link IPostContainer} to create the post in * - * @return The {@link ForumChannel} + * @return The {@link IPostContainer} */ @Nonnull - ForumChannel getChannel(); + IPostContainer getChannel(); /** * Configures that tags which should be applied to the new post. diff --git a/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java b/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java index 1d8151a102..a611589ac0 100644 --- a/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java +++ b/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java @@ -812,6 +812,13 @@ default SnowflakeCacheView getForumChannelCache() return CacheView.allSnowflakes(() -> this.getShardCache().stream().map(JDA::getForumChannelCache)); } + @Nonnull + @Override + default SnowflakeCacheView getMediaChannelCache() + { + return CacheView.allSnowflakes(() -> this.getShardCache().stream().map(JDA::getMediaChannelCache)); + } + /** * This returns the {@link net.dv8tion.jda.api.JDA JDA} instance which has the same id as the one provided. *
    If there is no shard with an id that matches the provided one, this will return {@code null}. diff --git a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java index daaa6f3c80..d00955ad78 100644 --- a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java +++ b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java @@ -19,7 +19,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.requests.GatewayIntent; @@ -73,7 +73,7 @@ public enum CacheFlag */ ROLE_TAGS, /** - * Enables cache for {@link ForumChannel#getAvailableTagCache()} and {@link ThreadChannel#getAppliedTags()} + * Enables cache for {@link IPostContainer#getAvailableTagCache()} and {@link ThreadChannel#getAppliedTags()} */ FORUM_TAGS, /** diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 4db04cdfdd..cdab669427 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -108,6 +108,7 @@ public class JDAImpl implements JDA protected final SnowflakeCacheViewImpl stageChannelCache = new SnowflakeCacheViewImpl<>(StageChannel.class, Channel::getName); protected final SnowflakeCacheViewImpl threadChannelsCache = new SnowflakeCacheViewImpl<>(ThreadChannel.class, Channel::getName); protected final SnowflakeCacheViewImpl forumChannelsCache = new SnowflakeCacheViewImpl<>(ForumChannel.class, Channel::getName); + protected final SnowflakeCacheViewImpl mediaChannelsCache = new SnowflakeCacheViewImpl<>(MediaChannel.class, Channel::getName); protected final SnowflakeCacheViewImpl privateChannelCache = new SnowflakeCacheViewImpl<>(PrivateChannel.class, Channel::getName); protected final LinkedList privateChannelLRU = new LinkedList<>(); @@ -775,6 +776,13 @@ public SnowflakeCacheView getForumChannelCache() return forumChannelsCache; } + @Nonnull + @Override + public SnowflakeCacheView getMediaChannelCache() + { + return mediaChannelsCache; + } + @Nonnull @Override public SnowflakeCacheView getPrivateChannelCache() @@ -1278,6 +1286,11 @@ public SnowflakeCacheViewImpl getForumChannelsView() return forumChannelsCache; } + public SnowflakeCacheViewImpl getMediaChannelsView() + { + return mediaChannelsCache; + } + public SnowflakeCacheViewImpl getPrivateChannelsView() { return privateChannelCache; diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index 903cf4f006..a3514369c7 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -59,6 +59,7 @@ import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.entities.channel.concrete.*; import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IPermissionContainerMixin; +import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IPostContainerMixin; import net.dv8tion.jda.internal.entities.channel.mixin.middleman.AudioChannelMixin; import net.dv8tion.jda.internal.entities.emoji.CustomEmojiImpl; import net.dv8tion.jda.internal.entities.emoji.RichCustomEmojiImpl; @@ -398,31 +399,28 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< return guildObj; } - private void createGuildChannel(GuildImpl guildObj, DataObject channelData) + public GuildChannel createGuildChannel(GuildImpl guildObj, DataObject channelData) { final ChannelType channelType = ChannelType.fromId(channelData.getInt("type")); switch (channelType) { case TEXT: - createTextChannel(guildObj, channelData, guildObj.getIdLong()); - break; + return createTextChannel(guildObj, channelData, guildObj.getIdLong()); case NEWS: - createNewsChannel(guildObj, channelData, guildObj.getIdLong()); - break; + return createNewsChannel(guildObj, channelData, guildObj.getIdLong()); case STAGE: - createStageChannel(guildObj, channelData, guildObj.getIdLong()); - break; + return createStageChannel(guildObj, channelData, guildObj.getIdLong()); case VOICE: - createVoiceChannel(guildObj, channelData, guildObj.getIdLong()); - break; + return createVoiceChannel(guildObj, channelData, guildObj.getIdLong()); case CATEGORY: - createCategory(guildObj, channelData, guildObj.getIdLong()); - break; + return createCategory(guildObj, channelData, guildObj.getIdLong()); case FORUM: - createForumChannel(guildObj, channelData, guildObj.getIdLong()); - break; + return createForumChannel(guildObj, channelData, guildObj.getIdLong()); + case MEDIA: + return createMediaChannel(guildObj, channelData, guildObj.getIdLong()); default: LOG.debug("Cannot create channel for type " + channelData.getInt("type")); + return null; } } @@ -1412,7 +1410,7 @@ public ForumChannel createForumChannel(GuildImpl guild, DataObject json, long gu .setParentCategory(json.getLong("parent_id", 0)) .setFlags(json.getInt("flags", 0)) .setDefaultReaction(json.optObject("default_reaction_emoji").orElse(null)) -// .setDefaultSortOrder(json.getInt("default_sort_order", -1)) + .setDefaultSortOrder(json.getInt("default_sort_order", -1)) .setDefaultLayout(json.getInt("default_forum_layout", -1)) .setName(json.getString("name")) .setTopic(json.getString("topic", null)) @@ -1427,7 +1425,59 @@ public ForumChannel createForumChannel(GuildImpl guild, DataObject json, long gu return channel; } - public ForumTagImpl createForumTag(ForumChannelImpl channel, DataObject json, int index) + public MediaChannel createMediaChannel(DataObject json, long guildId) + { + return createMediaChannel(null, json, guildId); + } + + public MediaChannel createMediaChannel(GuildImpl guild, DataObject json, long guildId) + { + boolean playbackCache = false; + final long id = json.getLong("id"); + MediaChannelImpl channel = (MediaChannelImpl) getJDA().getMediaChannelsView().get(id); + if (channel == null) + { + if (guild == null) + guild = (GuildImpl) getJDA().getGuildsView().get(guildId); + SnowflakeCacheViewImpl + guildView = guild.getMediaChannelsView(), + globalView = getJDA().getMediaChannelsView(); + try ( + UnlockHook vlock = guildView.writeLock(); + UnlockHook jlock = globalView.writeLock()) + { + channel = new MediaChannelImpl(id, guild); + guildView.getMap().put(id, channel); + playbackCache = globalView.getMap().put(id, channel) == null; + } + } + + if (api.isCacheFlagSet(CacheFlag.FORUM_TAGS)) + { + DataArray tags = json.getArray("available_tags"); + for (int i = 0; i < tags.length(); i++) + createForumTag(channel, tags.getObject(i), i); + } + + channel + .setParentCategory(json.getLong("parent_id", 0)) + .setFlags(json.getInt("flags", 0)) + .setDefaultReaction(json.optObject("default_reaction_emoji").orElse(null)) + .setDefaultSortOrder(json.getInt("default_sort_order", -1)) + .setName(json.getString("name")) + .setTopic(json.getString("topic", null)) + .setPosition(json.getInt("position")) + .setDefaultThreadSlowmode(json.getInt("default_thread_rate_limit_per_user", 0)) + .setSlowmode(json.getInt("rate_limit_per_user", 0)) + .setNSFW(json.getBoolean("nsfw")); + + createOverridesPass(channel, json.getArray("permission_overwrites")); + if (playbackCache) + getJDA().getEventCache().playbackCache(EventCache.Type.CHANNEL, id); + return channel; + } + + public ForumTagImpl createForumTag(IPostContainerMixin channel, DataObject json, int index) { final long id = json.getUnsignedLong("id"); SortedSnowflakeCacheViewImpl cache = channel.getAvailableTagCache(); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java index 82eb8c051b..d0e706a273 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -27,6 +27,7 @@ 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.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; @@ -110,6 +111,7 @@ public class GuildImpl implements Guild private final SortedSnowflakeCacheViewImpl stageChannelCache = new SortedSnowflakeCacheViewImpl<>(StageChannel.class, Channel::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl threadChannelCache = new SortedSnowflakeCacheViewImpl<>(ThreadChannel.class, Channel::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl forumChannelCache = new SortedSnowflakeCacheViewImpl<>(ForumChannel.class, Channel::getName, Comparator.naturalOrder()); + private final SortedSnowflakeCacheViewImpl mediaChannelCache = new SortedSnowflakeCacheViewImpl<>(MediaChannel.class, Channel::getName, Comparator.naturalOrder()); private final SortedSnowflakeCacheViewImpl roleCache = new SortedSnowflakeCacheViewImpl<>(Role.class, Role::getName, Comparator.reverseOrder()); private final SnowflakeCacheViewImpl emojicache = new SnowflakeCacheViewImpl<>(RichCustomEmoji.class, RichCustomEmoji::getName); private final SnowflakeCacheViewImpl stickerCache = new SnowflakeCacheViewImpl<>(GuildSticker.class, GuildSticker::getName); @@ -163,6 +165,7 @@ public void invalidate() SnowflakeCacheViewImpl threadView = getJDA().getThreadChannelsView(); SnowflakeCacheViewImpl newsView = getJDA().getNewsChannelView(); SnowflakeCacheViewImpl forumView = getJDA().getForumChannelsView(); + SnowflakeCacheViewImpl mediaView = getJDA().getMediaChannelsView(); SnowflakeCacheViewImpl voiceView = getJDA().getVoiceChannelsView(); SnowflakeCacheViewImpl categoryView = getJDA().getCategoriesView(); @@ -191,7 +194,12 @@ public void invalidate() try (UnlockHook hook = forumView.writeLock()) { getForumChannelCache() - .forEachUnordered(chan -> forumView.getMap().remove(chan.getIdLong())); + .forEachUnordered(chan -> forumView.getMap().remove(chan.getIdLong())); + } + try (UnlockHook hook = mediaView.writeLock()) + { + getMediaChannelsView() + .forEachUnordered(chan -> mediaView.getMap().remove(chan.getIdLong())); } try (UnlockHook hook = voiceView.writeLock()) { @@ -234,6 +242,76 @@ public void invalidate() } } + public void uncacheChannel(GuildChannel channel, boolean keepThreads) + { + long id = channel.getIdLong(); + switch (channel.getType()) + { + case TEXT: + api.getTextChannelsView().remove(id); + this.getTextChannelsView().remove(id); + break; + case NEWS: + api.getNewsChannelView().remove(id); + this.getNewsChannelView().remove(id); + break; + case MEDIA: + api.getMediaChannelsView().remove(id); + this.getMediaChannelsView().remove(id); + break; + case FORUM: + api.getForumChannelsView().remove(id); + this.getForumChannelsView().remove(id); + break; + case VOICE: + api.getVoiceChannelsView().remove(id); + this.getVoiceChannelsView().remove(id); + break; + case STAGE: + api.getStageChannelView().remove(id); + this.getStageChannelsView().remove(id); + break; + case CATEGORY: + api.getCategoriesView().remove(id); + this.getCategoriesView().remove(id); + break; + case GUILD_NEWS_THREAD: + case GUILD_PUBLIC_THREAD: + case GUILD_PRIVATE_THREAD: + api.getThreadChannelsView().remove(id); + this.getThreadChannelsView().remove(id); + break; + } + + if (!keepThreads && channel instanceof IThreadContainer) + { + // Remove dangling threads + SortedSnowflakeCacheViewImpl localView = this.getThreadChannelsView(); + SnowflakeCacheViewImpl globalView = api.getThreadChannelsView(); + Predicate predicate = thread -> channel.equals(thread.getParentChannel()); + + try (UnlockHook hook1 = localView.writeLock(); UnlockHook hook2 = globalView.writeLock()) + { + localView.getMap().valueCollection().removeIf(predicate); + globalView.getMap().valueCollection().removeIf(predicate); + } + } + + // This might be too presumptuous, Channel#getParent still returns null regardless if the category is uncached +// if (channel instanceof Category) +// { +// for (Channel chan : guild.getChannels()) +// { +// if (!(chan instanceof ICategorizableChannelMixin)) +// continue; +// +// ICategorizableChannelMixin categoizable = (ICategorizableChannelMixin) chan; +// if (categoizable.getParentCategoryIdLong() == id) +// categoizable.setParentCategory(0L); +// } +// } + } + @Nonnull @Override public RestAction> retrieveCommands(boolean withLocalizations) @@ -772,6 +850,13 @@ public SortedSnowflakeCacheView getForumChannelCache() return forumChannelCache; } + @Nonnull + @Override + public SnowflakeCacheView getMediaChannelCache() + { + return mediaChannelCache; + } + @Nonnull @Override public SortedSnowflakeCacheView getStageChannelCache() @@ -820,14 +905,16 @@ public List getChannels(boolean includeHidden) SnowflakeCacheViewImpl text = getTextChannelsView(); SnowflakeCacheViewImpl news = getNewsChannelView(); SnowflakeCacheViewImpl forum = getForumChannelsView(); + SnowflakeCacheViewImpl media = getMediaChannelsView(); - List channels = new ArrayList<>((int) (categories.size() + voice.size() + stage.size() + text.size() + news.size() + forum.size())); + List channels = new ArrayList<>((int) (categories.size() + voice.size() + stage.size() + text.size() + news.size() + forum.size() + media.size())); voice.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); stage.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); text.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); news.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); forum.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); + media.acceptStream(stream -> stream.filter(filterHidden).forEach(channels::add)); categories.forEach(category -> { @@ -1776,6 +1863,13 @@ public ChannelAction createForumChannel(@Nonnull String name, Cate return createChannel(ChannelType.FORUM, ForumChannel.class, name, parent); } + @Nonnull + @Override + public ChannelAction createMediaChannel(@Nonnull String name, @Nullable Category parent) + { + return createChannel(ChannelType.MEDIA, MediaChannel.class, name, parent); + } + @Nonnull @Override public ChannelAction createCategory(@Nonnull String name) @@ -2250,6 +2344,11 @@ public SortedSnowflakeCacheViewImpl getForumChannelsView() return forumChannelCache; } + public SortedSnowflakeCacheViewImpl getMediaChannelsView() + { + return mediaChannelCache; + } + public SortedSnowflakeCacheViewImpl getRolesView() { return roleCache; diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/AbstractChannelImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/AbstractChannelImpl.java index 6ee0c87800..6a97f8e0b0 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/channel/AbstractChannelImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/AbstractChannelImpl.java @@ -22,8 +22,8 @@ import net.dv8tion.jda.api.entities.channel.middleman.*; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.entities.channel.mixin.ChannelMixin; +import net.dv8tion.jda.internal.utils.ChannelUtil; import net.dv8tion.jda.internal.utils.EntityString; -import net.dv8tion.jda.internal.utils.Helpers; import javax.annotation.Nonnull; @@ -73,92 +73,99 @@ public T setName(String name) @Nonnull public PrivateChannel asPrivateChannel() { - return Helpers.safeChannelCast(this, PrivateChannel.class); + return ChannelUtil.safeChannelCast(this, PrivateChannel.class); } @Nonnull public TextChannel asTextChannel() { - return Helpers.safeChannelCast(this, TextChannel.class); + return ChannelUtil.safeChannelCast(this, TextChannel.class); } @Nonnull public NewsChannel asNewsChannel() { - return Helpers.safeChannelCast(this, NewsChannel.class); + return ChannelUtil.safeChannelCast(this, NewsChannel.class); } @Nonnull public VoiceChannel asVoiceChannel() { - return Helpers.safeChannelCast(this, VoiceChannel.class); + return ChannelUtil.safeChannelCast(this, VoiceChannel.class); } @Nonnull public StageChannel asStageChannel() { - return Helpers.safeChannelCast(this, StageChannel.class); + return ChannelUtil.safeChannelCast(this, StageChannel.class); } @Nonnull public ThreadChannel asThreadChannel() { - return Helpers.safeChannelCast(this, ThreadChannel.class); + return ChannelUtil.safeChannelCast(this, ThreadChannel.class); } @Nonnull public Category asCategory() { - return Helpers.safeChannelCast(this, Category.class); + return ChannelUtil.safeChannelCast(this, Category.class); } @Nonnull @Override public ForumChannel asForumChannel() { - return Helpers.safeChannelCast(this, ForumChannel.class); + return ChannelUtil.safeChannelCast(this, ForumChannel.class); + } + + @Nonnull + @Override + public MediaChannel asMediaChannel() + { + return ChannelUtil.safeChannelCast(this, MediaChannel.class); } @Nonnull public MessageChannel asMessageChannel() { - return Helpers.safeChannelCast(this, MessageChannel.class); + return ChannelUtil.safeChannelCast(this, MessageChannel.class); } @Nonnull public AudioChannel asAudioChannel() { - return Helpers.safeChannelCast(this, AudioChannel.class); + return ChannelUtil.safeChannelCast(this, AudioChannel.class); } @Nonnull public IThreadContainer asThreadContainer() { - return Helpers.safeChannelCast(this, IThreadContainer.class); + return ChannelUtil.safeChannelCast(this, IThreadContainer.class); } @Nonnull public GuildChannel asGuildChannel() { - return Helpers.safeChannelCast(this, GuildChannel.class); + return ChannelUtil.safeChannelCast(this, GuildChannel.class); } @Nonnull public GuildMessageChannel asGuildMessageChannel() { - return Helpers.safeChannelCast(this, GuildMessageChannel.class); + return ChannelUtil.safeChannelCast(this, GuildMessageChannel.class); } @Nonnull public StandardGuildChannel asStandardGuildChannel() { - return Helpers.safeChannelCast(this, StandardGuildChannel.class); + return ChannelUtil.safeChannelCast(this, StandardGuildChannel.class); } @Nonnull public StandardGuildMessageChannel asStandardGuildMessageChannel() { - return Helpers.safeChannelCast(this, StandardGuildMessageChannel.class); + return ChannelUtil.safeChannelCast(this, StandardGuildMessageChannel.class); } @Override diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/CategoryImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/CategoryImpl.java index 7018f18bb0..9dcbd3a5a4 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/CategoryImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/CategoryImpl.java @@ -104,6 +104,14 @@ public ChannelAction createForumChannel(@Nonnull String name) return trySync(action); } + @Nonnull + @Override + public ChannelAction createMediaChannel(@Nonnull String name) + { + ChannelAction action = getGuild().createMediaChannel(name, this); + return trySync(action); + } + @Nonnull @Override public CategoryOrderAction modifyTextChannelPositions() diff --git a/src/main/java/net/dv8tion/jda/internal/entities/ForumChannelImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ForumChannelImpl.java similarity index 79% rename from src/main/java/net/dv8tion/jda/internal/entities/ForumChannelImpl.java rename to src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ForumChannelImpl.java index 2000d654ac..42b46f53ec 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/ForumChannelImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ForumChannelImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.dv8tion.jda.internal.entities; +package net.dv8tion.jda.internal.entities.channel.concrete; import gnu.trove.map.TLongObjectMap; import net.dv8tion.jda.api.Permission; @@ -31,22 +31,14 @@ import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; import net.dv8tion.jda.api.managers.channel.concrete.ForumChannelManager; import net.dv8tion.jda.api.requests.restaction.ChannelAction; -import net.dv8tion.jda.api.requests.restaction.ForumPostAction; -import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction; import net.dv8tion.jda.api.utils.MiscUtil; import net.dv8tion.jda.api.utils.data.DataObject; -import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; -import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import net.dv8tion.jda.internal.entities.GuildImpl; import net.dv8tion.jda.internal.entities.channel.middleman.AbstractGuildChannelImpl; -import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IAgeRestrictedChannelMixin; -import net.dv8tion.jda.internal.entities.channel.mixin.attribute.ISlowmodeChannelMixin; -import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IWebhookContainerMixin; -import net.dv8tion.jda.internal.entities.channel.mixin.attribute.IThreadContainerMixin; -import net.dv8tion.jda.internal.entities.channel.mixin.attribute.ITopicChannelMixin; +import net.dv8tion.jda.internal.entities.channel.mixin.attribute.*; import net.dv8tion.jda.internal.entities.channel.mixin.middleman.StandardGuildChannelMixin; import net.dv8tion.jda.internal.entities.emoji.CustomEmojiImpl; import net.dv8tion.jda.internal.managers.channel.concrete.ForumChannelManagerImpl; -import net.dv8tion.jda.internal.requests.restaction.ForumPostActionImpl; import net.dv8tion.jda.internal.utils.Checks; import net.dv8tion.jda.internal.utils.cache.SortedSnowflakeCacheViewImpl; @@ -64,7 +56,7 @@ public class ForumChannelImpl extends AbstractGuildChannelImpl IAgeRestrictedChannelMixin, ISlowmodeChannelMixin, IWebhookContainerMixin, - IThreadContainerMixin, + IPostContainerMixin, ITopicChannelMixin { private final TLongObjectMap overrides = MiscUtil.newLongMap(); @@ -77,7 +69,7 @@ public class ForumChannelImpl extends AbstractGuildChannelImpl private int position; private int flags; private int slowmode; -// private int defaultSortOrder; + private int defaultSortOrder; private int defaultLayout; protected int defaultThreadSlowmode; @@ -114,6 +106,8 @@ public ChannelAction createCopy(@Nonnull Guild guild) .setSlowmode(slowmode) .setAvailableTags(getAvailableTags()) .setDefaultLayout(Layout.fromKey(defaultLayout)); + if (defaultSortOrder != -1) + action.setDefaultSortOrder(SortOrder.fromKey(defaultSortOrder)); if (defaultReaction instanceof UnicodeEmoji) action.setDefaultReaction(defaultReaction); if (guild.equals(getGuild())) @@ -195,40 +189,18 @@ public int getDefaultThreadSlowmode() return defaultThreadSlowmode; } -// @Nonnull -// @Override -// public SortOrder getDefaultSortOrder() -// { -// return SortOrder.fromKey(defaultSortOrder); -// } - - @Nonnull - @Override - public Layout getDefaultLayout() - { - return Layout.fromKey(defaultLayout); - } - @Nonnull @Override - public ForumPostAction createForumPost(@Nonnull String name, @Nonnull MessageCreateData message) + public SortOrder getDefaultSortOrder() { - checkPermission(Permission.MESSAGE_SEND); - return new ForumPostActionImpl(this, name, new MessageCreateBuilder().applyData(message)); + return SortOrder.fromKey(defaultSortOrder); } @Nonnull @Override - public ThreadChannelAction createThreadChannel(@Nonnull String name) - { - throw new UnsupportedOperationException("You cannot create threads without a message payload in forum channels! Use createForumPost(...) instead."); - } - - @Nonnull - @Override - public ThreadChannelAction createThreadChannel(@Nonnull String name, @Nonnull String messageId) + public Layout getDefaultLayout() { - throw new UnsupportedOperationException("You cannot create threads without a message payload in forum channels! Use createForumPost(...) instead."); + return Layout.fromKey(defaultLayout); } public int getRawFlags() @@ -236,10 +208,10 @@ public int getRawFlags() return flags; } -// public int getRawSortOrder() -// { -// return defaultSortOrder; -// } + public int getRawSortOrder() + { + return defaultSortOrder; + } public int getRawLayout() { @@ -287,12 +259,14 @@ public ForumChannelImpl setTopic(String topic) return this; } + @Override public ForumChannelImpl setFlags(int flags) { this.flags = flags; return this; } + @Override public ForumChannelImpl setDefaultReaction(DataObject emoji) { if (emoji != null && !emoji.isNull("emoji_id")) @@ -304,11 +278,12 @@ else if (emoji != null && !emoji.isNull("emoji_name")) return this; } -// public ForumChannelImpl setDefaultSortOrder(int defaultSortOrder) -// { -// this.defaultSortOrder = defaultSortOrder; -// return this; -// } + @Override + public ForumChannelImpl setDefaultSortOrder(int defaultSortOrder) + { + this.defaultSortOrder = defaultSortOrder; + return this; + } public ForumChannelImpl setDefaultLayout(int layout) { diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/MediaChannelImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/MediaChannelImpl.java new file mode 100644 index 0000000000..62bdb2dc37 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/MediaChannelImpl.java @@ -0,0 +1,273 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.channel.concrete; + +import gnu.trove.map.TLongObjectMap; +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.PermissionOverride; +import net.dv8tion.jda.api.entities.channel.ChannelFlag; +import net.dv8tion.jda.api.entities.channel.concrete.Category; +import net.dv8tion.jda.api.entities.channel.concrete.MediaChannel; +import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import net.dv8tion.jda.api.managers.channel.concrete.MediaChannelManager; +import net.dv8tion.jda.api.requests.restaction.ChannelAction; +import net.dv8tion.jda.api.utils.MiscUtil; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.entities.channel.middleman.AbstractGuildChannelImpl; +import net.dv8tion.jda.internal.entities.channel.mixin.attribute.*; +import net.dv8tion.jda.internal.entities.channel.mixin.middleman.StandardGuildChannelMixin; +import net.dv8tion.jda.internal.entities.emoji.CustomEmojiImpl; +import net.dv8tion.jda.internal.managers.channel.concrete.MediaChannelManagerImpl; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.cache.SortedSnowflakeCacheViewImpl; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +public class MediaChannelImpl extends AbstractGuildChannelImpl + implements MediaChannel, + GuildChannelUnion, + StandardGuildChannelMixin, + IAgeRestrictedChannelMixin, + ISlowmodeChannelMixin, + IWebhookContainerMixin, + IPostContainerMixin, + ITopicChannelMixin +{ + private final TLongObjectMap overrides = MiscUtil.newLongMap(); + private final SortedSnowflakeCacheViewImpl tagCache = new SortedSnowflakeCacheViewImpl<>(ForumTag.class, ForumTag::getName, Comparator.naturalOrder()); + + private Emoji defaultReaction; + private String topic; + private long parentCategoryId; + private boolean nsfw = false; + private int position; + private int flags; + private int slowmode; + private int defaultSortOrder; + protected int defaultThreadSlowmode; + + public MediaChannelImpl(long id, GuildImpl guild) + { + super(id, guild); + } + + @Nonnull + @Override + public MediaChannelManager getManager() + { + return new MediaChannelManagerImpl(this); + } + + @Nonnull + @Override + public List getMembers() + { + return Collections.unmodifiableList(getGuild().getMembers() + .stream() + .filter(m -> m.hasPermission(this, Permission.VIEW_CHANNEL)) + .collect(Collectors.toList())); + } + + @Nonnull + @Override + public ChannelAction createCopy(@Nonnull Guild guild) + { + Checks.notNull(guild, "Guild"); + ChannelAction action = guild.createMediaChannel(name) + .setNSFW(nsfw) + .setTopic(topic) + .setSlowmode(slowmode) + .setAvailableTags(getAvailableTags()); + if (defaultSortOrder != -1) + action.setDefaultSortOrder(SortOrder.fromKey(defaultSortOrder)); + if (defaultReaction instanceof UnicodeEmoji) + action.setDefaultReaction(defaultReaction); + if (guild.equals(getGuild())) + { + Category parent = getParentCategory(); + action.setDefaultReaction(defaultReaction); + if (parent != null) + action.setParent(parent); + for (PermissionOverride o : overrides.valueCollection()) + { + if (o.isMemberOverride()) + action.addMemberPermissionOverride(o.getIdLong(), o.getAllowedRaw(), o.getDeniedRaw()); + else + action.addRolePermissionOverride(o.getIdLong(), o.getAllowedRaw(), o.getDeniedRaw()); + } + } + return action; + } + + @Nonnull + @Override + public EnumSet getFlags() + { + return ChannelFlag.fromRaw(flags); + } + + @Nonnull + @Override + public SortedSnowflakeCacheViewImpl getAvailableTagCache() + { + return tagCache; + } + + @Override + public TLongObjectMap getPermissionOverrideMap() + { + return overrides; + } + + @Override + public boolean isNSFW() + { + return nsfw; + } + + @Override + public int getPositionRaw() + { + return position; + } + + @Override + public long getParentCategoryIdLong() + { + return parentCategoryId; + } + + @Override + public int getSlowmode() + { + return slowmode; + } + + @Override + public String getTopic() + { + return topic; + } + + @Override + public EmojiUnion getDefaultReaction() + { + return (EmojiUnion) defaultReaction; + } + + @Override + public int getDefaultThreadSlowmode() + { + return defaultThreadSlowmode; + } + + @Nonnull + @Override + public SortOrder getDefaultSortOrder() + { + return SortOrder.fromKey(defaultSortOrder); + } + + public int getRawFlags() + { + return flags; + } + + public int getRawSortOrder() + { + return defaultSortOrder; + } + + // Setters + + @Override + public MediaChannelImpl setParentCategory(long parentCategoryId) + { + this.parentCategoryId = parentCategoryId; + return this; + } + + @Override + public MediaChannelImpl setPosition(int position) + { + this.position = position; + return this; + } + + @Override + public MediaChannelImpl setDefaultThreadSlowmode(int defaultThreadSlowmode) + { + this.defaultThreadSlowmode = defaultThreadSlowmode; + return this; + } + + @Override + public MediaChannelImpl setNSFW(boolean nsfw) + { + this.nsfw = nsfw; + return this; + } + + @Override + public MediaChannelImpl setSlowmode(int slowmode) + { + this.slowmode = slowmode; + return this; + } + + @Override + public MediaChannelImpl setTopic(String topic) + { + this.topic = topic; + return this; + } + + public MediaChannelImpl setFlags(int flags) + { + this.flags = flags; + return this; + } + + public MediaChannelImpl setDefaultReaction(DataObject emoji) + { + if (emoji != null && !emoji.isNull("emoji_id")) + this.defaultReaction = new CustomEmojiImpl("", emoji.getUnsignedLong("emoji_id"), false); + else if (emoji != null && !emoji.isNull("emoji_name")) + this.defaultReaction = Emoji.fromUnicode(emoji.getString("emoji_name")); + else + this.defaultReaction = null; + return this; + } + + public MediaChannelImpl setDefaultSortOrder(int defaultSortOrder) + { + this.defaultSortOrder = defaultSortOrder; + return this; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ThreadChannelImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ThreadChannelImpl.java index ce4e5fd45f..f01c5485c4 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ThreadChannelImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/concrete/ThreadChannelImpl.java @@ -149,6 +149,9 @@ public List getMembers() @Override public IThreadContainerUnion getParentChannel() { + IThreadContainer realChannel = getGuild().getChannelById(IThreadContainer.class, parentChannel.getIdLong()); + if (realChannel != null) + parentChannel = (IThreadContainerUnion) realChannel; return parentChannel; } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/attribute/IPostContainerMixin.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/attribute/IPostContainerMixin.java new file mode 100644 index 0000000000..114e1e770e --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/attribute/IPostContainerMixin.java @@ -0,0 +1,66 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.channel.mixin.attribute; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; +import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.requests.restaction.ForumPostAction; +import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import net.dv8tion.jda.internal.requests.restaction.ForumPostActionImpl; +import net.dv8tion.jda.internal.utils.cache.SortedSnowflakeCacheViewImpl; + +import javax.annotation.Nonnull; + +public interface IPostContainerMixin> extends IPostContainer, IThreadContainerMixin +{ + @Nonnull + @Override + SortedSnowflakeCacheViewImpl getAvailableTagCache(); + + @Nonnull + @Override + default ForumPostAction createForumPost(@Nonnull String name, @Nonnull MessageCreateData message) + { + checkPermission(Permission.MESSAGE_SEND); + return new ForumPostActionImpl(this, name, new MessageCreateBuilder().applyData(message)); + } + + @Nonnull + @Override + default ThreadChannelAction createThreadChannel(@Nonnull String name) + { + throw new UnsupportedOperationException("You cannot create threads without a message payload in forum/media channels! Use createForumPost(...) instead."); + } + + @Nonnull + @Override + default ThreadChannelAction createThreadChannel(@Nonnull String name, @Nonnull String messageId) + { + throw new UnsupportedOperationException("You cannot create threads without a message payload in forum/media channels! Use createForumPost(...) instead."); + } + + T setDefaultReaction(DataObject emoji); + T setDefaultSortOrder(int defaultSortOrder); + T setFlags(int flags); + + int getRawSortOrder(); + int getRawFlags(); +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ChannelCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ChannelCreateHandler.java index 5454dad81c..c153eb8d83 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/ChannelCreateHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/ChannelCreateHandler.java @@ -68,6 +68,7 @@ private Channel buildChannel(ChannelType type, DataObject content, long guildId) case STAGE: return builder.createStageChannel(content, guildId); case CATEGORY: return builder.createCategory(content, guildId); case FORUM: return builder.createForumChannel(content, guildId); + case MEDIA: return builder.createMediaChannel(content, guildId); default: return null; diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java index 2ef2ccdc6f..a37aa145ca 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/ChannelDeleteHandler.java @@ -16,7 +16,6 @@ package net.dv8tion.jda.internal.handle; -import net.dv8tion.jda.api.entities.ScheduledEvent; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent; @@ -156,6 +155,22 @@ protected Long handleInternally(DataObject content) channel)); break; } + case MEDIA: + { + MediaChannel channel = getJDA().getMediaChannelsView().remove(channelId); + if (channel == null || guild == null) + { + WebSocketClient.LOG.debug("CHANNEL_DELETE attempted to delete a media channel that is not yet cached. JSON: {}", content); + return null; + } + + guild.getMediaChannelsView().remove(channel.getIdLong()); + getJDA().handleEvent( + new ChannelDeleteEvent( + getJDA(), responseNumber, + channel)); + break; + } case PRIVATE: { SnowflakeCacheViewImpl privateView = getJDA().getPrivateChannelsView(); diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ChannelUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ChannelUpdateHandler.java index 842cc20218..e36ba17698 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/ChannelUpdateHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/ChannelUpdateHandler.java @@ -25,11 +25,16 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.IPermissionHolder; import net.dv8tion.jda.api.entities.PermissionOverride; +import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelFlag; import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; -import net.dv8tion.jda.api.entities.channel.concrete.*; +import net.dv8tion.jda.api.entities.channel.concrete.Category; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.events.channel.forum.ForumTagAddEvent; import net.dv8tion.jda.api.events.channel.forum.ForumTagRemoveEvent; @@ -45,18 +50,22 @@ import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; -import net.dv8tion.jda.internal.entities.*; -import net.dv8tion.jda.internal.entities.channel.concrete.NewsChannelImpl; -import net.dv8tion.jda.internal.entities.channel.concrete.TextChannelImpl; +import net.dv8tion.jda.internal.entities.EntityBuilder; +import net.dv8tion.jda.internal.entities.ForumTagImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.entities.PermissionOverrideImpl; +import net.dv8tion.jda.internal.entities.channel.concrete.ForumChannelImpl; import net.dv8tion.jda.internal.entities.channel.middleman.AbstractGuildChannelImpl; import net.dv8tion.jda.internal.entities.channel.mixin.attribute.*; import net.dv8tion.jda.internal.entities.channel.mixin.middleman.AudioChannelMixin; +import net.dv8tion.jda.internal.entities.channel.mixin.middleman.MessageChannelMixin; import net.dv8tion.jda.internal.requests.WebSocketClient; import net.dv8tion.jda.internal.utils.UnlockHook; import net.dv8tion.jda.internal.utils.cache.SnowflakeCacheViewImpl; import net.dv8tion.jda.internal.utils.cache.SortedSnowflakeCacheViewImpl; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Objects; @@ -97,6 +106,8 @@ protected Long handleInternally(DataObject content) //Detect if we changed the channel type at all and reconstruct the channel entity if needed channel = handleChannelTypeChange(channel, content, type); + if (channel == null) + return null; //Handle shared properties @@ -132,6 +143,9 @@ protected Long handleInternally(DataObject content) if (channel instanceof AudioChannelMixin) handleAudioChannel((AudioChannelMixin) channel, content); + if (channel instanceof IPostContainerMixin) + handlePostContainer((IPostContainerMixin) channel, content); + //Handle concrete type specific properties switch (type) @@ -139,38 +153,9 @@ protected Long handleInternally(DataObject content) case FORUM: ForumChannelImpl forumChannel = (ForumChannelImpl) channel; - int flags = content.getInt("flags", 0); -// int sortOrder = content.getInt("default_sort_order", ((ForumChannelImpl) channel).getRawSortOrder()); int layout = content.getInt("default_forum_layout", ((ForumChannelImpl) channel).getRawLayout()); - EmojiUnion defaultReaction = content.optObject("default_reaction_emoji") - .map(json -> EntityBuilder.createEmoji(json, "emoji_name", "emoji_id")) - .orElse(null); - - int oldFlags = forumChannel.getRawFlags(); -// int oldSortOrder = forumChannel.getRawSortOrder(); int oldLayout = forumChannel.getRawLayout(); - EmojiUnion oldDefaultReaction = forumChannel.getDefaultReaction(); - content.optArray("available_tags").ifPresent( - array -> handleTagsUpdate(forumChannel, array) - ); - - if (oldFlags != flags) - { - forumChannel.setFlags(flags); - getJDA().handleEvent( - new ChannelUpdateFlagsEvent( - getJDA(), responseNumber, - forumChannel, ChannelFlag.fromRaw(oldFlags), ChannelFlag.fromRaw(flags))); - } -// if (oldSortOrder != sortOrder) -// { -// forumChannel.setDefaultSortOrder(sortOrder); -// getJDA().handleEvent( -// new ChannelUpdateDefaultSortOrderEvent( -// getJDA(), responseNumber, -// forumChannel, ForumChannel.SortOrder.fromKey(oldSortOrder), ForumChannel.SortOrder.fromKey(sortOrder))); -// } if (oldLayout != layout) { forumChannel.setDefaultLayout(layout); @@ -179,14 +164,6 @@ protected Long handleInternally(DataObject content) getJDA(), responseNumber, forumChannel, ForumChannel.Layout.fromKey(oldLayout), ForumChannel.Layout.fromKey(layout))); } - if (!Objects.equals(oldDefaultReaction, defaultReaction)) - { - forumChannel.setDefaultReaction(content.optObject("default_reaction_emoji").orElse(null)); - getJDA().handleEvent( - new ChannelUpdateDefaultReactionEvent( - getJDA(), responseNumber, - forumChannel, oldDefaultReaction, defaultReaction)); - } break; case VOICE: case TEXT: @@ -216,46 +193,52 @@ private AbstractGuildChannelImpl handleChannelTypeChange(AbstractGuildChannel EntityBuilder builder = getJDA().getEntityBuilder(); GuildImpl guild = channel.getGuild(); - if (newChannelType == ChannelType.TEXT) - { - //This assumes that if we're moving to a TextChannel that we're transitioning from a NewsChannel - NewsChannel newsChannel = (NewsChannel) channel; - getJDA().getNewsChannelView().remove(newsChannel.getIdLong()); - guild.getNewsChannelView().remove(newsChannel.getIdLong()); + ChannelType oldType = channel.getType(); - TextChannelImpl textChannel = (TextChannelImpl) builder.createTextChannel(guild, content, guild.getIdLong()); + EnumSet expectedTypes = EnumSet.complementOf(EnumSet.of( + ChannelType.PRIVATE, + ChannelType.GROUP, + ChannelType.GUILD_NEWS_THREAD, + ChannelType.GUILD_PRIVATE_THREAD, + ChannelType.GUILD_PUBLIC_THREAD, + ChannelType.UNKNOWN + )); - //CHANNEL_UPDATE doesn't track last_message_id, so make sure to copy it over. - textChannel.setLatestMessageIdLong(newsChannel.getLatestMessageIdLong()); + if (!expectedTypes.contains(oldType) || !expectedTypes.contains(newChannelType)) + { + WebSocketClient.LOG.warn("Unexpected channel type change {}->{}, discarding from cache.", channel.getType().getId(), content.getInt("type")); + guild.uncacheChannel(channel, false); + return null; + } - getJDA().handleEvent( - new ChannelUpdateTypeEvent( - getJDA(), responseNumber, - textChannel, ChannelType.NEWS, ChannelType.TEXT)); + guild.uncacheChannel(channel, true); + Channel newChannel = builder.createGuildChannel(guild, content); - return textChannel; + if (channel instanceof IThreadContainer) + { + if (newChannel instanceof IThreadContainer) + { + // Refresh thread parents to avoid keeping strong references to parent channel + guild.getThreadChannelCache().forEachUnordered(ThreadChannel::getParentChannel); + } + else + { + // Change introduced dangling thread channels (with no parent) + WebSocketClient.LOG.error("ThreadContainer channel transitioned into type that is not ThreadContainer? {} -> {}", channel.getType(), newChannel.getType()); + } } - if (newChannelType == ChannelType.NEWS) + if (newChannel instanceof MessageChannelMixin && channel instanceof MessageChannel) { - //This assumes that if we're moving to a NewsChannel that we're transitioning from a TextChannel - TextChannel textChannel = (TextChannel) channel; - getJDA().getTextChannelsView().remove(textChannel.getIdLong()); - guild.getTextChannelsView().remove(textChannel.getIdLong()); - - NewsChannelImpl newsChannel = (NewsChannelImpl) builder.createNewsChannel(guild, content, guild.getIdLong()); - - //CHANNEL_UPDATE doesn't track last_message_id, so make sure to copy it over. - newsChannel.setLatestMessageIdLong(textChannel.getLatestMessageIdLong()); - - getJDA().handleEvent( - new ChannelUpdateTypeEvent( - getJDA(), responseNumber, - newsChannel, ChannelType.TEXT, ChannelType.NEWS)); - - return newsChannel; + long latestMessageIdLong = ((MessageChannel) channel).getLatestMessageIdLong(); + ((MessageChannelMixin) channel).setLatestMessageIdLong(latestMessageIdLong); } + getJDA().handleEvent( + new ChannelUpdateTypeEvent( + getJDA(), responseNumber, + newChannel, oldType, newChannelType)); + return channel; } @@ -388,7 +371,7 @@ private void handleHideChildThreads(IThreadContainer channel) } } - private void handleTagsUpdate(ForumChannelImpl channel, DataArray tags) + private void handleTagsUpdate(IPostContainerMixin channel, DataArray tags) { if (!api.isCacheFlagSet(CacheFlag.FORUM_TAGS)) return; @@ -572,4 +555,50 @@ private void handleAudioChannel(AudioChannelMixin channel, DataObject content channel, Region.fromKey(oldRegion), Region.fromKey(regionRaw))); } } + + private void handlePostContainer(IPostContainerMixin channel, DataObject content) + { + content.optArray("available_tags").ifPresent( + array -> handleTagsUpdate(channel, array) + ); + + EmojiUnion defaultReaction = content.optObject("default_reaction_emoji") + .map(json -> EntityBuilder.createEmoji(json, "emoji_name", "emoji_id")) + .orElse(null); + EmojiUnion oldDefaultReaction = channel.getDefaultReaction(); + + if (!Objects.equals(oldDefaultReaction, defaultReaction)) + { + channel.setDefaultReaction(content.optObject("default_reaction_emoji").orElse(null)); + getJDA().handleEvent( + new ChannelUpdateDefaultReactionEvent( + getJDA(), responseNumber, + channel, oldDefaultReaction, defaultReaction)); + } + + + int sortOrder = content.getInt("default_sort_order", channel.getRawSortOrder()); + int oldSortOrder = channel.getRawSortOrder(); + + if (oldSortOrder != sortOrder) + { + channel.setDefaultSortOrder(sortOrder); + getJDA().handleEvent( + new ChannelUpdateDefaultSortOrderEvent( + getJDA(), responseNumber, + channel, IPostContainer.SortOrder.fromKey(oldSortOrder))); + } + + int newFlags = content.getInt("flags", 0); + int oldFlags = channel.getRawFlags(); + + if (oldFlags != newFlags) + { + channel.setFlags(newFlags); + getJDA().handleEvent( + new ChannelUpdateFlagsEvent( + getJDA(), responseNumber, + channel, ChannelFlag.fromRaw(oldFlags), ChannelFlag.fromRaw(newFlags))); + } + } } diff --git a/src/main/java/net/dv8tion/jda/internal/handle/ThreadUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/ThreadUpdateHandler.java index fb25f5e74c..b0efe4071d 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/ThreadUpdateHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/ThreadUpdateHandler.java @@ -57,6 +57,10 @@ protected Long handleInternally(DataObject content) //Refer to the documentation for more info: https://discord.com/developers/docs/topics/threads#unarchiving-a-thread if (thread == null) { + // This seems to never be true but its better to check + if (content.getObject("thread_metadata").getBoolean("archived")) + return null; + //Technically, when the ThreadChannel is unarchived the archive_timestamp (getTimeArchiveInfoLastModified) changes // as well, but we don't have the original value because we didn't have the thread in memory, so we can't // provide an entirely accurate ChannelUpdateArchiveTimestampEvent. Not sure how much that'll matter. diff --git a/src/main/java/net/dv8tion/jda/internal/managers/channel/ChannelManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/channel/ChannelManagerImpl.java index e915f2b820..9db8a80eb9 100644 --- a/src/main/java/net/dv8tion/jda/internal/managers/channel/ChannelManagerImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/managers/channel/ChannelManagerImpl.java @@ -26,7 +26,9 @@ import net.dv8tion.jda.api.entities.channel.ChannelFlag; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.IPermissionContainer; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; +import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.*; import net.dv8tion.jda.api.entities.channel.forums.BaseForumTag; import net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake; @@ -45,6 +47,7 @@ import net.dv8tion.jda.internal.entities.channel.mixin.middleman.GuildChannelMixin; import net.dv8tion.jda.internal.managers.ManagerBase; import net.dv8tion.jda.internal.requests.restaction.PermOverrideData; +import net.dv8tion.jda.internal.utils.ChannelUtil; import net.dv8tion.jda.internal.utils.Checks; import net.dv8tion.jda.internal.utils.PermissionUtil; import okhttp3.RequestBody; @@ -60,12 +63,6 @@ @SuppressWarnings("unchecked") //We do a lot of (M) and (T) casting that we know is correct but the compiler warns about. public class ChannelManagerImpl> extends ManagerBase implements ChannelManager { - private static final EnumSet SLOWMODE_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.FORUM, - ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_NEWS_THREAD, ChannelType.GUILD_PRIVATE_THREAD, - ChannelType.STAGE, ChannelType.VOICE); - private static final EnumSet NSFW_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.VOICE, ChannelType.FORUM, ChannelType.NEWS, ChannelType.STAGE); - private static final EnumSet TOPIC_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.FORUM, ChannelType.NEWS); - protected T channel; protected final EnumSet flags; @@ -74,6 +71,7 @@ public class ChannelManagerImpl appliedTags; protected Emoji defaultReactionEmoji; protected int defaultLayout; + protected int defaultSortOrder; protected ChannelType type; protected String name; protected String parent; @@ -85,6 +83,7 @@ public class ChannelManagerImpl= 0, "Slowmode per user must be between 0 and %d (seconds)!", ISlowmodeChannel.MAX_SLOWMODE); this.slowmode = slowmode; set |= SLOWMODE; return (M) this; } + @Nonnull + @CheckReturnValue + public M setDefaultThreadSlowmode(int slowmode) + { + Checks.check(channel instanceof IThreadContainer, "Cannot set default thread slowmode on channels of type %s!", channel.getType()); + Checks.check(slowmode <= ISlowmodeChannel.MAX_SLOWMODE && slowmode >= 0, "Slowmode per user must be between 0 and %d (seconds)!", ISlowmodeChannel.MAX_SLOWMODE); + this.defaultThreadSlowmode = slowmode; + set |= DEFAULT_THREAD_SLOWMODE; + return (M) this; + } + @Nonnull @CheckReturnValue public M setUserLimit(int userLimit) @@ -579,6 +597,8 @@ public M setInvitable(boolean invitable) public M setPinned(boolean pinned) { + if (!type.isThread()) + throw new IllegalStateException("Can only pin threads."); if (pinned) flags.add(ChannelFlag.PINNED); else @@ -589,6 +609,8 @@ public M setPinned(boolean pinned) public M setTagRequired(boolean requireTag) { + if (!(channel instanceof IPostContainer)) + throw new IllegalStateException("Can only set tag required flag on forum/media channels."); if (requireTag) flags.add(ChannelFlag.REQUIRE_TAG); else @@ -597,10 +619,22 @@ public M setTagRequired(boolean requireTag) return (M) this; } + public M setHideMediaDownloadOption(boolean hideOption) + { + if (!(channel instanceof MediaChannel)) + throw new IllegalStateException("Can only set hide media download flag on media channels."); + if (hideOption) + flags.add(ChannelFlag.HIDE_MEDIA_DOWNLOAD_OPTIONS); + else + flags.remove(ChannelFlag.HIDE_MEDIA_DOWNLOAD_OPTIONS); + set |= HIDE_MEDIA_DOWNLOAD_OPTIONS; + return (M) this; + } + public M setAvailableTags(List tags) { - if (type != ChannelType.FORUM) - throw new IllegalStateException("Can only set available tags on forum channels."); + if (!(channel instanceof IPostContainer)) + throw new IllegalStateException("Can only set available tags on forum/media channels."); Checks.noneNull(tags, "Available Tags"); this.availableTags = new ArrayList<>(tags); set |= AVAILABLE_TAGS; @@ -612,13 +646,13 @@ public M setAppliedTags(Collection tags) if (type != ChannelType.GUILD_PUBLIC_THREAD) throw new IllegalStateException("Can only set applied tags on forum post thread channels."); Checks.noneNull(tags, "Applied Tags"); - Checks.check(tags.size() <= ForumChannel.MAX_POST_TAGS, "Cannot apply more than %d tags to a post thread!", ForumChannel.MAX_POST_TAGS); + Checks.check(tags.size() <= IPostContainer.MAX_POST_TAGS, "Cannot apply more than %d tags to a post thread!", ForumChannel.MAX_POST_TAGS); ThreadChannel thread = (ThreadChannel) getChannel(); IThreadContainerUnion parentChannel = thread.getParentChannel(); - if (!(parentChannel instanceof ForumChannel)) - throw new IllegalStateException("Cannot apply tags to threads outside of forum channels."); + if (!(parentChannel instanceof IPostContainer)) + throw new IllegalStateException("Cannot apply tags to threads outside of forum/media channels."); if (tags.isEmpty() && parentChannel.asForumChannel().isTagRequired()) - throw new IllegalArgumentException("Cannot remove all tags from a forum post which requires at least one tag! See ForumChannel#isRequireTag()"); + throw new IllegalArgumentException("Cannot remove all tags from a forum post which requires at least one tag! See IPostContainer#isRequireTag()"); this.appliedTags = tags.stream().map(ISnowflake::getId).collect(Collectors.toList()); set |= APPLIED_TAGS; return (M) this; @@ -626,8 +660,8 @@ public M setAppliedTags(Collection tags) public M setDefaultReaction(Emoji emoji) { - if (type != ChannelType.FORUM) - throw new IllegalStateException("Can only set default reaction on forum channels."); + if (!(channel instanceof IPostContainer)) + throw new IllegalStateException("Can only set default reaction on forum/media channels."); this.defaultReactionEmoji = emoji; set |= DEFAULT_REACTION; return (M) this; @@ -645,6 +679,18 @@ public M setDefaultLayout(ForumChannel.Layout layout) return (M) this; } + public M setDefaultSortOrder(IPostContainer.SortOrder sortOrder) + { + if (!(channel instanceof IPostContainer)) + throw new IllegalStateException("Can only set default layout on forum/media channels."); + Checks.notNull(sortOrder, "SortOrder"); + if (sortOrder == IPostContainer.SortOrder.UNKNOWN) + throw new IllegalStateException("SortOrder type cannot be UNKNOWN."); + this.defaultSortOrder = sortOrder.getKey(); + set |= DEFAULT_SORT_ORDER; + return (M) this; + } + @Override protected RequestBody finalizeData() { @@ -661,6 +707,8 @@ protected RequestBody finalizeData() frame.put("nsfw", nsfw); if (shouldUpdate(SLOWMODE)) frame.put("rate_limit_per_user", slowmode); + if (shouldUpdate(DEFAULT_THREAD_SLOWMODE)) + frame.put("default_thread_rate_limit_per_user", defaultThreadSlowmode); if (shouldUpdate(USERLIMIT)) frame.put("user_limit", userlimit); if (shouldUpdate(BITRATE)) @@ -681,7 +729,7 @@ protected RequestBody finalizeData() frame.put("available_tags", DataArray.fromCollection(availableTags)); if (shouldUpdate(APPLIED_TAGS)) frame.put("applied_tags", DataArray.fromCollection(appliedTags)); - if (shouldUpdate(PINNED | REQUIRE_TAG)) + if (shouldUpdate(PINNED | REQUIRE_TAG | HIDE_MEDIA_DOWNLOAD_OPTIONS)) frame.put("flags", ChannelFlag.getRaw(flags)); if (shouldUpdate(DEFAULT_REACTION)) { @@ -694,6 +742,8 @@ else if (defaultReactionEmoji instanceof UnicodeEmoji) } if (shouldUpdate(DEFAULT_LAYOUT)) frame.put("default_forum_layout", defaultLayout); + if (shouldUpdate(DEFAULT_SORT_ORDER)) + frame.put("default_sort_order", defaultSortOrder); withLock(lock, (lock) -> { diff --git a/src/main/java/net/dv8tion/jda/internal/managers/channel/concrete/MediaChannelManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/channel/concrete/MediaChannelManagerImpl.java new file mode 100644 index 0000000000..527478b71c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/managers/channel/concrete/MediaChannelManagerImpl.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.managers.channel.concrete; + +import net.dv8tion.jda.api.entities.channel.concrete.MediaChannel; +import net.dv8tion.jda.api.managers.channel.concrete.MediaChannelManager; +import net.dv8tion.jda.internal.managers.channel.ChannelManagerImpl; + +public class MediaChannelManagerImpl extends ChannelManagerImpl implements MediaChannelManager +{ + public MediaChannelManagerImpl(MediaChannel channel) + { + super(channel); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java index 1467d81cba..7f96b98eb9 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java @@ -809,6 +809,7 @@ protected void invalidate() api.getStageChannelView().clear(); api.getThreadChannelsView().clear(); api.getForumChannelsView().clear(); + api.getMediaChannelsView().clear(); api.getGuildsView().clear(); api.getUsersView().clear(); diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/ChannelActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ChannelActionImpl.java index 722da24290..d5a245fbbd 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/ChannelActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ChannelActionImpl.java @@ -24,6 +24,7 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.attribute.ISlowmodeChannel; import net.dv8tion.jda.api.entities.channel.concrete.Category; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; @@ -43,8 +44,8 @@ import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.entities.EntityBuilder; -import net.dv8tion.jda.internal.requests.restaction.AuditableRestActionImpl; -import net.dv8tion.jda.internal.requests.restaction.PermOverrideData; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.utils.ChannelUtil; import net.dv8tion.jda.internal.utils.Checks; import net.dv8tion.jda.internal.utils.PermissionUtil; import okhttp3.RequestBody; @@ -60,12 +61,6 @@ public class ChannelActionImpl extends AuditableRestActionImpl implements ChannelAction { - private static final EnumSet SLOWMODE_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.FORUM, - ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_NEWS_THREAD, ChannelType.GUILD_PRIVATE_THREAD, - ChannelType.STAGE, ChannelType.VOICE); - private static final EnumSet NSFW_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.VOICE, ChannelType.FORUM, ChannelType.NEWS, ChannelType.STAGE); - private static final EnumSet TOPIC_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.FORUM, ChannelType.NEWS); - protected final TLongObjectMap overrides = new TLongObjectHashMap<>(); protected final Guild guild; protected final Class clazz; @@ -82,6 +77,7 @@ public class ChannelActionImpl extends AuditableRestActi // --text/forum/voice only-- protected Integer slowmode = null; + protected Integer defaultThreadSlowmode = null; // --text/forum/voice/news-- protected String topic = null; @@ -96,6 +92,7 @@ public class ChannelActionImpl extends AuditableRestActi // --forum only-- protected Integer defaultLayout = null; + protected Integer defaultSortOrder = null; public ChannelActionImpl(Class clazz, String name, Guild guild, ChannelType type) { @@ -190,11 +187,11 @@ public ChannelActionImpl setPosition(Integer position) @CheckReturnValue public ChannelActionImpl setTopic(String topic) { - Checks.checkSupportedChannelTypes(TOPIC_SUPPORTED, type, "Topic"); + Checks.checkSupportedChannelTypes(ChannelUtil.TOPIC_SUPPORTED, type, "Topic"); if (topic != null) { - if (type == ChannelType.FORUM) - Checks.notLonger(topic, ForumChannel.MAX_FORUM_TOPIC_LENGTH, "Topic"); + if (ChannelUtil.POST_CONTAINERS.contains(type)) + Checks.notLonger(topic, IPostContainer.MAX_POST_CONTAINER_TOPIC_LENGTH, "Topic"); else Checks.notLonger(topic, StandardGuildMessageChannel.MAX_TOPIC_LENGTH, "Topic"); } @@ -207,7 +204,7 @@ public ChannelActionImpl setTopic(String topic) @CheckReturnValue public ChannelActionImpl setNSFW(boolean nsfw) { - Checks.checkSupportedChannelTypes(NSFW_SUPPORTED, type, "NSFW (age-restricted)"); + Checks.checkSupportedChannelTypes(ChannelUtil.NSFW_SUPPORTED, type, "NSFW (age-restricted)"); this.nsfw = nsfw; return this; } @@ -217,18 +214,27 @@ public ChannelActionImpl setNSFW(boolean nsfw) @CheckReturnValue public ChannelActionImpl setSlowmode(int slowmode) { - Checks.checkSupportedChannelTypes(SLOWMODE_SUPPORTED, type, "Slowmode"); + Checks.checkSupportedChannelTypes(ChannelUtil.SLOWMODE_SUPPORTED, type, "Slowmode"); Checks.check(slowmode <= ISlowmodeChannel.MAX_SLOWMODE && slowmode >= 0, "Slowmode must be between 0 and %d (seconds)!", ISlowmodeChannel.MAX_SLOWMODE); this.slowmode = slowmode; return this; } + @Nonnull + @Override + public ChannelAction setDefaultThreadSlowmode(int slowmode) + { + Checks.checkSupportedChannelTypes(ChannelUtil.THREAD_CONTAINERS, type, "Default Thread Slowmode"); + Checks.check(slowmode <= ISlowmodeChannel.MAX_SLOWMODE && slowmode >= 0, "Slowmode must be between 0 and %d (seconds)!", ISlowmodeChannel.MAX_SLOWMODE); + this.defaultThreadSlowmode = slowmode; + return this; + } + @Nonnull @Override public ChannelAction setDefaultReaction(@Nullable Emoji emoji) { - if (type != ChannelType.FORUM) - throw new UnsupportedOperationException("Can only set default reaction emoji on a ForumChannel!"); + Checks.checkSupportedChannelTypes(ChannelUtil.POST_CONTAINERS, type, "Default Reaction"); this.defaultReactionEmoji = emoji; return this; } @@ -239,18 +245,27 @@ public ChannelAction setDefaultLayout(@Nonnull ForumChannel.Layout layout) { Checks.checkSupportedChannelTypes(EnumSet.of(ChannelType.FORUM), type, "Default Layout"); Checks.notNull(layout, "layout"); - if (layout == ForumChannel.Layout.UNKNOWN) - throw new IllegalStateException("Layout type cannot be UNKNOWN."); + Checks.check(layout != ForumChannel.Layout.UNKNOWN, "Layout type cannot be UNKNOWN."); this.defaultLayout = layout.getKey(); return this; } + @Nonnull + @Override + public ChannelAction setDefaultSortOrder(@Nonnull IPostContainer.SortOrder sortOrder) + { + Checks.checkSupportedChannelTypes(ChannelUtil.POST_CONTAINERS, type, "Default Sort Order"); + Checks.notNull(sortOrder, "SortOrder"); + Checks.check(sortOrder != IPostContainer.SortOrder.UNKNOWN, "Sort Order cannot be UNKNOWN."); + this.defaultSortOrder = sortOrder.getKey(); + return this; + } + @Nonnull @Override public ChannelAction setAvailableTags(@Nonnull List tags) { - if (type != ChannelType.FORUM) - throw new UnsupportedOperationException("Can only set available tags on a ForumChannel!"); + Checks.checkSupportedChannelTypes(ChannelUtil.POST_CONTAINERS, type, "Available Tags"); Checks.noneNull(tags, "Tags"); this.availableTags = new ArrayList<>(tags); return this; @@ -417,6 +432,8 @@ protected RequestBody finalizeData() //Text and Forum if (slowmode != null) object.put("rate_limit_per_user", slowmode); + if (defaultThreadSlowmode != null) + object.put("default_thread_rate_limit_per_user", defaultThreadSlowmode); //Text, Forum, and News if (topic != null && !topic.isEmpty()) @@ -424,13 +441,17 @@ protected RequestBody finalizeData() if (nsfw != null) object.put("nsfw", nsfw); - //Forum only + //Forum/Media only if (defaultReactionEmoji instanceof CustomEmoji) object.put("default_reaction_emoji", DataObject.empty().put("emoji_id", ((CustomEmoji) defaultReactionEmoji).getId())); else if (defaultReactionEmoji instanceof UnicodeEmoji) object.put("default_reaction_emoji", DataObject.empty().put("emoji_name", defaultReactionEmoji.getName())); if (availableTags != null) object.put("available_tags", DataArray.fromCollection(availableTags)); + if (defaultSortOrder != null) + object.put("default_sort_order", defaultSortOrder); + + //Forum only if (defaultLayout != null) object.put("default_forum_layout", defaultLayout); @@ -451,31 +472,10 @@ else if (defaultReactionEmoji instanceof UnicodeEmoji) protected void handleSuccess(Response response, Request request) { EntityBuilder builder = api.getEntityBuilder(); - GuildChannel channel; - switch (type) - { - case TEXT: - channel = builder.createTextChannel(response.getObject(), guild.getIdLong()); - break; - case NEWS: - channel = builder.createNewsChannel(response.getObject(), guild.getIdLong()); - break; - case VOICE: - channel = builder.createVoiceChannel(response.getObject(), guild.getIdLong()); - break; - case STAGE: - channel = builder.createStageChannel(response.getObject(), guild.getIdLong()); - break; - case CATEGORY: - channel = builder.createCategory(response.getObject(), guild.getIdLong()); - break; - case FORUM: - channel = builder.createForumChannel(response.getObject(), guild.getIdLong()); - break; - default: - request.onFailure(new IllegalStateException("Created channel of unknown type!")); - return; - } - request.onSuccess(clazz.cast(channel)); + GuildChannel channel = builder.createGuildChannel((GuildImpl) guild, response.getObject()); + if (channel == null) + request.onFailure(new IllegalStateException("Created channel of unknown type!")); + else + request.onSuccess(clazz.cast(channel)); } } diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/ForumPostActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ForumPostActionImpl.java index 07285efa32..e40c0cff0b 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/ForumPostActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/ForumPostActionImpl.java @@ -22,6 +22,7 @@ import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IPostContainer; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumPost; @@ -46,12 +47,12 @@ public class ForumPostActionImpl extends RestActionImpl implements ForumPostAction, MessageCreateBuilderMixin { private final MessageCreateBuilder builder; - private final ForumChannel channel; + private final IPostContainer channel; private final TLongSet appliedTags = new TLongHashSet(); private String name; private ThreadChannel.AutoArchiveDuration autoArchiveDuration; - public ForumPostActionImpl(ForumChannel channel, String name, MessageCreateBuilder builder) + public ForumPostActionImpl(IPostContainer channel, String name, MessageCreateBuilder builder) { super(channel.getJDA(), Route.Channels.CREATE_THREAD.compile(channel.getId())); this.builder = builder; @@ -89,7 +90,7 @@ public Guild getGuild() @Nonnull @Override - public ForumChannel getChannel() + public IPostContainer getChannel() { return channel; } diff --git a/src/main/java/net/dv8tion/jda/internal/utils/ChannelUtil.java b/src/main/java/net/dv8tion/jda/internal/utils/ChannelUtil.java new file mode 100644 index 0000000000..1f70447799 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/utils/ChannelUtil.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.utils; + +import net.dv8tion.jda.api.entities.channel.Channel; +import net.dv8tion.jda.api.entities.channel.ChannelType; + +import java.util.EnumSet; + +public class ChannelUtil +{ + public static final EnumSet SLOWMODE_SUPPORTED = EnumSet.of( + ChannelType.TEXT, ChannelType.FORUM, ChannelType.MEDIA, + ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_NEWS_THREAD, ChannelType.GUILD_PRIVATE_THREAD, + ChannelType.STAGE, ChannelType.VOICE + ); + + public static final EnumSet NSFW_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.VOICE, ChannelType.FORUM, ChannelType.MEDIA, ChannelType.NEWS, ChannelType.STAGE); + + public static final EnumSet TOPIC_SUPPORTED = EnumSet.of(ChannelType.TEXT, ChannelType.FORUM, ChannelType.MEDIA, ChannelType.NEWS); + + public static final EnumSet POST_CONTAINERS = EnumSet.of(ChannelType.FORUM, ChannelType.MEDIA); + + public static final EnumSet THREAD_CONTAINERS = EnumSet.of(ChannelType.TEXT, ChannelType.NEWS, ChannelType.FORUM, ChannelType.MEDIA); + + public static T safeChannelCast(Object instance, Class toObjectClass) + { + if (toObjectClass.isInstance(instance)) + return toObjectClass.cast(instance); + + String cleanedClassName = instance.getClass().getSimpleName().replace("Impl", ""); + throw new IllegalStateException(Helpers.format("Cannot convert channel of type %s to %s!", cleanedClassName, toObjectClass.getSimpleName())); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java index 84fa4ab311..1c9ca0a0f6 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java @@ -18,7 +18,6 @@ import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; -import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; @@ -49,15 +48,6 @@ public static Consumer emptyConsumer() return (Consumer) EMPTY_CONSUMER; } - public static T safeChannelCast(Object instance, Class toObjectClass) - { - if (toObjectClass.isInstance(instance)) - return toObjectClass.cast(instance); - - String cleanedClassName = instance.getClass().getSimpleName().replace("Impl", ""); - throw new IllegalStateException(Helpers.format("Cannot convert channel of type %s to %s!", cleanedClassName, toObjectClass.getSimpleName())); - } - public static OffsetDateTime toOffset(long instant) { return OffsetDateTime.ofInstant(Instant.ofEpochMilli(instant), OFFSET); diff --git a/src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java b/src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java new file mode 100644 index 0000000000..da07ad56bb --- /dev/null +++ b/src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.attribute.IGuildChannelContainer; +import net.dv8tion.jda.api.entities.channel.concrete.Category; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public class ChannelConsistencyTest +{ + private static Set getMethodNames(Class clazz) + { + return Arrays.stream(clazz.getDeclaredMethods()).map(Method::getName).collect(Collectors.toSet()); + } + + private static String getChannelName(ChannelType type) + { + return type.name().substring(0, 1) + type.name().substring(1).toLowerCase(Locale.ROOT); + } + + @Test + public void checkCreateChannelMethods() + { + Set guildMethods = getMethodNames(Guild.class); + + EnumSet creatable = EnumSet.complementOf(EnumSet.of( + ChannelType.UNKNOWN, ChannelType.PRIVATE, ChannelType.GROUP, ChannelType.CATEGORY, + ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_PRIVATE_THREAD, ChannelType.GUILD_NEWS_THREAD + )); + + for (ChannelType type : creatable) + { + String channelName = getChannelName(type); + String methodName = "create" + channelName + "Channel"; + Assertions.assertTrue(guildMethods.contains(methodName), "Missing method Guild#" + methodName); + } + + Set categoryMethods = getMethodNames(Category.class); + + for (ChannelType type : creatable) + { + String channelName = getChannelName(type); + String methodName = "create" + channelName + "Channel"; + Assertions.assertTrue(categoryMethods.contains(methodName), "Missing method Category#" + methodName); + } + } + + @Test + public void checkCacheAccessMethods() + { + Set jdaMethods = getMethodNames(IGuildChannelContainer.class); + Set categoryMethods = getMethodNames(Category.class); + + EnumSet cacheable = EnumSet.complementOf(EnumSet.of( + ChannelType.UNKNOWN, ChannelType.PRIVATE, ChannelType.GROUP, ChannelType.CATEGORY, + ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_PRIVATE_THREAD, ChannelType.GUILD_NEWS_THREAD + )); + + for (ChannelType type : cacheable) + { + String channelName = getChannelName(type); + + String methodName = "get" + channelName + "ChannelCache"; + Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName); + + methodName = "get" + channelName + "ChannelsByName"; + Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName); + + methodName = "get" + channelName + "ChannelById"; + Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName); + + methodName = "get" + channelName + "Channels"; + Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName); + Assertions.assertTrue(categoryMethods.contains(methodName), "Missing method Category#" + methodName); + } + } + + @Test + public void checkManagerExists() + { + EnumSet editable = EnumSet.complementOf(EnumSet.of( + ChannelType.UNKNOWN, ChannelType.PRIVATE, ChannelType.GROUP, ChannelType.CATEGORY, + ChannelType.GUILD_PUBLIC_THREAD, ChannelType.GUILD_PRIVATE_THREAD, ChannelType.GUILD_NEWS_THREAD + )); + + for (ChannelType type : editable) + { + String channelName = getChannelName(type); + + Assertions.assertDoesNotThrow(() -> { + Class.forName("net.dv8tion.jda.api.managers.channel.concrete." + channelName + "ChannelManager"); + }, "Missing manager interface for ChannelType." + type); + } + } +}