From c76e0c2c3bc5c57fda23972b0026900d59622aff Mon Sep 17 00:00:00 2001 From: Vankka Date: Sat, 31 Jul 2021 01:21:23 +0300 Subject: [PATCH] More work on first part api & added a bunch of nullability annotations --- .../com/discordsrv/api/DiscordSRVApi.java | 7 +- .../discordsrv/api/DiscordSRVApiProvider.java | 2 + .../api/component/MinecraftComponent.java | 4 +- .../component/MinecraftComponentFactory.java | 2 + .../api/discord/api/DiscordAPI.java | 27 ++++- .../api/discord/api/entity/Snowflake.java | 23 +++++ .../api/entity/channel/DiscordDMChannel.java | 39 ++++++++ .../entity/channel/DiscordMessageChannel.java | 23 +++++ .../api/entity/guild/DiscordGuild.java | 33 ++++++- .../api/entity/guild/DiscordGuildMember.java | 30 ++++++ .../discord/api/entity/guild/DiscordRole.java | 23 +++++ .../discord/api/entity/user/DiscordUser.java | 23 +++++ .../exception/RestErrorResponseException.java | 8 ++ .../exception/UnknownChannelException.java | 10 +- .../exception/UnknownMessageException.java | 10 +- .../connection/DiscordConnectionDetails.java | 7 +- .../api/event/bus/EventListener.java | 4 + .../event/bus/internal/EventStateHolder.java | 5 +- .../api/player/DiscordSRVPlayer.java | 4 + .../api/player/IPlayerProvider.java | 2 + .../bukkit/player/BukkitOfflinePlayer.java | 5 +- .../bukkit/player/BukkitPlayerProvider.java | 26 +++-- .../bungee/player/BungeePlayer.java | 2 +- .../discordsrv/common/AbstractDiscordSRV.java | 6 +- .../common/component/ComponentFactory.java | 3 +- .../component/MinecraftComponentImpl.java | 6 +- .../common/discord/api/DiscordAPIImpl.java | 80 +++++++-------- .../api/channel/DiscordDMChannelImpl.java | 98 +++++++++++++++++++ .../channel/DiscordMessageChannelImpl.java | 57 +++++++++++ .../api/channel/DiscordTextChannelImpl.java | 31 ++---- .../discord/api/guild/DiscordGuildImpl.java | 48 ++++++--- .../api/guild/DiscordGuildMemberImpl.java | 34 +++++++ .../discord/api/guild/DiscordRoleImpl.java | 44 +++++++++ .../message/ReceivedDiscordMessageImpl.java | 6 +- .../discord/api/user/DiscordUserImpl.java | 18 ++++ .../details/DiscordConnectionDetailsImpl.java | 9 +- .../common/event/bus/EventListenerImpl.java | 5 +- .../common/player/IOfflinePlayer.java | 4 + .../com/discordsrv/common/player/IPlayer.java | 3 +- .../provider/AbstractPlayerProvider.java | 4 +- .../player/provider/PlayerProvider.java | 4 +- .../sponge/player/SpongeOfflinePlayer.java | 2 +- .../velocity/player/VelocityPlayer.java | 2 +- 43 files changed, 650 insertions(+), 133 deletions(-) create mode 100644 api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordDMChannel.java create mode 100644 common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java create mode 100644 common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordMessageChannelImpl.java create mode 100644 common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordRoleImpl.java diff --git a/api/src/main/java/com/discordsrv/api/DiscordSRVApi.java b/api/src/main/java/com/discordsrv/api/DiscordSRVApi.java index f09e2175..d6b62dea 100644 --- a/api/src/main/java/com/discordsrv/api/DiscordSRVApi.java +++ b/api/src/main/java/com/discordsrv/api/DiscordSRVApi.java @@ -32,7 +32,8 @@ import com.discordsrv.api.player.IPlayerProvider; import net.dv8tion.jda.api.JDA; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; + +import java.util.Optional; /** * The DiscordSRV API. @@ -87,8 +88,8 @@ public interface DiscordSRVApi { * @see #isReady() * @see #discordConnectionDetails() */ - @Nullable - JDA jda(); + @NotNull + Optional jda(); /** * Discord connection detail manager, specify {@link net.dv8tion.jda.api.requests.GatewayIntent}s and {@link net.dv8tion.jda.api.utils.cache.CacheFlag}s you need here. diff --git a/api/src/main/java/com/discordsrv/api/DiscordSRVApiProvider.java b/api/src/main/java/com/discordsrv/api/DiscordSRVApiProvider.java index d0618a09..cf5a00ba 100644 --- a/api/src/main/java/com/discordsrv/api/DiscordSRVApiProvider.java +++ b/api/src/main/java/com/discordsrv/api/DiscordSRVApiProvider.java @@ -24,6 +24,7 @@ package com.discordsrv.api; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Optional; @@ -66,6 +67,7 @@ public final class DiscordSRVApiProvider { * @return the DiscordSRV api in an optional * @see #isAvailable() */ + @NotNull public static Optional optional() { return Optional.ofNullable(API); } diff --git a/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java b/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java index 98d607bd..29397649 100644 --- a/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java +++ b/api/src/main/java/com/discordsrv/api/component/MinecraftComponent.java @@ -58,6 +58,7 @@ public interface MinecraftComponent { * Use for reference only, this is not a substitute for legacy. * @return the plain message */ + @NotNull String asPlainString(); /** @@ -103,6 +104,7 @@ public interface MinecraftComponent { * Returns the Adventure Component returned by the gson serializer of this adapter. * @return the {@code net.kyori.adventure.text.Component} (or relocated), cast this to your end class */ + @NotNull Object getComponent(); /** @@ -110,7 +112,7 @@ public interface MinecraftComponent { * @param adventureComponent the component * @throws IllegalArgumentException if the provided component cannot be processed by the gson serializer of this apdater */ - void setComponent(Object adventureComponent); + void setComponent(@NotNull Object adventureComponent); } } diff --git a/api/src/main/java/com/discordsrv/api/component/MinecraftComponentFactory.java b/api/src/main/java/com/discordsrv/api/component/MinecraftComponentFactory.java index 81e7db08..7826ffc8 100644 --- a/api/src/main/java/com/discordsrv/api/component/MinecraftComponentFactory.java +++ b/api/src/main/java/com/discordsrv/api/component/MinecraftComponentFactory.java @@ -24,6 +24,7 @@ package com.discordsrv.api.component; import com.discordsrv.api.DiscordSRVApi; +import org.jetbrains.annotations.NotNull; /** * A factory for creating {@link MinecraftComponent}s. @@ -36,5 +37,6 @@ public interface MinecraftComponentFactory { * * @return a new {@link MinecraftComponent} */ + @NotNull MinecraftComponent empty(); } diff --git a/api/src/main/java/com/discordsrv/api/discord/api/DiscordAPI.java b/api/src/main/java/com/discordsrv/api/discord/api/DiscordAPI.java index 302b6c82..6f95188e 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/DiscordAPI.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/DiscordAPI.java @@ -23,6 +23,8 @@ package com.discordsrv.api.discord.api; +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.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.user.DiscordUser; @@ -36,23 +38,42 @@ import java.util.Optional; public interface DiscordAPI { /** - * Gets a Discord text channel by id. + * Gets a Discord message channel by id, the provided entity can be cached and will not update if it changes on Discord. + * @param id the id for the message channel + * @return the message channel + */ + @NotNull + Optional getMessageChannelById(@NotNull String id); + + /** + * Gets a Discord direct message channel by id, the provided entity can be cached and will not update if it changes on Discord. + * @param id the id for the direct message channel + * @return the direct message channel + */ + @NotNull + Optional getDirectMessageChannelById(@NotNull String id); + + /** + * Gets a Discord text channel by id, the provided entity can be cached and will not update if it changes on Discord. * @param id the id for the text channel * @return the text channel */ + @NotNull Optional getTextChannelById(@NotNull String id); /** - * Gets a Discord server by id. + * Gets a Discord server by id, the provided entity can be cached and will not update if it changes on Discord. * @param id the id for the Discord server * @return the Discord server */ + @NotNull Optional getGuildById(@NotNull String id); /** - * Gets a Discord user by id. + * Gets a Discord user by id, the provided entity can be cached and will not update if it changes on Discord. * @param id the id for the Discord user * @return the Discord user */ + @NotNull Optional getUserById(@NotNull String id); } diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/Snowflake.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/Snowflake.java index 8bfa25e0..74041f9d 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/Snowflake.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/Snowflake.java @@ -1,3 +1,26 @@ +/* + * 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; import org.jetbrains.annotations.NotNull; diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordDMChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordDMChannel.java new file mode 100644 index 00000000..487589de --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordDMChannel.java @@ -0,0 +1,39 @@ +/* + * 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.user.DiscordUser; + +/** + * A Discord direct message channel. + */ +public interface DiscordDMChannel extends DiscordMessageChannel { + + /** + * Gets the {@link DiscordUser} that is associated with this direct message channel. + * @return the user this direct message is with + */ + DiscordUser getUser(); + +} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordMessageChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordMessageChannel.java index 4696dbcb..97663ab7 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordMessageChannel.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordMessageChannel.java @@ -1,3 +1,26 @@ +/* + * 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.Snowflake; diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuild.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuild.java index 8f605b06..f8218563 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuild.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordGuild.java @@ -1,3 +1,26 @@ +/* + * 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.guild; import java.util.Optional; @@ -20,9 +43,17 @@ public interface DiscordGuild { int getMemberCount(); /** - * Gets a Discord guild member by id from the cache. + * Gets a Discord guild member by id from the cache, the provided entity can be cached and will not update if it changes on Discord. * @param id the id for the Discord guild member * @return the Discord guild member from the cache */ Optional getMemberById(String id); + + /** + * Gets a Discord role by id from the cache, the provided entity can be cached and will not update if it changes on Discord. + * @param id the id for the Discord role + * @return the Discord role from the cache + */ + Optional getRoleById(String id); + } 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 41b876be..3b1978ae 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 @@ -1,8 +1,32 @@ +/* + * 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.guild; import com.discordsrv.api.discord.api.entity.user.DiscordUser; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Optional; /** @@ -17,6 +41,12 @@ public interface DiscordGuildMember extends DiscordUser { @NotNull Optional getNickname(); + /** + * Gets the roles of this Discord server member. + * @return the server member's roles in order from highest to lowest, this does not include the "@everyone" role + */ + List getRoles(); + /** * Gets the effective name of this Discord server member. * @return the Discord server member's effective name diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordRole.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordRole.java index 089dd9d4..6392e41d 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordRole.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/guild/DiscordRole.java @@ -1,3 +1,26 @@ +/* + * 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.guild; import com.discordsrv.api.discord.api.entity.Snowflake; diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java index 47338fe9..6963039d 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java @@ -1,3 +1,26 @@ +/* + * 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.user; import com.discordsrv.api.discord.api.entity.Snowflake; diff --git a/api/src/main/java/com/discordsrv/api/discord/api/exception/RestErrorResponseException.java b/api/src/main/java/com/discordsrv/api/discord/api/exception/RestErrorResponseException.java index b3f5bd3c..5a509724 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/exception/RestErrorResponseException.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/exception/RestErrorResponseException.java @@ -27,11 +27,19 @@ public class RestErrorResponseException extends RuntimeException { private final int errorCode; + public RestErrorResponseException(int errorCode) { + this.errorCode = errorCode; + } + public RestErrorResponseException(int errorCode, Throwable cause) { super(cause); this.errorCode = errorCode; } + /** + * The error code if available, otherwise {@code -1}. + * @return the Discord error code or {@code -1} + */ public int getErrorCode() { return errorCode; } diff --git a/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownChannelException.java b/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownChannelException.java index df1fd779..11018a56 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownChannelException.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownChannelException.java @@ -23,10 +23,16 @@ package com.discordsrv.api.discord.api.exception; -public class UnknownChannelException extends RuntimeException { +import net.dv8tion.jda.api.requests.ErrorResponse; + +public class UnknownChannelException extends RestErrorResponseException { + + public UnknownChannelException() { + super(-1); + } public UnknownChannelException(Throwable cause) { - super(cause); + super(ErrorResponse.UNKNOWN_CHANNEL.getCode(), cause); } } diff --git a/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownMessageException.java b/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownMessageException.java index fb803bbe..795afc09 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownMessageException.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/exception/UnknownMessageException.java @@ -23,10 +23,16 @@ package com.discordsrv.api.discord.api.exception; -public class UnknownMessageException extends RuntimeException { +import net.dv8tion.jda.api.requests.ErrorResponse; + +public class UnknownMessageException extends RestErrorResponseException { + + public UnknownMessageException() { + super(-1); + } public UnknownMessageException(Throwable cause) { - super(cause); + super(ErrorResponse.UNKNOWN_MESSAGE.getCode(), cause); } } diff --git a/api/src/main/java/com/discordsrv/api/discord/connection/DiscordConnectionDetails.java b/api/src/main/java/com/discordsrv/api/discord/connection/DiscordConnectionDetails.java index b484c15e..ceac9ae7 100644 --- a/api/src/main/java/com/discordsrv/api/discord/connection/DiscordConnectionDetails.java +++ b/api/src/main/java/com/discordsrv/api/discord/connection/DiscordConnectionDetails.java @@ -26,6 +26,7 @@ package com.discordsrv.api.discord.connection; import com.discordsrv.api.DiscordSRVApi; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.cache.CacheFlag; +import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -46,6 +47,7 @@ public interface DiscordConnectionDetails { * The current gateway intents. * @return the current set of gateway intents */ + @NotNull Set getGatewayIntents(); /** @@ -56,12 +58,13 @@ public interface DiscordConnectionDetails { * @throws IllegalStateException if DiscordSRV is already connecting/connected to Discord * @see #readyToTakeDetails() */ - void requestGatewayIntent(GatewayIntent gatewayIntent, GatewayIntent... gatewayIntents); + void requestGatewayIntent(@NotNull GatewayIntent gatewayIntent, @NotNull GatewayIntent... gatewayIntents); /** * The current cache flags. * @return the current set of cache flags */ + @NotNull Set getCacheFlags(); /** @@ -73,6 +76,6 @@ public interface DiscordConnectionDetails { * @throws IllegalArgumentException if one of the requested {@link CacheFlag}s requires a {@link GatewayIntent} that hasn't been requested * @see #readyToTakeDetails() */ - void requestCacheFlag(CacheFlag cacheFlag, CacheFlag... cacheFlags); + void requestCacheFlag(@NotNull CacheFlag cacheFlag, @NotNull CacheFlag... cacheFlags); } diff --git a/api/src/main/java/com/discordsrv/api/event/bus/EventListener.java b/api/src/main/java/com/discordsrv/api/event/bus/EventListener.java index 6b773996..6372bb42 100644 --- a/api/src/main/java/com/discordsrv/api/event/bus/EventListener.java +++ b/api/src/main/java/com/discordsrv/api/event/bus/EventListener.java @@ -23,6 +23,8 @@ package com.discordsrv.api.event.bus; +import org.jetbrains.annotations.NotNull; + import java.lang.reflect.Method; /** @@ -36,6 +38,7 @@ public interface EventListener { * @return the event listener class name * @see Class#getName() */ + @NotNull String className(); /** @@ -43,6 +46,7 @@ public interface EventListener { * @return the method name for this event listener * @see Method#getName() */ + @NotNull String methodName(); } diff --git a/api/src/main/java/com/discordsrv/api/event/bus/internal/EventStateHolder.java b/api/src/main/java/com/discordsrv/api/event/bus/internal/EventStateHolder.java index 89b5e324..8f3fe28f 100644 --- a/api/src/main/java/com/discordsrv/api/event/bus/internal/EventStateHolder.java +++ b/api/src/main/java/com/discordsrv/api/event/bus/internal/EventStateHolder.java @@ -25,6 +25,7 @@ package com.discordsrv.api.event.bus.internal; import com.discordsrv.api.event.bus.EventListener; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; @ApiStatus.Internal public final class EventStateHolder { @@ -37,12 +38,12 @@ public final class EventStateHolder { private static class FakeListener implements EventListener { @Override - public String className() { + public @NotNull String className() { return null; } @Override - public String methodName() { + public @NotNull String methodName() { return null; } } diff --git a/api/src/main/java/com/discordsrv/api/player/DiscordSRVPlayer.java b/api/src/main/java/com/discordsrv/api/player/DiscordSRVPlayer.java index 6121a9ba..ccf1388e 100644 --- a/api/src/main/java/com/discordsrv/api/player/DiscordSRVPlayer.java +++ b/api/src/main/java/com/discordsrv/api/player/DiscordSRVPlayer.java @@ -23,6 +23,8 @@ package com.discordsrv.api.player; +import org.jetbrains.annotations.NotNull; + import java.util.UUID; /** @@ -34,12 +36,14 @@ public interface DiscordSRVPlayer { * The username of the player. * @return the player's username */ + @NotNull String getUsername(); /** * The {@link UUID} of the player. * @return the player's unique id */ + @NotNull UUID uuid(); } diff --git a/api/src/main/java/com/discordsrv/api/player/IPlayerProvider.java b/api/src/main/java/com/discordsrv/api/player/IPlayerProvider.java index 04bb1aeb..b53f6d47 100644 --- a/api/src/main/java/com/discordsrv/api/player/IPlayerProvider.java +++ b/api/src/main/java/com/discordsrv/api/player/IPlayerProvider.java @@ -38,6 +38,7 @@ public interface IPlayerProvider { * @param player the uuid for the player * @return the {@link DiscordSRVPlayer} instance for the player, if available */ + @NotNull Optional player(@NotNull UUID player); /** @@ -45,6 +46,7 @@ public interface IPlayerProvider { * @param username case-insensitive username for the player * @return the {@link DiscordSRVPlayer} instance for the player, if available */ + @NotNull Optional player(@NotNull String username); } diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitOfflinePlayer.java b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitOfflinePlayer.java index 45d6dd41..7e6bd782 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitOfflinePlayer.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitOfflinePlayer.java @@ -23,6 +23,7 @@ import com.discordsrv.common.player.IOfflinePlayer; import net.kyori.adventure.identity.Identity; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class BukkitOfflinePlayer implements IOfflinePlayer { @@ -30,14 +31,14 @@ public class BukkitOfflinePlayer implements IOfflinePlayer { private final OfflinePlayer offlinePlayer; private final Identity identity; - public BukkitOfflinePlayer(BukkitDiscordSRV discordSRV, OfflinePlayer offlinePlayer) { + public BukkitOfflinePlayer(BukkitDiscordSRV discordSRV, @NotNull OfflinePlayer offlinePlayer) { this.discordSRV = discordSRV; this.offlinePlayer = offlinePlayer; this.identity = Identity.identity(offlinePlayer.getUniqueId()); } @Override - public String getUsername() { + public @Nullable String getUsername() { return offlinePlayer.getName(); } diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayerProvider.java b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayerProvider.java index 0c8b2625..d3280187 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayerProvider.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayerProvider.java @@ -32,6 +32,7 @@ import org.bukkit.event.player.PlayerQuitEvent; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; public class BukkitPlayerProvider extends ServerPlayerProvider implements Listener { @@ -73,26 +74,31 @@ public class BukkitPlayerProvider extends ServerPlayerProvider imp // IOfflinePlayer - private Optional convert(OfflinePlayer offlinePlayer) { - if (offlinePlayer == null) { - return Optional.empty(); + private CompletableFuture> getFuture(Supplier provider) { + CompletableFuture> future = new CompletableFuture<>(); + try { + OfflinePlayer offlinePlayer = provider.get(); + if (offlinePlayer == null) { + future.complete(Optional.empty()); + return future; + } + + future.complete(Optional.of(new BukkitOfflinePlayer(discordSRV, offlinePlayer))); + } catch (Throwable t) { + future.completeExceptionally(t); } - return Optional.of(new BukkitOfflinePlayer(discordSRV, offlinePlayer)); + return future; } @Override public CompletableFuture> offlinePlayer(UUID uuid) { - CompletableFuture> future = new CompletableFuture<>(); - future.complete(convert(discordSRV.server().getOfflinePlayer(uuid))); - return future; + return getFuture(() -> discordSRV.server().getOfflinePlayer(uuid)); } @SuppressWarnings("deprecation") // Shut up, I know @Override public CompletableFuture> offlinePlayer(String username) { - CompletableFuture> future = new CompletableFuture<>(); - future.complete(convert(discordSRV.server().getOfflinePlayer(username))); - return future; + return getFuture(() -> discordSRV.server().getOfflinePlayer(username)); } public IOfflinePlayer offlinePlayer(OfflinePlayer offlinePlayer) { diff --git a/bungee/src/main/java/com/discordsrv/bungee/player/BungeePlayer.java b/bungee/src/main/java/com/discordsrv/bungee/player/BungeePlayer.java index 3ee43dd6..4df9fc94 100644 --- a/bungee/src/main/java/com/discordsrv/bungee/player/BungeePlayer.java +++ b/bungee/src/main/java/com/discordsrv/bungee/player/BungeePlayer.java @@ -57,7 +57,7 @@ public class BungeePlayer implements IPlayer { } @Override - public String getUsername() { + public @NotNull String getUsername() { return player.getName(); } diff --git a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java index f04c2544..e66b4a4c 100644 --- a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java +++ b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java @@ -44,6 +44,7 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.OverridingMethodsMustInvokeSuper; import java.util.Locale; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -96,8 +97,9 @@ public abstract class AbstractDiscordSRV jda() { + return Optional.ofNullable(discordConnectionManager) + .map(DiscordConnectionManager::instance); } @Override diff --git a/common/src/main/java/com/discordsrv/common/component/ComponentFactory.java b/common/src/main/java/com/discordsrv/common/component/ComponentFactory.java index db71490d..b5a48712 100644 --- a/common/src/main/java/com/discordsrv/common/component/ComponentFactory.java +++ b/common/src/main/java/com/discordsrv/common/component/ComponentFactory.java @@ -20,11 +20,12 @@ package com.discordsrv.common.component; import com.discordsrv.api.component.MinecraftComponent; import com.discordsrv.api.component.MinecraftComponentFactory; +import org.jetbrains.annotations.NotNull; public class ComponentFactory implements MinecraftComponentFactory { @Override - public MinecraftComponent empty() { + public @NotNull MinecraftComponent empty() { return MinecraftComponentImpl.empty(); } } diff --git a/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java b/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java index 7a22ffa3..ad8e3b8d 100644 --- a/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java +++ b/common/src/main/java/com/discordsrv/common/component/MinecraftComponentImpl.java @@ -62,7 +62,7 @@ public class MinecraftComponentImpl implements MinecraftComponent { } @Override - public String asPlainString() { + public @NotNull String asPlainString() { return PlainTextComponentSerializer.plainText().serialize(component); } @@ -98,7 +98,7 @@ public class MinecraftComponentImpl implements MinecraftComponent { } @Override - public Object getComponent() { + public @NotNull Object getComponent() { try { return adapter.deserialize() .invoke( @@ -111,7 +111,7 @@ public class MinecraftComponentImpl implements MinecraftComponent { } @Override - public void setComponent(Object adventureComponent) { + public void setComponent(@NotNull Object adventureComponent) { try { json = (String) adapter.serialize() .invoke( 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 5bb7cc93..69681842 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,6 +21,8 @@ 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.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.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.user.DiscordUser; @@ -30,12 +32,12 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.channels.BaseChannelConfig; import com.discordsrv.common.config.main.channels.ChannelConfig; import com.discordsrv.common.config.main.channels.ChannelConfigHolder; +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.user.DiscordUserImpl; import com.github.benmanes.caffeine.cache.*; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.Webhook; @@ -58,72 +60,62 @@ public class DiscordAPIImpl implements DiscordAPI { client.close(); } }) - .expireAfter(new CacheExpiry()) - .buildAsync(new CacheLoader()); + .expireAfter(new WebhookCacheExpiry()) + .buildAsync(new WebhookCacheLoader()); public DiscordAPIImpl(DiscordSRV discordSRV) { this.discordSRV = discordSRV; } - public WebhookClient getWebhookClient(String channelId) { - CompletableFuture clientFuture = cachedClients.getIfPresent(channelId); - if (clientFuture == null) { - return null; - } - - return clientFuture.join(); - } - public CompletableFuture queryWebhookClient(String channelId) { return cachedClients.get(channelId); } @Override - public Optional getTextChannelById(@NotNull String id) { - JDA jda = discordSRV.jda(); - if (jda == null) { - return Optional.empty(); + public @NotNull Optional getMessageChannelById(@NotNull String id) { + Optional textChannel = getTextChannelById(id); + if (textChannel.isPresent()) { + return textChannel; } - TextChannel textChannel = jda.getTextChannelById(id); - return textChannel != null - ? Optional.of(new DiscordTextChannelImpl(discordSRV, textChannel)) - : Optional.empty(); + return getDirectMessageChannelById(id); } @Override - public Optional getGuildById(@NotNull String id) { - JDA jda = discordSRV.jda(); - if (jda == null) { - return Optional.empty(); - } - - Guild guild = jda.getGuildById(id); - return guild != null - ? Optional.of(new DiscordGuildImpl(discordSRV, guild)) - : Optional.empty(); + public @NotNull Optional getDirectMessageChannelById(@NotNull String id) { + return discordSRV.jda() + .map(jda -> jda.getPrivateChannelById(id)) + .map(privateChannel -> new DiscordDMChannelImpl(discordSRV, privateChannel)); } @Override - public Optional getUserById(@NotNull String id) { - JDA jda = discordSRV.jda(); - if (jda == null) { - return Optional.empty(); - } - - User user = jda.getUserById(id); - return user != null - ? Optional.of(new DiscordUserImpl(user)) - : Optional.empty(); + public @NotNull Optional getTextChannelById(@NotNull String id) { + return discordSRV.jda() + .map(jda -> jda.getTextChannelById(id)) + .map(textChannel -> new DiscordTextChannelImpl(discordSRV, textChannel)); } - private class CacheLoader implements AsyncCacheLoader { + @Override + public @NotNull Optional getGuildById(@NotNull String id) { + return discordSRV.jda() + .map(jda -> jda.getGuildById(id)) + .map(guild -> new DiscordGuildImpl(discordSRV, guild)); + } + + @Override + public @NotNull Optional getUserById(@NotNull String id) { + return discordSRV.jda() + .map(jda -> jda.getUserById(id)) + .map(DiscordUserImpl::new); + } + + private class WebhookCacheLoader implements AsyncCacheLoader { @Override public @NonNull CompletableFuture asyncLoad(@NonNull String channelId, @NonNull Executor executor) { CompletableFuture future = new CompletableFuture<>(); - JDA jda = discordSRV.jda(); + JDA jda = discordSRV.jda().orElse(null); if (jda == null) { future.completeExceptionally(new NotReadyException()); return future; @@ -131,7 +123,7 @@ public class DiscordAPIImpl implements DiscordAPI { TextChannel textChannel = jda.getTextChannelById(channelId); if (textChannel == null) { - future.completeExceptionally(new UnknownChannelException(null)); + future.completeExceptionally(new UnknownChannelException()); return future; } @@ -167,7 +159,7 @@ public class DiscordAPIImpl implements DiscordAPI { } } - private class CacheExpiry implements Expiry { + private class WebhookCacheExpiry implements Expiry { private boolean isConfiguredChannel(String channelId) { for (ChannelConfigHolder value : discordSRV.config().channels.values()) { 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/channel/DiscordDMChannelImpl.java new file mode 100644 index 00000000..cf6d14cb --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java @@ -0,0 +1,98 @@ +/* + * 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 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.api.discord.api.entity.user.DiscordUser; +import com.discordsrv.api.discord.api.exception.NotReadyException; +import com.discordsrv.api.discord.api.exception.UnknownChannelException; +import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; +import com.discordsrv.common.discord.api.message.util.SendableDiscordMessageUtil; +import com.discordsrv.common.discord.api.user.DiscordUserImpl; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.PrivateChannel; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements DiscordDMChannel { + + private final DiscordSRV discordSRV; + private final String id; + private final DiscordUser user; + + public DiscordDMChannelImpl(DiscordSRV discordSRV, PrivateChannel privateChannel) { + this.discordSRV = discordSRV; + this.id = privateChannel.getId(); + this.user = new DiscordUserImpl(privateChannel.getUser()); + } + + private PrivateChannel privateChannel() { + JDA jda = discordSRV.jda().orElse(null); + if (jda == null) { + throw new NotReadyException(); + } + + PrivateChannel privateChannel = jda.getPrivateChannelById(id); + if (privateChannel == null) { + throw new UnknownChannelException(); + } + + return privateChannel; + } + + @Override + public @NotNull String getId() { + return id; + } + + @Override + public DiscordUser getUser() { + return user; + } + + @Override + public @NotNull CompletableFuture sendMessage(SendableDiscordMessage message) { + if (message.isWebhookMessage()) { + throw new IllegalArgumentException("Cannot send webhook messages to DMChannels"); + } + + CompletableFuture future = privateChannel() + .sendMessage(SendableDiscordMessageUtil.toJDA(message)) + .submit() + .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); + return mapExceptions(future); + } + + @Override + public @NotNull CompletableFuture editMessageById(String id, SendableDiscordMessage message) { + if (message.isWebhookMessage()) { + throw new IllegalArgumentException("Cannot send webhook messages to DMChannels"); + } + + CompletableFuture future = privateChannel() + .editMessageById(id, SendableDiscordMessageUtil.toJDA(message)) + .submit() + .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); + return mapExceptions(future); + } +} 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/channel/DiscordMessageChannelImpl.java new file mode 100644 index 00000000..792beb4f --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordMessageChannelImpl.java @@ -0,0 +1,57 @@ +/* + * 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 com.discordsrv.api.discord.api.exception.RestErrorResponseException; +import com.discordsrv.api.discord.api.exception.UnknownChannelException; +import com.discordsrv.api.discord.api.exception.UnknownMessageException; +import lombok.SneakyThrows; +import net.dv8tion.jda.api.exceptions.ErrorResponseException; +import net.dv8tion.jda.api.requests.ErrorResponse; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; + +public abstract class DiscordMessageChannelImpl { + + @SuppressWarnings("Convert2Lambda") // SneakyThrows + protected final CompletableFuture mapExceptions(CompletableFuture future) { + return future.handle(new BiFunction() { + + @SneakyThrows + @Override + public T apply(T msg, Throwable t) { + if (t instanceof ErrorResponseException) { + ErrorResponse errorResponse = ((ErrorResponseException) t).getErrorResponse(); + if (errorResponse != null) { + if (errorResponse == ErrorResponse.UNKNOWN_MESSAGE) { + throw new UnknownMessageException(t); + } else if (errorResponse == ErrorResponse.UNKNOWN_CHANNEL) { + throw new UnknownChannelException(t); + } + } + throw new RestErrorResponseException(((ErrorResponseException) t).getErrorCode(), t); + } else if (t != null) { + throw t; + } + return msg; + } + }); + } +} 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 index 2c84937f..c566022b 100644 --- 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 @@ -22,13 +22,11 @@ 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.exception.NotReadyException; -import com.discordsrv.api.discord.api.exception.RestErrorResponseException; -import com.discordsrv.api.discord.api.exception.UnknownChannelException; -import com.discordsrv.api.discord.api.exception.UnknownMessageException; 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.api.discord.api.exception.NotReadyException; +import com.discordsrv.api.discord.api.exception.UnknownChannelException; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.discord.api.guild.DiscordGuildImpl; import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; @@ -37,8 +35,6 @@ import net.dv8tion.jda.api.JDA; 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.exceptions.ErrorResponseException; -import net.dv8tion.jda.api.requests.ErrorResponse; import net.dv8tion.jda.api.requests.restaction.MessageAction; import net.dv8tion.jda.api.utils.MiscUtil; import org.jetbrains.annotations.NotNull; @@ -46,7 +42,7 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; -public class DiscordTextChannelImpl implements DiscordTextChannel { +public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements DiscordTextChannel { private final DiscordSRV discordSRV; private final String id; @@ -107,7 +103,7 @@ public class DiscordTextChannelImpl implements DiscordTextChannel { client, SendableDiscordMessageUtil.toWebhook(message))) .thenApply(msg -> ReceivedDiscordMessageImpl.fromWebhook(discordSRV, msg)); } else { - JDA jda = discordSRV.jda(); + JDA jda = discordSRV.jda().orElse(null); if (jda == null) { throw new NotReadyException(); } @@ -115,7 +111,7 @@ public class DiscordTextChannelImpl implements DiscordTextChannel { TextChannel textChannel = jda.getTextChannelById(getId()); if (textChannel == null) { future = new CompletableFuture<>(); - future.completeExceptionally(new UnknownChannelException(null)); + future.completeExceptionally(new UnknownChannelException()); return future; } @@ -125,21 +121,6 @@ public class DiscordTextChannelImpl implements DiscordTextChannel { .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)); } - return future.handle((msg, t) -> { - if (t instanceof ErrorResponseException) { - ErrorResponse errorResponse = ((ErrorResponseException) t).getErrorResponse(); - if (errorResponse != null) { - if (errorResponse == ErrorResponse.UNKNOWN_MESSAGE) { - throw new UnknownMessageException(t); - } else if (errorResponse == ErrorResponse.UNKNOWN_CHANNEL) { - throw new UnknownChannelException(t); - } - } - throw new RestErrorResponseException(((ErrorResponseException) t).getErrorCode(), t); - } else if (t != null) { - throw (RuntimeException) t; - } - return msg; - }); + return mapExceptions(future); } } 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/guild/DiscordGuildImpl.java index 0c06f326..836ca485 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildImpl.java @@ -1,11 +1,28 @@ +/* + * 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.guild; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.guild.DiscordGuildMember; +import com.discordsrv.api.discord.api.entity.guild.DiscordRole; import com.discordsrv.common.DiscordSRV; -import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; import java.util.Optional; @@ -31,21 +48,22 @@ public class DiscordGuildImpl implements DiscordGuild { return memberCount; } + private Optional guild() { + return discordSRV.jda() + .map(jda -> jda.getGuildById(id)); + } + @Override public Optional getMemberById(String id) { - JDA jda = discordSRV.jda(); - if (jda == null) { - return Optional.empty(); - } + return guild() + .map(guild -> guild.getMemberById(id)) + .map(DiscordGuildMemberImpl::new); + } - Guild guild = jda.getGuildById(this.id); - if (guild == null) { - return Optional.empty(); - } - - Member member = guild.getMemberById(id); - return member != null - ? Optional.of(new DiscordGuildMemberImpl(member)) - : Optional.empty(); + @Override + public Optional getRoleById(String id) { + return guild() + .map(guild -> guild.getRoleById(id)) + .map(DiscordRoleImpl::new); } } 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/guild/DiscordGuildMemberImpl.java index 7a4f6b8a..8383b3ce 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildMemberImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordGuildMemberImpl.java @@ -1,23 +1,57 @@ +/* + * 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.guild; import com.discordsrv.api.discord.api.entity.guild.DiscordGuildMember; +import com.discordsrv.api.discord.api.entity.guild.DiscordRole; import com.discordsrv.common.discord.api.user.DiscordUserImpl; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGuildMember { private final String nickname; + private final List roles; public DiscordGuildMemberImpl(Member member) { super(member.getUser()); this.nickname = member.getNickname(); + + List roles = new ArrayList<>(); + for (Role role : member.getRoles()) { + roles.add(new DiscordRoleImpl(role)); + } + this.roles = roles; } @Override public @NotNull Optional getNickname() { return Optional.ofNullable(nickname); } + + @Override + public List getRoles() { + return roles; + } } 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/guild/DiscordRoleImpl.java new file mode 100644 index 00000000..7b885aba --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/guild/DiscordRoleImpl.java @@ -0,0 +1,44 @@ +/* + * 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.guild; + +import com.discordsrv.api.discord.api.entity.guild.DiscordRole; +import net.dv8tion.jda.api.entities.Role; +import org.jetbrains.annotations.NotNull; + +public class DiscordRoleImpl implements DiscordRole { + + private final String id; + private final String name; + + public DiscordRoleImpl(Role role) { + this.id = role.getId(); + this.name = role.getName(); + } + + @Override + public @NotNull String getId() { + return id; + } + + @Override + public @NotNull String getName() { + return name; + } +} 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/message/ReceivedDiscordMessageImpl.java index 0d9ea0cf..3615001b 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java @@ -152,10 +152,14 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple @Override public @NotNull CompletableFuture edit(SendableDiscordMessage message) { + if (!isWebhookMessage() && message.isWebhookMessage()) { + throw new IllegalArgumentException("Cannot edit a non-webhook message into a webhook message"); + } + DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null); if (textChannel == null) { CompletableFuture future = new CompletableFuture<>(); - future.completeExceptionally(new UnknownChannelException(null)); + future.completeExceptionally(new UnknownChannelException()); return future; } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/user/DiscordUserImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/user/DiscordUserImpl.java index 1c430800..b615c5c8 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/user/DiscordUserImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/user/DiscordUserImpl.java @@ -1,3 +1,21 @@ +/* + * 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.user; import com.discordsrv.api.discord.api.entity.user.DiscordUser; diff --git a/common/src/main/java/com/discordsrv/common/discord/details/DiscordConnectionDetailsImpl.java b/common/src/main/java/com/discordsrv/common/discord/details/DiscordConnectionDetailsImpl.java index 38f6e3f7..757c0fb4 100644 --- a/common/src/main/java/com/discordsrv/common/discord/details/DiscordConnectionDetailsImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/details/DiscordConnectionDetailsImpl.java @@ -23,6 +23,7 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.exception.util.ExceptionUtil; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.cache.CacheFlag; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -49,12 +50,12 @@ public class DiscordConnectionDetailsImpl implements DiscordConnectionDetails { } @Override - public Set getGatewayIntents() { + public @NotNull Set getGatewayIntents() { return gatewayIntents; } @Override - public void requestGatewayIntent(GatewayIntent gatewayIntent, GatewayIntent... gatewayIntents) { + public void requestGatewayIntent(@NotNull GatewayIntent gatewayIntent, GatewayIntent... gatewayIntents) { check(); List intents = new ArrayList<>(Collections.singleton(gatewayIntent)); @@ -64,12 +65,12 @@ public class DiscordConnectionDetailsImpl implements DiscordConnectionDetails { } @Override - public Set getCacheFlags() { + public @NotNull Set getCacheFlags() { return cacheFlags; } @Override - public void requestCacheFlag(CacheFlag cacheFlag, CacheFlag... cacheFlags) { + public void requestCacheFlag(@NotNull CacheFlag cacheFlag, CacheFlag... cacheFlags) { check(); List flags = new ArrayList<>(Collections.singleton(cacheFlag)); diff --git a/common/src/main/java/com/discordsrv/common/event/bus/EventListenerImpl.java b/common/src/main/java/com/discordsrv/common/event/bus/EventListenerImpl.java index a4cf27da..6cf3e6ca 100644 --- a/common/src/main/java/com/discordsrv/common/event/bus/EventListenerImpl.java +++ b/common/src/main/java/com/discordsrv/common/event/bus/EventListenerImpl.java @@ -21,6 +21,7 @@ package com.discordsrv.common.event.bus; import com.discordsrv.api.event.bus.EventListener; import com.discordsrv.api.event.bus.EventPriority; import com.discordsrv.api.event.bus.Subscribe; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Method; @@ -55,12 +56,12 @@ public class EventListenerImpl implements EventListener { } @Override - public String className() { + public @NotNull String className() { return listenerClass.getName(); } @Override - public String methodName() { + public @NotNull String methodName() { return method.getName(); } diff --git a/common/src/main/java/com/discordsrv/common/player/IOfflinePlayer.java b/common/src/main/java/com/discordsrv/common/player/IOfflinePlayer.java index 7defb24e..3f491d75 100644 --- a/common/src/main/java/com/discordsrv/common/player/IOfflinePlayer.java +++ b/common/src/main/java/com/discordsrv/common/player/IOfflinePlayer.java @@ -20,14 +20,18 @@ package com.discordsrv.common.player; import net.kyori.adventure.identity.Identified; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.UUID; public interface IOfflinePlayer extends Identified { + @Nullable String getUsername(); @ApiStatus.NonExtendable + @NotNull default UUID uuid() { return identity().uuid(); } diff --git a/common/src/main/java/com/discordsrv/common/player/IPlayer.java b/common/src/main/java/com/discordsrv/common/player/IPlayer.java index 114ad098..f3f6a6af 100644 --- a/common/src/main/java/com/discordsrv/common/player/IPlayer.java +++ b/common/src/main/java/com/discordsrv/common/player/IPlayer.java @@ -22,6 +22,7 @@ import com.discordsrv.api.player.DiscordSRVPlayer; import com.discordsrv.common.command.game.sender.ICommandSender; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -29,7 +30,7 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende @Override @ApiStatus.NonExtendable - default UUID uuid() { + default @NotNull UUID uuid() { return identity().uuid(); } diff --git a/common/src/main/java/com/discordsrv/common/player/provider/AbstractPlayerProvider.java b/common/src/main/java/com/discordsrv/common/player/provider/AbstractPlayerProvider.java index 01436747..83f9259f 100644 --- a/common/src/main/java/com/discordsrv/common/player/provider/AbstractPlayerProvider.java +++ b/common/src/main/java/com/discordsrv/common/player/provider/AbstractPlayerProvider.java @@ -45,12 +45,12 @@ public abstract class AbstractPlayerProvider implements Playe } @Override - public final Optional player(@NotNull UUID uuid) { + public final @NotNull Optional player(@NotNull UUID uuid) { return Optional.ofNullable(players.get(uuid)); } @Override - public final Optional player(@NotNull String username) { + public final @NotNull Optional player(@NotNull String username) { for (T value : allPlayers) { if (value.getUsername().equalsIgnoreCase(username)) { return Optional.of(value); diff --git a/common/src/main/java/com/discordsrv/common/player/provider/PlayerProvider.java b/common/src/main/java/com/discordsrv/common/player/provider/PlayerProvider.java index f14efd1d..d5f1ec79 100644 --- a/common/src/main/java/com/discordsrv/common/player/provider/PlayerProvider.java +++ b/common/src/main/java/com/discordsrv/common/player/provider/PlayerProvider.java @@ -32,13 +32,13 @@ public interface PlayerProvider extends IPlayerProvider { * Gets an online player by {@link UUID}. * @param uuid the uuid of the Player */ - Optional player(@NotNull UUID uuid); + @NotNull Optional player(@NotNull UUID uuid); /** * Gets an online player by username. * @param username case-insensitive username for the player */ - Optional player(@NotNull String username); + @NotNull Optional player(@NotNull String username); /** * Gets all online players. diff --git a/sponge/src/main/java/com/discordsrv/sponge/player/SpongeOfflinePlayer.java b/sponge/src/main/java/com/discordsrv/sponge/player/SpongeOfflinePlayer.java index 78c92d0c..65ef5a62 100644 --- a/sponge/src/main/java/com/discordsrv/sponge/player/SpongeOfflinePlayer.java +++ b/sponge/src/main/java/com/discordsrv/sponge/player/SpongeOfflinePlayer.java @@ -32,7 +32,7 @@ public class SpongeOfflinePlayer implements IOfflinePlayer { } @Override - public String getUsername() { + public @NotNull String getUsername() { return user.name(); } diff --git a/velocity/src/main/java/com/discordsrv/velocity/player/VelocityPlayer.java b/velocity/src/main/java/com/discordsrv/velocity/player/VelocityPlayer.java index 209c0d29..0c4de623 100644 --- a/velocity/src/main/java/com/discordsrv/velocity/player/VelocityPlayer.java +++ b/velocity/src/main/java/com/discordsrv/velocity/player/VelocityPlayer.java @@ -51,7 +51,7 @@ public class VelocityPlayer implements IPlayer { } @Override - public String getUsername() { + public @NotNull String getUsername() { return player.getUsername(); }