diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/DiscordUser.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/DiscordUser.java index bb1b91f3..bc28a8f5 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/DiscordUser.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/DiscordUser.java @@ -28,6 +28,7 @@ import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel; import com.discordsrv.api.placeholder.annotation.Placeholder; import net.dv8tion.jda.api.entities.User; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; @@ -64,6 +65,23 @@ public interface DiscordUser extends Snowflake, Mentionable { @NotNull String getDiscriminator(); + /** + * Gets the Discord user's avatar url, if an avatar is set. + * @return the user's avatar url or {@code null} + */ + @Placeholder("user_avatar_url") + @Nullable + String getAvatarUrl(); + + /** + * Gets the Discord user's avatar that is currently active, + * if an avatar isn't set it'll be the url to the default avatar provided by Discord. + * @return the user's avatar url + */ + @Placeholder("user_effective_avatar_url") + @NotNull + String getEffectiveAvatarUrl(); + /** * Gets the Discord user's username followed by a {@code #} and their discriminator. * @return the Discord user's username & discriminator in the following format {@code Username#1234} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildChannel.java new file mode 100644 index 00000000..4305de12 --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildChannel.java @@ -0,0 +1,44 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.discordsrv.api.discord.api.entity.channel; + +import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; +import org.jetbrains.annotations.NotNull; + +public interface DiscordGuildChannel { + + /** + * Gets the name of the channel. + * @return the name of the channel + */ + @NotNull + String getName(); + + /** + * Gets the Discord server that this channel is in. + * @return the Discord server that contains this channel + */ + @NotNull + DiscordGuild getGuild(); +} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java new file mode 100644 index 00000000..86f9a603 --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java @@ -0,0 +1,47 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.discordsrv.api.discord.api.entity.channel; + +import com.discordsrv.api.discord.api.entity.Mentionable; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * A regular Discord channel that messages can be sent to (threads not included). + */ +public interface DiscordGuildMessageChannel extends DiscordMessageChannel, DiscordGuildChannel, Mentionable { + + @NotNull + List getActiveThreads(); + + CompletableFuture> retrieveArchivedPrivateThreads(); + CompletableFuture> retrieveArchivedJoinedPrivateThreads(); + CompletableFuture> retrieveArchivedPublicThreads(); + + CompletableFuture createThread(String name, boolean privateThread); + CompletableFuture createThread(String name, long messageId); + +} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java new file mode 100644 index 00000000..c1fcf369 --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java @@ -0,0 +1,37 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.discordsrv.api.discord.api.entity.channel; + +import com.discordsrv.api.DiscordSRVApi; +import net.dv8tion.jda.api.entities.NewsChannel; + +public interface DiscordNewsChannel extends DiscordGuildMessageChannel { + + /** + * Returns the JDA representation of this object. This should not be used if it can be avoided. + * @return the JDA representation of this object + * @see DiscordSRVApi#jda() + */ + NewsChannel getAsJDANewsChannel(); +} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java index b7d0fd02..c7ecaabb 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java @@ -24,23 +24,13 @@ package com.discordsrv.api.discord.api.entity.channel; import com.discordsrv.api.DiscordSRVApi; -import com.discordsrv.api.discord.api.entity.Mentionable; -import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; import net.dv8tion.jda.api.entities.TextChannel; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * A Discord text channel. */ -public interface DiscordTextChannel extends DiscordMessageChannel, Mentionable { - - /** - * Gets the name of the text channel. - * @return the name of the channel - */ - @NotNull - String getName(); +public interface DiscordTextChannel extends DiscordGuildMessageChannel { /** * Gets the topic of the text channel. @@ -49,13 +39,6 @@ public interface DiscordTextChannel extends DiscordMessageChannel, Mentionable { @Nullable String getTopic(); - /** - * Gets the Discord server that this channel is in. - * @return the Discord server that contains this channel - */ - @NotNull - DiscordGuild getGuild(); - /** * Returns the JDA representation of this object. This should not be used if it can be avoided. * @return the JDA representation of this object diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuildMember.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuildMember.java index e6fbc92a..3ad08cac 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuildMember.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuildMember.java @@ -67,6 +67,13 @@ public interface DiscordGuildMember extends DiscordUser, Mentionable { return getNickname().orElseGet(this::getUsername); } + /** + * Gets the avatar url that is active for this user in this server. + * @return the user's avatar url in this server + */ + @Placeholder("user_effective_avatar_url") + String getEffectiveServerAvatarUrl(); + /** * Gets the color of this user's highest role that has a color. * @return the color that will be used for this user diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/impl/SendableDiscordMessageImpl.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/impl/SendableDiscordMessageImpl.java index 4d1f9b75..216be860 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/impl/SendableDiscordMessageImpl.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/impl/SendableDiscordMessageImpl.java @@ -160,6 +160,20 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { return this; } + @Override + public @NotNull Builder convertToNonWebhook() { + String webhookUsername = this.webhookUsername; + if (webhookUsername == null) { + return this; + } + + // TODO: configuration? + this.content = webhookUsername + " > " + content; + this.webhookUsername = null; + this.webhookAvatarUrl = null; + return this; + } + @Override public @NotNull SendableDiscordMessage build() { return new SendableDiscordMessageImpl(content, embeds, allowedMentions, webhookUsername, webhookAvatarUrl); @@ -167,8 +181,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { @Override public Formatter toFormatter() { - return new FormatterImpl( - clone()); + return new FormatterImpl(clone()); } @SuppressWarnings("MethodDoesntCallSuperMethod") @@ -218,6 +231,12 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { return this; } + @Override + public @NotNull Formatter convertToNonWebhook() { + builder.convertToNonWebhook(); + return this; + } + private Function wrapFunction(Function function) { return matcher -> { Object result = function.apply(matcher); diff --git a/common/src/main/java/com/discordsrv/common/module/modules/DiscordAPIEventModule.java b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java similarity index 50% rename from common/src/main/java/com/discordsrv/common/module/modules/DiscordAPIEventModule.java rename to common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java index 39749445..eaa37ecc 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/DiscordAPIEventModule.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java @@ -16,15 +16,19 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules; +package com.discordsrv.common.discord.api; +import com.discordsrv.api.discord.events.DiscordMessageDeleteEvent; +import com.discordsrv.api.discord.events.DiscordMessageReceiveEvent; +import com.discordsrv.api.discord.events.DiscordMessageUpdateEvent; import com.discordsrv.api.event.bus.Subscribe; -import com.discordsrv.api.discord.events.DiscordMessageReceivedEvent; import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.discord.api.channel.DiscordMessageChannelImpl; -import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordMessageChannelImpl; +import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; import com.discordsrv.common.module.type.AbstractModule; +import net.dv8tion.jda.api.events.message.MessageDeleteEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.MessageUpdateEvent; public class DiscordAPIEventModule extends AbstractModule { @@ -33,10 +37,26 @@ public class DiscordAPIEventModule extends AbstractModule { } @Subscribe - public void onMessageReceivedEvent(MessageReceivedEvent event) { - discordSRV.eventBus().publish(new DiscordMessageReceivedEvent( - ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()), - DiscordMessageChannelImpl.get(discordSRV, event.getChannel())) - ); + public void onMessageReceived(MessageReceivedEvent event) { + discordSRV.eventBus().publish(new DiscordMessageReceiveEvent( + DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()) + )); + } + + @Subscribe + public void onMessageUpdate(MessageUpdateEvent event) { + discordSRV.eventBus().publish(new DiscordMessageUpdateEvent( + DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()) + )); + } + + @Subscribe + public void onMessageDelete(MessageDeleteEvent event) { + discordSRV.eventBus().publish(new DiscordMessageDeleteEvent( + DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + event.getMessageIdLong() + )); } } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIImpl.java index a231e967..085b3eff 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIImpl.java @@ -21,10 +21,12 @@ package com.discordsrv.common.discord.api; import club.minnced.discord.webhook.WebhookClient; import club.minnced.discord.webhook.WebhookClientBuilder; import com.discordsrv.api.discord.api.DiscordAPI; +import com.discordsrv.api.discord.api.ThreadChannelLookup; import com.discordsrv.api.discord.api.entity.DiscordUser; import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel; import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel; import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.guild.DiscordRole; import com.discordsrv.api.discord.api.exception.NotReadyException; @@ -32,35 +34,43 @@ import com.discordsrv.api.discord.api.exception.RestErrorResponseException; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; import com.discordsrv.common.config.main.channels.base.IChannelConfig; -import com.discordsrv.common.discord.api.channel.DiscordDMChannelImpl; -import com.discordsrv.common.discord.api.channel.DiscordTextChannelImpl; -import com.discordsrv.common.discord.api.guild.DiscordGuildImpl; -import com.discordsrv.common.discord.api.guild.DiscordRoleImpl; +import com.discordsrv.common.config.main.channels.base.ThreadConfig; +import com.discordsrv.common.discord.api.entity.DiscordUserImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordDMChannelImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordTextChannelImpl; +import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl; +import com.discordsrv.common.discord.api.entity.guild.DiscordRoleImpl; +import com.discordsrv.common.function.CheckedSupplier; import com.github.benmanes.caffeine.cache.AsyncCacheLoader; import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Expiry; import com.github.benmanes.caffeine.cache.RemovalListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.ThreadChannel; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.Webhook; import net.dv8tion.jda.api.exceptions.ErrorResponseException; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; import net.dv8tion.jda.api.requests.ErrorResponse; import net.dv8tion.jda.api.requests.GatewayIntent; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class DiscordAPIImpl implements DiscordAPI { private final DiscordSRV discordSRV; - private final AsyncLoadingCache cachedClients; + private final List threadLookups = new CopyOnWriteArrayList<>(); public DiscordAPIImpl(DiscordSRV discordSRV) { this.discordSRV = discordSRV; @@ -82,6 +92,226 @@ public class DiscordAPIImpl implements DiscordAPI { return cachedClients; } + /** + * Finds active threads based for the provided {@link IChannelConfig}. + * @param config the config that specified the threads + * @return the list of active threads + */ + public List findThreads(IChannelConfig config) { + List channels = new ArrayList<>(); + findOrCreateThreads(config, channels::add, null); + return channels; + } + + /** + * Finds or potentially unarchives or creates threads based on the provided {@link IChannelConfig}. + * @param config the config + * @param channelConsumer the consumer that will take the channels as they are gathered + * @param futures a possibly null list of {@link CompletableFuture} for tasks that need to be completed to get all threads + */ + public void findOrCreateThreads( + IChannelConfig config, + Consumer channelConsumer, + @Nullable List> futures + ) { + List threads = config.threads(); + if (threads == null) { + return; + } + + for (ThreadConfig threadConfig : threads) { + long channelId = threadConfig.channelId; + DiscordTextChannel channel = getTextChannelById(channelId).orElse(null); + if (channel == null) { + if (channelId > 0) { + discordSRV.logger().error("Unable to find channel with ID " + + Long.toUnsignedString(channelId) + + ", unable to forward message to Discord"); + } + continue; + } + + // Check if a thread by the same name is still active + DiscordThreadChannel thread = findThread(threadConfig, channel.getActiveThreads()); + if (thread != null) { + ThreadChannel jdaChannel = thread.getAsJDAThreadChannel(); + if (!jdaChannel.isLocked() && !jdaChannel.isArchived()) { + channelConsumer.accept(thread); + continue; + } + } + + if (futures == null) { + // Futures not specified, don't try to unarchive or create threads + continue; + } + + CompletableFuture future; + if (thread != null) { + // Unarchive the thread + future = new CompletableFuture<>(); + unarchiveOrCreateThread(threadConfig, channel, thread, future); + } else { + // Find or create the thread + future = findOrCreateThread(threadConfig, channel); + } + + futures.add(future.handle((threadChannel, t) -> { + if (t instanceof InsufficientPermissionException) { + discordSRV.logger().error( + "Unable to send message to channel " + channel + + " because the bot is lacking the " + + ((InsufficientPermissionException) t).getPermission().getName() + + " permission"); + throw new RuntimeException(); + } else if (t != null) { + discordSRV.logger().error("Failed to find thread in channel " + channel, t); + throw new RuntimeException(); + } + + if (threadChannel != null) { + channelConsumer.accept(threadChannel); + } + return threadChannel; + })); + } + } + + private DiscordThreadChannel findThread(ThreadConfig config, List threads) { + for (DiscordThreadChannel thread : threads) { + if (thread.getName().equals(config.threadName)) { + return thread; + } + } + return null; + } + + private CompletableFuture findOrCreateThread(ThreadConfig config, DiscordTextChannel textChannel) { + if (!config.unarchive) { + return textChannel.createThread(config.threadName, config.privateThread); + } + + CompletableFuture completableFuture = new CompletableFuture<>(); + lookupThreads( + textChannel, + config.privateThread, + lookup -> findOrCreateThread(config, textChannel, lookup, completableFuture), + (thread, throwable) -> { + if (throwable != null) { + completableFuture.completeExceptionally(throwable); + } else { + completableFuture.complete(thread); + } + }); + return completableFuture; + } + + private void findOrCreateThread( + ThreadConfig config, + DiscordTextChannel textChannel, + ThreadChannelLookup lookup, + CompletableFuture completableFuture + ) { + completableFuture.whenComplete((threadChannel, throwable) -> { + CompletableFuture future = lookup.getChannelFuture(); + if (throwable != null) { + future.completeExceptionally(throwable); + } else { + future.complete(threadChannel); + } + }); + lookup.getFuture().whenComplete((channels, throwable) -> { + if (throwable != null) { + completableFuture.completeExceptionally(throwable); + return; + } + + DiscordThreadChannel thread = findThread(config, channels); + unarchiveOrCreateThread(config, textChannel, thread, completableFuture); + }).exceptionally(t -> { + if (t instanceof CompletionException) { + completableFuture.completeExceptionally(t.getCause()); + return null; + } + completableFuture.completeExceptionally(t); + return null; + }); + } + + private void unarchiveOrCreateThread( + ThreadConfig config, + DiscordTextChannel textChannel, + DiscordThreadChannel thread, + CompletableFuture future + ) { + if (thread != null) { + ThreadChannel channel = thread.getAsJDAThreadChannel(); + if (channel.isLocked() || channel.isArchived()) { + try { + channel.getManager().setArchived(false).queue( + v -> future.complete(thread), + future::completeExceptionally + ); + } catch (Throwable t) { + future.completeExceptionally(t); + } + } else { + future.complete(thread); + } + return; + } + + textChannel.createThread(config.threadName, config.privateThread).whenComplete(((threadChannel, t) -> { + if (t != null) { + future.completeExceptionally(t); + } else { + future.complete(threadChannel); + } + })); + } + + public void lookupThreads( + DiscordTextChannel textChannel, + boolean privateThreads, + Consumer lookupConsumer, + BiConsumer channelConsumer + ) { + ThreadChannelLookup lookup; + synchronized (threadLookups) { + for (ThreadChannelLookup threadLookup : threadLookups) { + if (threadLookup.isPrivateThreads() != privateThreads + || threadLookup.getChannelId() != textChannel.getId()) { + continue; + } + + threadLookup.getChannelFuture().whenComplete(channelConsumer); + return; + } + + lookup = new ThreadChannelLookup( + textChannel.getId(), privateThreads, + privateThreads + ? textChannel.retrieveArchivedPrivateThreads() + : textChannel.retrieveArchivedPublicThreads() + ); + threadLookups.add(lookup); + } + + lookup.getChannelFuture().whenComplete(channelConsumer); + lookupConsumer.accept(lookup); + lookup.getFuture().whenComplete((channel, t) -> threadLookups.remove(lookup)); + } + + public CompletableFuture mapExceptions(CheckedSupplier> futureSupplier) { + try { + return mapExceptions(futureSupplier.get()); + } catch (Throwable t) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(t); + return future; + } + } + public CompletableFuture mapExceptions(CompletableFuture future) { return future.handle((msg, t) -> { if (t instanceof ErrorResponseException) { @@ -172,7 +402,7 @@ public class DiscordAPIImpl implements DiscordAPI { public @NonNull CompletableFuture asyncLoad(@NonNull Long channelId, @NonNull Executor executor) { JDA jda = discordSRV.jda().orElse(null); if (jda == null) { - return discordSRV.discordAPI().notReady(); + return notReady(); } CompletableFuture future = new CompletableFuture<>(); @@ -208,7 +438,7 @@ public class DiscordAPIImpl implements DiscordAPI { }).thenApply(webhook -> WebhookClientBuilder.fromJDA(webhook) .setHttpClient(jda.getHttpClient()) - .setExecutorService(discordSRV.scheduler().scheduledExecutor()) + .setExecutorService(discordSRV.scheduler().scheduledExecutorService()) .build() ); } @@ -219,7 +449,7 @@ public class DiscordAPIImpl implements DiscordAPI { private boolean isConfiguredChannel(Long channelId) { for (BaseChannelConfig config : discordSRV.config().channels.values()) { if (config instanceof IChannelConfig - && ((IChannelConfig) config).ids().contains(channelId)) { + && ((IChannelConfig) config).channelIds().contains(channelId)) { return true; } } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java deleted file mode 100644 index 29434eda..00000000 --- a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of DiscordSRV, licensed under the GPLv3 License - * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.discordsrv.common.discord.api.channel; - -import club.minnced.discord.webhook.WebhookClient; -import club.minnced.discord.webhook.receive.ReadonlyMessage; -import club.minnced.discord.webhook.send.WebhookMessage; -import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; -import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; -import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; -import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; -import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.discord.api.guild.DiscordGuildImpl; -import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; -import com.discordsrv.common.discord.api.message.util.SendableDiscordMessageUtil; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.requests.restaction.MessageAction; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; - -public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements DiscordTextChannel { - - private final DiscordSRV discordSRV; - private final TextChannel textChannel; - private final DiscordGuild guild; - - public DiscordTextChannelImpl(DiscordSRV discordSRV, TextChannel textChannel) { - this.discordSRV = discordSRV; - this.textChannel = textChannel; - this.guild = new DiscordGuildImpl(discordSRV, textChannel.getGuild()); - } - - @Override - public long getId() { - return textChannel.getIdLong(); - } - - @Override - public @NotNull String getName() { - return textChannel.getName(); - } - - @Override - public @Nullable String getTopic() { - return textChannel.getTopic(); - } - - @Override - public @NotNull DiscordGuild getGuild() { - return guild; - } - - @Override - public TextChannel getAsJDATextChannel() { - return textChannel; - } - - @Override - public @NotNull CompletableFuture sendMessage(SendableDiscordMessage message) { - return message(message, WebhookClient::send, MessageChannel::sendMessage); - } - - @Override - public CompletableFuture deleteMessageById(long id) { - return null; // TODO - } - - @Override - public @NotNull CompletableFuture editMessageById(long id, SendableDiscordMessage message) { - return message( - message, - (client, msg) -> client.edit(id, msg), - (textChannel, msg) -> textChannel.editMessageById(id, msg) - ); - } - - @Override - public MessageChannel getAsJDAMessageChannel() { - return textChannel; - } - - private CompletableFuture message( - SendableDiscordMessage message, - BiFunction> webhookFunction, - BiFunction jdaFunction) { - CompletableFuture future; - if (message.isWebhookMessage()) { - future = discordSRV.discordAPI().queryWebhookClient(getId()) - .thenCompose(client -> webhookFunction.apply( - client, SendableDiscordMessageUtil.toWebhook(message))) - .thenApply(msg -> ReceivedDiscordMessageImpl.fromWebhook(discordSRV, msg)); - } else { - future = jdaFunction - .apply(textChannel, SendableDiscordMessageUtil.toJDA(message)) - .submit() - .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); - } - - return discordSRV.discordAPI().mapExceptions(future); - } - - @Override - public String getAsMention() { - return textChannel.getAsMention(); - } -} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/DiscordUserImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/DiscordUserImpl.java similarity index 86% rename from common/src/main/java/com/discordsrv/common/discord/api/DiscordUserImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/DiscordUserImpl.java index f95037d4..037e003d 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/DiscordUserImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/DiscordUserImpl.java @@ -16,15 +16,16 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api; +package com.discordsrv.common.discord.api.entity; import com.discordsrv.api.discord.api.entity.DiscordUser; import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel; import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.discord.api.channel.DiscordDMChannelImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordDMChannelImpl; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.User; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; @@ -65,6 +66,16 @@ public class DiscordUserImpl implements DiscordUser { return user.getDiscriminator(); } + @Override + public @Nullable String getAvatarUrl() { + return user.getAvatarUrl(); + } + + @Override + public @NotNull String getEffectiveAvatarUrl() { + return user.getEffectiveAvatarUrl(); + } + @Override public CompletableFuture openPrivateChannel() { JDA jda = discordSRV.jda().orElse(null); diff --git a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java similarity index 70% rename from common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java index 82886619..f58f8abd 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java @@ -16,56 +16,42 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.channel; +package com.discordsrv.common.discord.api.entity.channel; import com.discordsrv.api.discord.api.entity.DiscordUser; import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.discord.api.DiscordUserImpl; -import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; -import com.discordsrv.common.discord.api.message.util.SendableDiscordMessageUtil; -import net.dv8tion.jda.api.entities.MessageChannel; +import com.discordsrv.common.discord.api.entity.DiscordUserImpl; +import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; +import com.discordsrv.common.discord.api.entity.message.util.SendableDiscordMessageUtil; import net.dv8tion.jda.api.entities.PrivateChannel; import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; -public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements DiscordDMChannel { +public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements DiscordDMChannel { - private final DiscordSRV discordSRV; - private final PrivateChannel privateChannel; private final DiscordUser user; public DiscordDMChannelImpl(DiscordSRV discordSRV, PrivateChannel privateChannel) { - this.discordSRV = discordSRV; - this.privateChannel = privateChannel; + super(discordSRV, privateChannel); this.user = new DiscordUserImpl(discordSRV, privateChannel.getUser()); } - @Override - public long getId() { - return privateChannel.getIdLong(); - } - @Override public DiscordUser getUser() { return user; } @Override - public PrivateChannel getAsJDAPrivateChannel() { - return privateChannel; - } - - @Override - public @NotNull CompletableFuture sendMessage(SendableDiscordMessage message) { + public @NotNull CompletableFuture sendMessage(@NotNull SendableDiscordMessage message) { if (message.isWebhookMessage()) { throw new IllegalArgumentException("Cannot send webhook messages to DMChannels"); } - CompletableFuture future = privateChannel + CompletableFuture future = channel .sendMessage(SendableDiscordMessageUtil.toJDA(message)) .submit() .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); @@ -74,17 +60,20 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D } @Override - public CompletableFuture deleteMessageById(long id) { - return discordSRV.discordAPI().mapExceptions(privateChannel.deleteMessageById(id).submit()); + public CompletableFuture deleteMessageById(long id, boolean webhookMessage) { + if (webhookMessage) { + throw new IllegalArgumentException("DMChannels do not contain webhook messages"); + } + return discordSRV.discordAPI().mapExceptions(channel.deleteMessageById(id).submit()); } @Override - public @NotNull CompletableFuture editMessageById(long id, SendableDiscordMessage message) { + public @NotNull CompletableFuture editMessageById(long id, @NotNull SendableDiscordMessage message) { if (message.isWebhookMessage()) { throw new IllegalArgumentException("Cannot send webhook messages to DMChannels"); } - CompletableFuture future = privateChannel + CompletableFuture future = channel .editMessageById(id, SendableDiscordMessageUtil.toJDA(message)) .submit() .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); @@ -93,7 +82,7 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D } @Override - public MessageChannel getAsJDAMessageChannel() { - return privateChannel; + public PrivateChannel getAsJDAPrivateChannel() { + return channel; } } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java new file mode 100644 index 00000000..37389a1f --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java @@ -0,0 +1,174 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.discordsrv.common.discord.api.entity.channel; + +import club.minnced.discord.webhook.WebhookClient; +import club.minnced.discord.webhook.receive.ReadonlyMessage; +import club.minnced.discord.webhook.send.WebhookMessage; +import com.discordsrv.api.discord.api.entity.channel.DiscordGuildMessageChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel; +import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; +import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; +import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; +import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl; +import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; +import com.discordsrv.common.discord.api.entity.message.util.SendableDiscordMessageUtil; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction; +import net.dv8tion.jda.api.requests.restaction.pagination.ThreadChannelPaginationAction; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +public abstract class DiscordGuildMessageChannelImpl + extends DiscordMessageChannelImpl + implements DiscordGuildMessageChannel { + + private final DiscordGuild guild; + + public DiscordGuildMessageChannelImpl(DiscordSRV discordSRV, T channel) { + super(discordSRV, channel); + this.guild = new DiscordGuildImpl(discordSRV, channel.getGuild()); + } + + @Override + public @NotNull String getName() { + return channel.getName(); + } + + @Override + public String getAsMention() { + return channel.getAsMention(); + } + + @Override + public @NotNull DiscordGuild getGuild() { + return guild; + } + + @Override + public @NotNull List getActiveThreads() { + List threads = channel.getThreadChannels(); + List threadChannels = new ArrayList<>(threads.size()); + for (ThreadChannel thread : threads) { + threadChannels.add(new DiscordThreadChannelImpl(discordSRV, thread)); + } + return threadChannels; + } + + @Override + public CompletableFuture> retrieveArchivedPrivateThreads() { + return threads(IThreadContainer::retrieveArchivedPrivateThreadChannels); + } + + @Override + public CompletableFuture> retrieveArchivedJoinedPrivateThreads() { + return threads(IThreadContainer::retrieveArchivedPrivateJoinedThreadChannels); + } + + @Override + public CompletableFuture> retrieveArchivedPublicThreads() { + return threads(IThreadContainer::retrieveArchivedPublicThreadChannels); + } + + private CompletableFuture> threads(Function action) { + return discordSRV.discordAPI().mapExceptions(() -> + action.apply(channel) + .submit() + .thenApply(channels -> channels.stream() + .map(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) + .collect(Collectors.toList()) + ) + ); + } + + @Override + public CompletableFuture createThread(String name, boolean privateThread) { + return thread(channel -> channel.createThreadChannel(name, privateThread)); + } + + @Override + public CompletableFuture createThread(String name, long messageId) { + return thread(channel -> channel.createThreadChannel(name, messageId)); + } + + private CompletableFuture thread(Function action) { + return discordSRV.discordAPI().mapExceptions(() -> + action.apply(channel) + .submit() + .thenApply(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) + ); + } + + @Override + public @NotNull CompletableFuture sendMessage(@NotNull SendableDiscordMessage message) { + return message(message, WebhookClient::send, MessageChannel::sendMessage); + } + + @Override + public @NotNull CompletableFuture editMessageById(long id, @NotNull SendableDiscordMessage message) { + return message( + message, + (client, msg) -> client.edit(id, msg), + (textChannel, msg) -> textChannel.editMessageById(id, msg) + ); + } + + private CompletableFuture message( + SendableDiscordMessage message, + BiFunction> webhookFunction, + BiFunction jdaFunction) { + return discordSRV.discordAPI().mapExceptions(() -> { + CompletableFuture future; + if (message.isWebhookMessage()) { + future = discordSRV.discordAPI().queryWebhookClient(getId()) + .thenCompose(client -> webhookFunction.apply( + client, SendableDiscordMessageUtil.toWebhook(message))) + .thenApply(msg -> ReceivedDiscordMessageImpl.fromWebhook(discordSRV, msg)); + } else { + future = jdaFunction + .apply(channel, SendableDiscordMessageUtil.toJDA(message)) + .submit() + .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); + } + return future; + }); + } + + @Override + public CompletableFuture deleteMessageById(long id, boolean webhookMessage) { + CompletableFuture future; + if (webhookMessage) { + future = channel.deleteMessageById(id).submit(); + } else { + future = discordSRV.discordAPI() + .queryWebhookClient(channel.getIdLong()) + .thenCompose(client -> client.delete(id)); + } + return discordSRV.discordAPI().mapExceptions(future); + } + +} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java new file mode 100644 index 00000000..a0196793 --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java @@ -0,0 +1,37 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.discordsrv.common.discord.api.entity.channel; + +import com.discordsrv.api.discord.api.entity.channel.DiscordNewsChannel; +import com.discordsrv.common.DiscordSRV; +import net.dv8tion.jda.api.entities.NewsChannel; + +public class DiscordNewsChannelImpl + extends DiscordGuildMessageChannelImpl + implements DiscordNewsChannel { + + public DiscordNewsChannelImpl(DiscordSRV discordSRV, NewsChannel channel) { + super(discordSRV, channel); + } + + @Override + public NewsChannel getAsJDANewsChannel() { + return channel; + } +} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordMessageChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java similarity index 53% rename from common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordMessageChannelImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java index 3cb1b228..13000e25 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordMessageChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java @@ -16,23 +16,27 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.channel; +package com.discordsrv.common.discord.api.entity.channel; -import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; import com.discordsrv.common.DiscordSRV; -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.entities.PrivateChannel; import net.dv8tion.jda.api.entities.TextChannel; +import org.jetbrains.annotations.Nullable; -public abstract class DiscordMessageChannelImpl implements DiscordMessageChannel { +public class DiscordTextChannelImpl extends DiscordGuildMessageChannelImpl implements DiscordTextChannel { - public static DiscordMessageChannelImpl get(DiscordSRV discordSRV, MessageChannel messageChannel) { - if (messageChannel instanceof TextChannel) { - return new DiscordTextChannelImpl(discordSRV, (TextChannel) messageChannel); - } else if (messageChannel instanceof PrivateChannel) { - return new DiscordDMChannelImpl(discordSRV, (PrivateChannel) messageChannel); - } else { - throw new IllegalArgumentException("Unknown MessageChannel type"); - } + public DiscordTextChannelImpl(DiscordSRV discordSRV, TextChannel textChannel) { + super(discordSRV, textChannel); } + + @Override + public @Nullable String getTopic() { + return channel.getTopic(); + } + + @Override + public TextChannel getAsJDATextChannel() { + return channel; + } + } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildImpl.java similarity index 98% rename from common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildImpl.java index b78ae77b..9f061266 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildImpl.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.guild; +package com.discordsrv.common.discord.api.entity.guild; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.guild.DiscordGuildMember; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildMemberImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildMemberImpl.java similarity index 94% rename from common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildMemberImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildMemberImpl.java index 0e138223..9c96c40e 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildMemberImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordGuildMemberImpl.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.guild; +package com.discordsrv.common.discord.api.entity.guild; import com.discordsrv.api.color.Color; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; @@ -26,7 +26,7 @@ import com.discordsrv.api.placeholder.annotation.Placeholder; import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; -import com.discordsrv.common.discord.api.DiscordUserImpl; +import com.discordsrv.common.discord.api.entity.DiscordUserImpl; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.User; @@ -73,6 +73,11 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu return roles; } + @Override + public String getEffectiveServerAvatarUrl() { + return member.getEffectiveAvatarUrl(); + } + @Override public Color getColor() { return color; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordRoleImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordRoleImpl.java similarity index 96% rename from common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordRoleImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordRoleImpl.java index a7fe57fd..a381fa22 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordRoleImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/guild/DiscordRoleImpl.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.guild; +package com.discordsrv.common.discord.api.entity.guild; import com.discordsrv.api.color.Color; import com.discordsrv.api.discord.api.entity.guild.DiscordRole; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageClusterImpl.java similarity index 98% rename from common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageClusterImpl.java index 59dec600..dcf8aaac 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageClusterImpl.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.message; +package com.discordsrv.common.discord.api.entity.message; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java similarity index 97% rename from common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java index b19ee73d..1f038916 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.discord.api.message; +package com.discordsrv.common.discord.api.entity.message; import club.minnced.discord.webhook.WebhookClient; import club.minnced.discord.webhook.receive.ReadonlyAttachment; @@ -40,9 +40,9 @@ import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; -import com.discordsrv.common.discord.api.DiscordUserImpl; -import com.discordsrv.common.discord.api.channel.DiscordMessageChannelImpl; -import com.discordsrv.common.discord.api.guild.DiscordGuildMemberImpl; +import com.discordsrv.common.discord.api.entity.DiscordUserImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordMessageChannelImpl; +import com.discordsrv.common.discord.api.entity.guild.DiscordGuildMemberImpl; import com.discordsrv.common.function.OrDefault; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; @@ -265,7 +265,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple return future; } - return textChannel.deleteMessageById(getId()); + return textChannel.deleteMessageById(getId(), fromSelf && getWebhookUsername().isPresent()); } @Override