mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-12-26 17:18:29 +01:00
Change common listeners to a module system, update to JDA5, fix typos, simplify 1st party api, improved the way mentions are translated between Minecraft and Discord
This commit is contained in:
parent
b415e5419e
commit
9ffce74061
@ -23,15 +23,16 @@
|
||||
|
||||
package com.discordsrv.api.discord.api;
|
||||
|
||||
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.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.api.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A basic Discord API wrapper for a limited amount of functions, with a minimal amount of breaking changes.
|
||||
@ -39,7 +40,7 @@ import java.util.Optional;
|
||||
public interface DiscordAPI {
|
||||
|
||||
/**
|
||||
* Gets a Discord message channel by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Gets a Discord message channel by id, the provided entity should not be cached.
|
||||
* @param id the id for the message channel
|
||||
* @return the message channel
|
||||
*/
|
||||
@ -47,7 +48,7 @@ public interface DiscordAPI {
|
||||
Optional<? extends DiscordMessageChannel> getMessageChannelById(long id);
|
||||
|
||||
/**
|
||||
* Gets a Discord direct message channel by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Gets a Discord direct message channel by id, the provided entity should not be cached.
|
||||
* @param id the id for the direct message channel
|
||||
* @return the direct message channel
|
||||
*/
|
||||
@ -55,7 +56,7 @@ public interface DiscordAPI {
|
||||
Optional<DiscordDMChannel> getDirectMessageChannelById(long id);
|
||||
|
||||
/**
|
||||
* Gets a Discord text channel by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Gets a Discord text channel by id, the provided entity should not be cached.
|
||||
* @param id the id for the text channel
|
||||
* @return the text channel
|
||||
*/
|
||||
@ -63,7 +64,7 @@ public interface DiscordAPI {
|
||||
Optional<DiscordTextChannel> getTextChannelById(long id);
|
||||
|
||||
/**
|
||||
* Gets a Discord server by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Gets a Discord server by id, the provided entity should not be cached.
|
||||
* @param id the id for the Discord server
|
||||
* @return the Discord server
|
||||
*/
|
||||
@ -71,15 +72,30 @@ public interface DiscordAPI {
|
||||
Optional<DiscordGuild> getGuildById(long id);
|
||||
|
||||
/**
|
||||
* Gets a Discord user by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Gets a Discord user by id, the provided entity should not be cached.
|
||||
* This will always return an empty optional if {@link #isUserCachingEnabled()} returns {@code false}.
|
||||
* @param id the id for the Discord user
|
||||
* @return the Discord user
|
||||
* @see #isUserCachingEnabled()
|
||||
*/
|
||||
@NotNull
|
||||
Optional<DiscordUser> getUserById(long id);
|
||||
|
||||
/**
|
||||
* Gets a Discord role by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* Looks up a Discord user by id from Discord, the provided entity can be cached but will not be updated if the entity changes on Discord.
|
||||
* @param id the id for the Discord user
|
||||
* @return a future that will result in a {@link DiscordUser} for the id or throw a
|
||||
*/
|
||||
CompletableFuture<DiscordUser> retrieveUserById(long id);
|
||||
|
||||
/**
|
||||
* Gets if user caching is enabled.
|
||||
* @return {@code true} if user caching is enabled.
|
||||
*/
|
||||
boolean isUserCachingEnabled();
|
||||
|
||||
/**
|
||||
* Gets a Discord role by id, the provided entity should not be cached.
|
||||
* @param id the id for the Discord role
|
||||
* @return the Discord role
|
||||
*/
|
||||
|
@ -23,13 +23,18 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
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 java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A Discord user.
|
||||
*/
|
||||
public interface DiscordUser extends Snowflake {
|
||||
public interface DiscordUser extends Snowflake, Mentionable {
|
||||
|
||||
/**
|
||||
* Gets if this user is the bot this DiscordSRV instance is connected.
|
||||
@ -67,4 +72,17 @@ public interface DiscordUser extends Snowflake {
|
||||
default String getAsTag() {
|
||||
return getUsername() + "#" + getDiscriminator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a private channel with the user or instantly returns the already cached private channel for this user.
|
||||
* @return a future for the private channel with this Discord user
|
||||
*/
|
||||
CompletableFuture<DiscordDMChannel> openPrivateChannel();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
User getAsJDAUser();
|
||||
}
|
||||
|
@ -21,18 +21,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.discordsrv.api.discord.api.exception;
|
||||
package com.discordsrv.api.discord.api.entity;
|
||||
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
|
||||
public class UnknownMessageException extends RestErrorResponseException {
|
||||
|
||||
public UnknownMessageException() {
|
||||
super(-1);
|
||||
}
|
||||
|
||||
public UnknownMessageException(Throwable cause) {
|
||||
super(ErrorResponse.UNKNOWN_MESSAGE.getCode(), cause);
|
||||
}
|
||||
public interface Mentionable {
|
||||
|
||||
String getAsMention();
|
||||
}
|
@ -23,7 +23,9 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity.channel;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.api.entity.DiscordUser;
|
||||
import net.dv8tion.jda.api.entities.PrivateChannel;
|
||||
|
||||
/**
|
||||
* A Discord direct message channel.
|
||||
@ -36,4 +38,11 @@ public interface DiscordDMChannel extends DiscordMessageChannel {
|
||||
*/
|
||||
DiscordUser getUser();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
PrivateChannel getAsJDAPrivateChannel();
|
||||
|
||||
}
|
||||
|
@ -23,9 +23,11 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity.channel;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.api.entity.Snowflake;
|
||||
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -64,4 +66,10 @@ public interface DiscordMessageChannel extends Snowflake {
|
||||
@NotNull
|
||||
CompletableFuture<ReceivedDiscordMessage> editMessageById(long id, SendableDiscordMessage message);
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
MessageChannel getAsJDAMessageChannel();
|
||||
}
|
||||
|
@ -23,13 +23,17 @@
|
||||
|
||||
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 {
|
||||
public interface DiscordTextChannel extends DiscordMessageChannel, Mentionable {
|
||||
|
||||
/**
|
||||
* Gets the name of the text channel.
|
||||
@ -42,7 +46,7 @@ public interface DiscordTextChannel extends DiscordMessageChannel {
|
||||
* Gets the topic of the text channel.
|
||||
* @return the topic of the channel
|
||||
*/
|
||||
@NotNull
|
||||
@Nullable
|
||||
String getTopic();
|
||||
|
||||
/**
|
||||
@ -52,5 +56,11 @@ public interface DiscordTextChannel extends DiscordMessageChannel {
|
||||
@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
|
||||
* @see DiscordSRVApi#jda()
|
||||
*/
|
||||
TextChannel getAsJDATextChannel();
|
||||
|
||||
}
|
||||
|
@ -23,10 +23,14 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity.guild;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.api.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A Discord server.
|
||||
@ -54,6 +58,12 @@ public interface DiscordGuild extends Snowflake {
|
||||
*/
|
||||
Optional<DiscordGuildMember> getMemberById(long id);
|
||||
|
||||
/**
|
||||
* Gets the members of this server that are in the cache.
|
||||
* @return the Discord server members that are currently cached
|
||||
*/
|
||||
Set<DiscordGuildMember> getCachedMembers();
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -61,4 +71,16 @@ public interface DiscordGuild extends Snowflake {
|
||||
*/
|
||||
Optional<DiscordRole> getRoleById(long id);
|
||||
|
||||
/**
|
||||
* Gets the roles in this Discord server.
|
||||
* @return an ordered list of the roles in this Discord server
|
||||
*/
|
||||
List<DiscordRole> getRoles();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
Guild getAsJDAGuild();
|
||||
}
|
||||
|
@ -23,9 +23,12 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity.guild;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.color.Color;
|
||||
import com.discordsrv.api.discord.api.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.api.entity.Mentionable;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
@ -34,7 +37,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* A Discord server member.
|
||||
*/
|
||||
public interface DiscordGuildMember extends DiscordUser {
|
||||
public interface DiscordGuildMember extends DiscordUser, Mentionable {
|
||||
|
||||
/**
|
||||
* Gets the Discord server this member is from.
|
||||
@ -71,4 +74,11 @@ public interface DiscordGuildMember extends DiscordUser {
|
||||
@Placeholder("user_color")
|
||||
Color getColor();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
Member getAsJDAMember();
|
||||
|
||||
}
|
||||
|
@ -23,15 +23,18 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.entity.guild;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.color.Color;
|
||||
import com.discordsrv.api.discord.api.entity.Mentionable;
|
||||
import com.discordsrv.api.discord.api.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A Discord server role.
|
||||
*/
|
||||
public interface DiscordRole extends Snowflake {
|
||||
public interface DiscordRole extends Snowflake, Mentionable {
|
||||
|
||||
/**
|
||||
* The default {@link DiscordRole} color.
|
||||
@ -68,4 +71,11 @@ public interface DiscordRole extends Snowflake {
|
||||
* @return true if this role is displayed separately in the member list
|
||||
*/
|
||||
boolean isHoisted();
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
Role getAsJDARole();
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import com.discordsrv.api.discord.api.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.api.entity.guild.DiscordGuildMember;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -40,6 +41,12 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflake {
|
||||
|
||||
/**
|
||||
* Gets the attachments of this message.
|
||||
* @return this message's attachments
|
||||
*/
|
||||
List<Attachment> getAttachments();
|
||||
|
||||
/**
|
||||
* Determines if this message was sent by this DiscordSRV instance's Discord bot,
|
||||
* or a webhook being used by this DiscordSRV instance.
|
||||
@ -62,14 +69,14 @@ public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflak
|
||||
|
||||
/**
|
||||
* Gets the text channel the message was sent in. Not present if this message is a dm.
|
||||
* @return a optional potentially containing the text channel the message was sent in
|
||||
* @return an optional potentially containing the text channel the message was sent in
|
||||
*/
|
||||
@NotNull
|
||||
Optional<DiscordTextChannel> getTextChannel();
|
||||
|
||||
/**
|
||||
* Gets the dm channel the message was sent in. Not present if this message was sent in a server.
|
||||
* @return a optional potentially containing the dm channel the message was sent in
|
||||
* @return an optional potentially containing the dm channel the message was sent in
|
||||
*/
|
||||
@NotNull
|
||||
Optional<DiscordDMChannel> getDMChannel();
|
||||
@ -77,14 +84,14 @@ public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflak
|
||||
/**
|
||||
* Gets the Discord server member that sent this message.
|
||||
* This is not present if the message was sent by a webhook.
|
||||
* @return a optional potentially containing the Discord server member that sent this message
|
||||
* @return an optional potentially containing the Discord server member that sent this message
|
||||
*/
|
||||
@NotNull
|
||||
Optional<DiscordGuildMember> getMember();
|
||||
|
||||
/**
|
||||
* Gets the Discord server the message was posted in. This is not present if the message was a dm.
|
||||
* @return a optional potentially containing the Discord server the message was posted in
|
||||
* @return an optional potentially containing the Discord server the message was posted in
|
||||
*/
|
||||
@NotNull
|
||||
default Optional<DiscordGuild> getGuild() {
|
||||
@ -109,4 +116,23 @@ public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflak
|
||||
*/
|
||||
@NotNull
|
||||
CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message);
|
||||
|
||||
class Attachment {
|
||||
|
||||
private final String fileName;
|
||||
private final String url;
|
||||
|
||||
public Attachment(String fileName, String url) {
|
||||
this.fileName = fileName;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String fileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String url() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,8 +214,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
throw new IllegalStateException("DiscordSRVApi not available");
|
||||
}
|
||||
this.replacements.put(PlaceholderService.PATTERN,
|
||||
wrapFunction(matcher ->
|
||||
api.placeholderService().getResultAsString(matcher, context)));
|
||||
wrapFunction(matcher -> api.placeholderService().getResultAsString(matcher, context)));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -23,16 +23,4 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.exception;
|
||||
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
|
||||
public class UnknownChannelException extends RestErrorResponseException {
|
||||
|
||||
public UnknownChannelException() {
|
||||
super(-1);
|
||||
}
|
||||
|
||||
public UnknownChannelException(Throwable cause) {
|
||||
super(ErrorResponse.UNKNOWN_CHANNEL.getCode(), cause);
|
||||
}
|
||||
|
||||
}
|
||||
public class EntityNoLongerAvailableException extends Exception {}
|
@ -23,16 +23,18 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.exception;
|
||||
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
|
||||
public class RestErrorResponseException extends RuntimeException {
|
||||
|
||||
private final int errorCode;
|
||||
|
||||
public RestErrorResponseException(int errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
public RestErrorResponseException(ErrorResponse response) {
|
||||
this(response.getCode(), response.getMeaning(), new EntityNoLongerAvailableException());
|
||||
}
|
||||
|
||||
public RestErrorResponseException(int errorCode, Throwable cause) {
|
||||
super(cause);
|
||||
public RestErrorResponseException(int errorCode, String message, Throwable cause) {
|
||||
super(message + " (" + errorCode + ")", cause);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,16 @@
|
||||
|
||||
package com.discordsrv.api.discord.api.util;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public final class DiscordFormattingUtil {
|
||||
|
||||
private DiscordFormattingUtil() {}
|
||||
|
||||
public static String escapeContent(String content) {
|
||||
content = escapeChars(content, '*', '_', '|', '`', '~', '>');
|
||||
content = escapeChars(content, '*', '_', '|', '`', '~');
|
||||
content = escapeQuote(content);
|
||||
content = escapeMentions(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
@ -40,4 +44,12 @@ public final class DiscordFormattingUtil {
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private static String escapeQuote(String input) {
|
||||
return input.replaceAll("^>", Matcher.quoteReplacement("\\>"));
|
||||
}
|
||||
|
||||
private static String escapeMentions(String input) {
|
||||
return input.replaceAll("<([@#])", Matcher.quoteReplacement("\\<") + "$1");
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public interface EventBus {
|
||||
|
||||
/**
|
||||
* Subscribes the provided event listener to this {@link EventBus}.
|
||||
* @param eventListener a event listener with at least one valid {@link Subscribe} method.
|
||||
* @param eventListener an event listener with at least one valid {@link Subscribe} method.
|
||||
*
|
||||
* @throws IllegalArgumentException if the given listener does not contain any valid listeners
|
||||
*/
|
||||
@ -43,7 +43,7 @@ public interface EventBus {
|
||||
|
||||
/**
|
||||
* Unsubscribes a listener that was registered before.
|
||||
* @param eventListener a listener that was subscribed with {@link #subscribe(Object)} before
|
||||
* @param eventListener an event listener that was subscribed with {@link #subscribe(Object)} before
|
||||
*/
|
||||
void unsubscribe(@NotNull Object eventListener);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
package com.discordsrv.api.event.events.message.receive.discord;
|
||||
|
||||
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.event.events.Cancellable;
|
||||
import com.discordsrv.api.event.events.Processable;
|
||||
@ -33,7 +34,7 @@ public class DiscordMessageProcessingEvent implements Cancellable, Processable {
|
||||
|
||||
private final ReceivedDiscordMessage discordMessage;
|
||||
private String messageContent;
|
||||
private DiscordTextChannel channel;
|
||||
private final DiscordTextChannel channel;
|
||||
private boolean cancelled;
|
||||
private boolean processed;
|
||||
|
||||
@ -59,8 +60,8 @@ public class DiscordMessageProcessingEvent implements Cancellable, Processable {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(DiscordTextChannel channel) {
|
||||
this.channel = channel;
|
||||
public DiscordGuild getGuild() {
|
||||
return channel.getGuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,7 +14,7 @@ ext {
|
||||
// MinecraftDependencyDownload
|
||||
mddVersion = '1.0.0-SNAPSHOT'
|
||||
// JDA
|
||||
jdaVersion = '4.3.0_334'
|
||||
jdaVersion = '5.0.0-alpha.2'
|
||||
// Configurate
|
||||
configurateVersion = '4.1.2'
|
||||
// Adventure & Adventure Platform
|
||||
@ -59,7 +59,7 @@ allprojects {
|
||||
|
||||
mavenCentral()
|
||||
maven { url 'https://nexus.scarsz.me/content/groups/public/' }
|
||||
maven { url 'https://m2.dv8tion.net/releases/' }
|
||||
//maven { url 'https://m2.dv8tion.net/releases/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -24,7 +24,6 @@ import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
import com.discordsrv.common.api.util.ApiInstanceUtil;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
||||
import com.discordsrv.common.component.ComponentFactory;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
@ -36,11 +35,13 @@ import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;
|
||||
import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl;
|
||||
import com.discordsrv.common.event.bus.EventBusImpl;
|
||||
import com.discordsrv.common.function.CheckedRunnable;
|
||||
import com.discordsrv.common.listener.ChannelLookupListener;
|
||||
import com.discordsrv.common.listener.DiscordAPIListener;
|
||||
import com.discordsrv.common.listener.DiscordChatListener;
|
||||
import com.discordsrv.common.listener.GameChatListener;
|
||||
import com.discordsrv.common.logging.DependencyLoggingHandler;
|
||||
import com.discordsrv.common.module.Module;
|
||||
import com.discordsrv.common.module.ModuleManager;
|
||||
import com.discordsrv.common.module.modules.DiscordAPIEventModule;
|
||||
import com.discordsrv.common.module.modules.DiscordToMinecraftModule;
|
||||
import com.discordsrv.common.module.modules.GlobalChannelLookupModule;
|
||||
import com.discordsrv.common.module.modules.MinecraftToDiscordModule;
|
||||
import com.discordsrv.common.placeholder.ComponentResultStringifier;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.placeholder.context.GlobalTextHandlingContext;
|
||||
@ -49,6 +50,7 @@ import net.dv8tion.jda.api.JDA;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -73,8 +75,8 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
private DiscordConnectionDetails discordConnectionDetails;
|
||||
|
||||
// DiscordSRV
|
||||
private final DefaultGlobalChannel defaultGlobalChannel = new DefaultGlobalChannel(this);
|
||||
private ChannelConfigHelper channelConfig;
|
||||
private ModuleManager moduleManager;
|
||||
private DiscordConnectionManager discordConnectionManager;
|
||||
|
||||
// Internal
|
||||
@ -132,11 +134,6 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
|
||||
// DiscordSRV
|
||||
|
||||
@Override
|
||||
public DefaultGlobalChannel defaultGlobalChannel() {
|
||||
return defaultGlobalChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfigHelper channelConfig() {
|
||||
return channelConfig;
|
||||
@ -164,6 +161,21 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
return configManager().config();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Module> T getModule(Class<T> moduleType) {
|
||||
return moduleManager.getModule(moduleType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerModule(Module module) {
|
||||
moduleManager.register(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterModule(Module module) {
|
||||
moduleManager.unregister(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale locale() {
|
||||
// TODO: config
|
||||
@ -250,13 +262,16 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
// Register PlayerProvider listeners
|
||||
playerProvider().subscribe();
|
||||
|
||||
// Register listeners
|
||||
// DiscordAPI
|
||||
eventBus().subscribe(new DiscordAPIListener(this));
|
||||
// Chat
|
||||
eventBus().subscribe(new ChannelLookupListener(this));
|
||||
eventBus().subscribe(new GameChatListener(this));
|
||||
eventBus().subscribe(new DiscordChatListener(this));
|
||||
// Register modules
|
||||
moduleManager = new ModuleManager(this);
|
||||
for (Module module : Arrays.asList(
|
||||
new DiscordAPIEventModule(this),
|
||||
new DiscordToMinecraftModule(this),
|
||||
new GlobalChannelLookupModule(this),
|
||||
new MinecraftToDiscordModule(this)
|
||||
)) {
|
||||
registerModule(module);
|
||||
}
|
||||
}
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
|
@ -20,7 +20,6 @@ package com.discordsrv.common;
|
||||
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.common.channel.ChannelConfigHelper;
|
||||
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
||||
import com.discordsrv.common.component.ComponentFactory;
|
||||
import com.discordsrv.common.config.connection.ConnectionConfig;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
@ -29,10 +28,11 @@ import com.discordsrv.common.config.manager.MainConfigManager;
|
||||
import com.discordsrv.common.console.Console;
|
||||
import com.discordsrv.common.discord.api.DiscordAPIImpl;
|
||||
import com.discordsrv.common.discord.connection.DiscordConnectionManager;
|
||||
import com.discordsrv.logging.Logger;
|
||||
import com.discordsrv.common.module.Module;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.player.provider.AbstractPlayerProvider;
|
||||
import com.discordsrv.common.scheduler.Scheduler;
|
||||
import com.discordsrv.logging.Logger;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -72,12 +72,17 @@ public interface DiscordSRV extends DiscordSRVApi {
|
||||
ConnectionConfig connectionConfig();
|
||||
MainConfigManager<? extends MainConfig> configManager();
|
||||
MainConfig config();
|
||||
// Config helper
|
||||
ChannelConfigHelper channelConfig();
|
||||
|
||||
// Internal
|
||||
DefaultGlobalChannel defaultGlobalChannel();
|
||||
ChannelConfigHelper channelConfig();
|
||||
DiscordConnectionManager discordConnectionManager();
|
||||
|
||||
// Modules
|
||||
<T extends Module> T getModule(Class<T> moduleType);
|
||||
void registerModule(Module module);
|
||||
void unregisterModule(Module module);
|
||||
|
||||
Locale locale();
|
||||
void setStatus(Status status);
|
||||
|
||||
|
@ -88,9 +88,7 @@ public class ChannelConfigHelper {
|
||||
|
||||
synchronized (discordToConfigMap) {
|
||||
discordToConfigMap.clear();
|
||||
for (Map.Entry<Long, Pair<String, ChannelConfig>> entry : newMap.entrySet()) {
|
||||
discordToConfigMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
discordToConfigMap.putAll(newMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ public class ComponentFactory implements MinecraftComponentFactory {
|
||||
public ComponentFactory(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.minecraftSerializer = new MinecraftSerializer(
|
||||
MinecraftSerializerOptions.defaults().addRenderer(new DiscordSRVMinecraftRenderer(discordSRV)),
|
||||
MinecraftSerializerOptions.escapeDefaults()
|
||||
MinecraftSerializerOptions.defaults()
|
||||
.addRenderer(new DiscordSRVMinecraftRenderer(discordSRV))
|
||||
);
|
||||
this.discordSerializer = new DiscordSerializer(DiscordSerializerOptions.defaults());
|
||||
}
|
||||
|
@ -18,79 +18,143 @@
|
||||
|
||||
package com.discordsrv.common.component.renderer;
|
||||
|
||||
import com.discordsrv.api.component.EnhancedTextBuilder;
|
||||
import com.discordsrv.api.discord.api.entity.DiscordUser;
|
||||
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.api.event.events.message.receive.discord.DiscordMessageProcessingEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import dev.vankka.mcdiscordreserializer.renderer.implementation.DefaultMinecraftRenderer;
|
||||
import lombok.NonNull;
|
||||
import net.dv8tion.jda.api.entities.AbstractChannel;
|
||||
import net.dv8tion.jda.api.entities.GuildChannel;
|
||||
import net.dv8tion.jda.api.utils.MiscUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
|
||||
|
||||
private static final ThreadLocal<Long> GUILD_CONTEXT = ThreadLocal.withInitial(() -> 0L);
|
||||
private static final ThreadLocal<Context> CONTEXT = new ThreadLocal<>();
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public DiscordSRVMinecraftRenderer(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
public static void runInGuildContext(long guildId, Runnable runnable) {
|
||||
getWithGuildContext(guildId, () -> {
|
||||
public static void runInContext(
|
||||
DiscordMessageProcessingEvent event,
|
||||
OrDefault<DiscordToMinecraftChatConfig> config,
|
||||
Runnable runnable
|
||||
) {
|
||||
getWithContext(event, config, () -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> T getWithGuildContext(long guildId, Supplier<T> supplier) {
|
||||
GUILD_CONTEXT.set(guildId);
|
||||
public static <T> T getWithContext(
|
||||
DiscordMessageProcessingEvent event,
|
||||
OrDefault<DiscordToMinecraftChatConfig> config,
|
||||
Supplier<T> supplier
|
||||
) {
|
||||
CONTEXT.set(new Context(event, config));
|
||||
T output = supplier.get();
|
||||
GUILD_CONTEXT.set(0L);
|
||||
CONTEXT.remove();
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component appendChannelMention(@NonNull Component component, @NonNull String id) {
|
||||
return component.append(Component.text(
|
||||
discordSRV.jda()
|
||||
.map(jda -> jda.getGuildChannelById(id))
|
||||
.map(AbstractChannel::getName)
|
||||
.map(name -> "#" + name)
|
||||
.orElse("<#" + id + ">")
|
||||
public @NotNull Component appendChannelMention(@NonNull Component component, @NonNull String id) {
|
||||
Context context = CONTEXT.get();
|
||||
DiscordToMinecraftChatConfig.Mentions.Format format =
|
||||
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.channel) : null;
|
||||
if (format == null) {
|
||||
return component.append(Component.text("<#" + id + ">"));
|
||||
}
|
||||
|
||||
GuildChannel guildChannel = discordSRV.jda()
|
||||
.map(jda -> jda.getGuildChannelById(id))
|
||||
.orElse(null);
|
||||
|
||||
return component.append(ComponentUtil.fromAPI(
|
||||
discordSRV.componentFactory()
|
||||
.enhancedBuilder(guildChannel != null ? format.format : format.unknownFormat)
|
||||
.addReplacement("%channel_name%", guildChannel != null ? guildChannel.getName() : null)
|
||||
.applyPlaceholderService()
|
||||
.build()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component appendUserMention(@NonNull Component component, @NonNull String id) {
|
||||
long guildId = GUILD_CONTEXT.get();
|
||||
Optional<DiscordGuild> guild = guildId > 0
|
||||
? discordSRV.discordAPI().getGuildById(guildId)
|
||||
: Optional.empty();
|
||||
public @NotNull Component appendUserMention(@NonNull Component component, @NonNull String id) {
|
||||
Context context = CONTEXT.get();
|
||||
DiscordToMinecraftChatConfig.Mentions.Format format =
|
||||
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.user) : null;
|
||||
DiscordGuild guild = context != null
|
||||
? discordSRV.discordAPI()
|
||||
.getGuildById(context.event.getGuild().getId())
|
||||
.orElse(null)
|
||||
: null;
|
||||
if (format == null || guild == null) {
|
||||
return component.append(Component.text("<@" + id + ">"));
|
||||
}
|
||||
|
||||
long userId = MiscUtil.parseLong(id);
|
||||
return component.append(Component.text(
|
||||
guild.flatMap(g -> g.getMemberById(userId))
|
||||
.map(member -> "@" + member.getEffectiveName())
|
||||
.orElseGet(() -> discordSRV.discordAPI()
|
||||
.getUserById(userId)
|
||||
.map(user -> "@" + user.getUsername())
|
||||
.orElse("<@" + id + ">"))
|
||||
DiscordUser user = discordSRV.discordAPI().getUserById(userId).orElse(null);
|
||||
DiscordGuildMember member = guild.getMemberById(userId).orElse(null);
|
||||
|
||||
EnhancedTextBuilder builder = discordSRV.componentFactory()
|
||||
.enhancedBuilder(user != null ? format.format : format.unknownFormat);
|
||||
|
||||
if (user != null) {
|
||||
builder.addContext(user);
|
||||
}
|
||||
if (member != null) {
|
||||
builder.addContext(member);
|
||||
}
|
||||
|
||||
return component.append(ComponentUtil.fromAPI(
|
||||
builder.applyPlaceholderService().build()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component appendRoleMention(@NonNull Component component, @NonNull String id) {
|
||||
return component.append(Component.text(
|
||||
discordSRV.discordAPI()
|
||||
.getRoleById(MiscUtil.parseLong(id))
|
||||
.map(DiscordRole::getName)
|
||||
.map(name -> "@" + name)
|
||||
.orElse("<@" + id + ">")
|
||||
public @NotNull Component appendRoleMention(@NonNull Component component, @NonNull String id) {
|
||||
Context context = CONTEXT.get();
|
||||
DiscordToMinecraftChatConfig.Mentions.Format format =
|
||||
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.role) : null;
|
||||
if (format == null) {
|
||||
return component.append(Component.text("<#" + id + ">"));
|
||||
}
|
||||
|
||||
long roleId = MiscUtil.parseLong(id);
|
||||
DiscordRole role = discordSRV.discordAPI().getRoleById(roleId).orElse(null);
|
||||
|
||||
EnhancedTextBuilder builder = discordSRV.componentFactory()
|
||||
.enhancedBuilder(role != null ? format.format : format.unknownFormat);
|
||||
|
||||
if (role != null) {
|
||||
builder.addContext(role);
|
||||
}
|
||||
|
||||
return component.append(ComponentUtil.fromAPI(
|
||||
builder.applyPlaceholderService().build()
|
||||
));
|
||||
}
|
||||
|
||||
private static class Context {
|
||||
|
||||
private final DiscordMessageProcessingEvent event;
|
||||
private final OrDefault<DiscordToMinecraftChatConfig> config;
|
||||
|
||||
public Context(DiscordMessageProcessingEvent event, OrDefault<DiscordToMinecraftChatConfig> config) {
|
||||
this.event = event;
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,20 +31,21 @@ import java.util.regex.Pattern;
|
||||
public class DiscordToMinecraftChatConfig {
|
||||
|
||||
@Comment("The Discord to Minecraft message format for regular users")
|
||||
public String format = "[ᛩF2Discord&r] [hover:show_text:Tag: %user_tag%&r\\nRoles: %user_roles_, |text_&7&oNone%%]%user_color%%user_effective_name%&r » %message%";
|
||||
public String format = "[ᛩF2Discord&r] [hover:show_text:Tag: %user_tag%&r\nRoles: %user_roles_, |text_&7&oNone%]%user_color%%user_effective_name%&r » %message% %message_attachments%";
|
||||
|
||||
@Comment("The Discord to Minecraft message format for webhook messages (if enabled)")
|
||||
public String webhookFormat = "[ᛩF2Discord&r] [hover:show_text:Webhook message]%user_name%&r » %message%";
|
||||
|
||||
@Comment("Users, bots and webhooks to ignore")
|
||||
public Ignores ignores = new Ignores();
|
||||
public String webhookFormat = "[ᛩF2Discord&r] [hover:show_text:Webhook message]%user_name%&r » %message% %message_attachments%";
|
||||
|
||||
// TODO: more info on regex pairs (String#replaceAll)
|
||||
@Comment("Regex filters for Discord message contents (this is the %message% part of the \"format\" option)")
|
||||
public Map<Pattern, String> contentRegexFilters = new LinkedHashMap<>();
|
||||
|
||||
@Comment("Users, bots and webhooks to ignore")
|
||||
public Ignores ignores = new Ignores();
|
||||
|
||||
@ConfigSerializable
|
||||
public static class Ignores {
|
||||
|
||||
@Comment("User, bot and webhook ids to ignore")
|
||||
public IDs usersAndWebhookIds = new IDs();
|
||||
|
||||
@ -67,4 +68,33 @@ public class DiscordToMinecraftChatConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("The representations of Discord mentions in-game")
|
||||
public Mentions mentions = new Mentions();
|
||||
|
||||
@ConfigSerializable
|
||||
public static class Mentions {
|
||||
|
||||
public Format role = new Format("ᛩf2@%role_name%", "ᛩf2@deleted-role");
|
||||
public Format channel = new Format("ᛩf2#%channel_name%", "ᛩf2#deleted-channel");
|
||||
public Format user = new Format("[hover:show_text:Tag: %user_tag%&r\nRoles: %user_roles_, |text_&7&oNone%]ᛩf2@%user_effective_name|user_name%", "ᛩf2@Unknown user");
|
||||
|
||||
@ConfigSerializable
|
||||
public static class Format {
|
||||
|
||||
@Comment("The format shown in-game")
|
||||
public String format = "";
|
||||
|
||||
@Comment("The format when the entity is deleted or can't be looked up")
|
||||
public String unknownFormat = "";
|
||||
|
||||
public Format() {}
|
||||
|
||||
public Format(String format, String unknownFormat) {
|
||||
this.format = format;
|
||||
this.unknownFormat = unknownFormat;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,4 +38,16 @@ public class MinecraftToDiscordChatConfig {
|
||||
@Comment("Regex filters for Minecraft message contents (this is the %message% part of the \"format\" option)")
|
||||
public Map<Pattern, String> contentRegexFilters = new LinkedHashMap<>();
|
||||
|
||||
@Comment("What mentions should be translated from chat messages to mentions (this does not effect if they will cause a notification or not)")
|
||||
public Mentions mentions = new Mentions();
|
||||
|
||||
@ConfigSerializable
|
||||
public static class Mentions {
|
||||
|
||||
public boolean roles = true;
|
||||
public boolean users = true;
|
||||
public boolean channels = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ 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.guild.DiscordRole;
|
||||
import com.discordsrv.api.discord.api.exception.NotReadyException;
|
||||
import com.discordsrv.api.discord.api.exception.UnknownChannelException;
|
||||
import com.discordsrv.api.discord.api.exception.RestErrorResponseException;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||
@ -36,7 +36,6 @@ 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.discord.api.user.DiscordUserImpl;
|
||||
import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.Expiry;
|
||||
@ -45,6 +44,9 @@ import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
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.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;
|
||||
@ -80,6 +82,26 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
return cachedClients;
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> mapExceptions(CompletableFuture<T> future) {
|
||||
return future.handle((msg, t) -> {
|
||||
if (t instanceof ErrorResponseException) {
|
||||
ErrorResponseException exception = (ErrorResponseException) t;
|
||||
int code = exception.getErrorCode();
|
||||
ErrorResponse response = exception.getErrorResponse();
|
||||
throw new RestErrorResponseException(code, response != null ? response.getMeaning() : "Unknown", t);
|
||||
} else if (t != null) {
|
||||
throw (RuntimeException) t;
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> notReady() {
|
||||
CompletableFuture<T> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new NotReadyException());
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<? extends DiscordMessageChannel> getMessageChannelById(long id) {
|
||||
Optional<DiscordTextChannel> textChannel = getTextChannelById(id);
|
||||
@ -115,7 +137,26 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
public @NotNull Optional<DiscordUser> getUserById(long id) {
|
||||
return discordSRV.jda()
|
||||
.map(jda -> jda.getUserById(id))
|
||||
.map(DiscordUserImpl::new);
|
||||
.map(user -> new DiscordUserImpl(discordSRV, user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DiscordUser> retrieveUserById(long id) {
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
return notReady();
|
||||
}
|
||||
|
||||
return jda.retrieveUserById(id)
|
||||
.submit()
|
||||
.thenApply(user -> new DiscordUserImpl(discordSRV, user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserCachingEnabled() {
|
||||
return discordSRV.discordConnectionDetails()
|
||||
.getGatewayIntents()
|
||||
.contains(GatewayIntent.GUILD_MEMBERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,17 +170,15 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<WebhookClient> asyncLoad(@NonNull Long channelId, @NonNull Executor executor) {
|
||||
CompletableFuture<WebhookClient> future = new CompletableFuture<>();
|
||||
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
future.completeExceptionally(new NotReadyException());
|
||||
return future;
|
||||
return discordSRV.discordAPI().notReady();
|
||||
}
|
||||
|
||||
CompletableFuture<WebhookClient> future = new CompletableFuture<>();
|
||||
TextChannel textChannel = jda.getTextChannelById(channelId);
|
||||
if (textChannel == null) {
|
||||
future.completeExceptionally(new UnknownChannelException());
|
||||
future.completeExceptionally(new IllegalArgumentException("Channel could not be found"));
|
||||
return future;
|
||||
}
|
||||
|
||||
|
@ -16,31 +16,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.discord.api.user;
|
||||
package com.discordsrv.common.discord.api;
|
||||
|
||||
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 net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DiscordUserImpl implements DiscordUser {
|
||||
|
||||
private final long id;
|
||||
private final DiscordSRV discordSRV;
|
||||
private final User user;
|
||||
private final boolean self;
|
||||
private final boolean bot;
|
||||
private final String username;
|
||||
private final String discriminator;
|
||||
|
||||
public DiscordUserImpl(User user) {
|
||||
this.id = user.getIdLong();
|
||||
public DiscordUserImpl(DiscordSRV discordSRV, User user) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.user = user;
|
||||
this.self = user.getIdLong() == user.getJDA().getSelfUser().getIdLong();
|
||||
this.bot = user.isBot();
|
||||
this.username = user.getName();
|
||||
this.discriminator = user.getDiscriminator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
return user.getIdLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,16 +52,39 @@ public class DiscordUserImpl implements DiscordUser {
|
||||
|
||||
@Override
|
||||
public boolean isBot() {
|
||||
return bot;
|
||||
return user.isBot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getUsername() {
|
||||
return username;
|
||||
return user.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDiscriminator() {
|
||||
return discriminator;
|
||||
return user.getDiscriminator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DiscordDMChannel> openPrivateChannel() {
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
return discordSRV.discordAPI().notReady();
|
||||
}
|
||||
|
||||
return jda.retrieveUserById(getId())
|
||||
.submit()
|
||||
.thenCompose(user -> user.openPrivateChannel().submit())
|
||||
.thenApply(privateChannel -> new DiscordDMChannelImpl(discordSRV, privateChannel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getAsJDAUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return user.getAsMention();
|
||||
}
|
||||
}
|
@ -18,17 +18,15 @@
|
||||
|
||||
package com.discordsrv.common.discord.api.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.api.discord.api.entity.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.DiscordUserImpl;
|
||||
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.MessageChannel;
|
||||
import net.dv8tion.jda.api.entities.PrivateChannel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -37,32 +35,18 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements DiscordDMChannel {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final long id;
|
||||
private final PrivateChannel privateChannel;
|
||||
private final DiscordUser user;
|
||||
|
||||
public DiscordDMChannelImpl(DiscordSRV discordSRV, PrivateChannel privateChannel) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.id = privateChannel.getIdLong();
|
||||
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;
|
||||
this.privateChannel = privateChannel;
|
||||
this.user = new DiscordUserImpl(discordSRV, privateChannel.getUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
return privateChannel.getIdLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,25 +54,28 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateChannel getAsJDAPrivateChannel() {
|
||||
return privateChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ReceivedDiscordMessage> sendMessage(SendableDiscordMessage message) {
|
||||
if (message.isWebhookMessage()) {
|
||||
throw new IllegalArgumentException("Cannot send webhook messages to DMChannels");
|
||||
}
|
||||
|
||||
CompletableFuture<ReceivedDiscordMessage> future = privateChannel()
|
||||
CompletableFuture<ReceivedDiscordMessage> future = privateChannel
|
||||
.sendMessage(SendableDiscordMessageUtil.toJDA(message))
|
||||
.submit()
|
||||
.thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg));
|
||||
return mapExceptions(future);
|
||||
|
||||
return discordSRV.discordAPI().mapExceptions(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteMessageById(long id) {
|
||||
CompletableFuture<Void> future = privateChannel()
|
||||
.deleteMessageById(id)
|
||||
.submit();
|
||||
return mapExceptions(future);
|
||||
return discordSRV.discordAPI().mapExceptions(privateChannel.deleteMessageById(id).submit());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -97,10 +84,16 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D
|
||||
throw new IllegalArgumentException("Cannot send webhook messages to DMChannels");
|
||||
}
|
||||
|
||||
CompletableFuture<ReceivedDiscordMessage> future = privateChannel()
|
||||
CompletableFuture<ReceivedDiscordMessage> future = privateChannel
|
||||
.editMessageById(id, SendableDiscordMessageUtil.toJDA(message))
|
||||
.submit()
|
||||
.thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg));
|
||||
return mapExceptions(future);
|
||||
|
||||
return discordSRV.discordAPI().mapExceptions(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel getAsJDAMessageChannel() {
|
||||
return privateChannel;
|
||||
}
|
||||
}
|
||||
|
@ -19,19 +19,10 @@
|
||||
package com.discordsrv.common.discord.api.channel;
|
||||
|
||||
import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel;
|
||||
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.common.DiscordSRV;
|
||||
import lombok.SneakyThrows;
|
||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||
import net.dv8tion.jda.api.entities.PrivateChannel;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
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 implements DiscordMessageChannel {
|
||||
|
||||
@ -44,29 +35,4 @@ public abstract class DiscordMessageChannelImpl implements DiscordMessageChannel
|
||||
throw new IllegalArgumentException("Unknown MessageChannel type");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("Convert2Lambda") // SneakyThrows
|
||||
protected final <T> CompletableFuture<T> mapExceptions(CompletableFuture<T> future) {
|
||||
return future.handle(new BiFunction<T, Throwable, T>() {
|
||||
|
||||
@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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -25,18 +25,16 @@ 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.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;
|
||||
import com.discordsrv.common.discord.api.message.util.SendableDiscordMessageUtil;
|
||||
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.requests.restaction.MessageAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
@ -44,32 +42,28 @@ import java.util.function.BiFunction;
|
||||
public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements DiscordTextChannel {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final String topic;
|
||||
private final TextChannel textChannel;
|
||||
private final DiscordGuild guild;
|
||||
|
||||
public DiscordTextChannelImpl(DiscordSRV discordSRV, TextChannel textChannel) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.id = textChannel.getIdLong();
|
||||
this.name = textChannel.getName();
|
||||
this.topic = textChannel.getTopic();
|
||||
this.textChannel = textChannel;
|
||||
this.guild = new DiscordGuildImpl(discordSRV, textChannel.getGuild());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
return textChannel.getIdLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
return textChannel.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getTopic() {
|
||||
return topic;
|
||||
public @Nullable String getTopic() {
|
||||
return textChannel.getTopic();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,6 +71,11 @@ public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements
|
||||
return guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextChannel getAsJDATextChannel() {
|
||||
return textChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<ReceivedDiscordMessage> sendMessage(SendableDiscordMessage message) {
|
||||
return message(message, WebhookClient::send, MessageChannel::sendMessage);
|
||||
@ -96,6 +95,11 @@ public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel getAsJDAMessageChannel() {
|
||||
return textChannel;
|
||||
}
|
||||
|
||||
private CompletableFuture<ReceivedDiscordMessage> message(
|
||||
SendableDiscordMessage message,
|
||||
BiFunction<WebhookClient, WebhookMessage, CompletableFuture<ReadonlyMessage>> webhookFunction,
|
||||
@ -107,24 +111,17 @@ public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements
|
||||
client, SendableDiscordMessageUtil.toWebhook(message)))
|
||||
.thenApply(msg -> ReceivedDiscordMessageImpl.fromWebhook(discordSRV, msg));
|
||||
} else {
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
throw new NotReadyException();
|
||||
}
|
||||
|
||||
TextChannel textChannel = jda.getTextChannelById(getId());
|
||||
if (textChannel == null) {
|
||||
future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new UnknownChannelException());
|
||||
return future;
|
||||
}
|
||||
|
||||
future = jdaFunction
|
||||
.apply(textChannel, SendableDiscordMessageUtil.toJDA(message))
|
||||
.submit()
|
||||
.thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg));
|
||||
}
|
||||
|
||||
return mapExceptions(future);
|
||||
return discordSRV.discordAPI().mapExceptions(future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return textChannel.getAsMention();
|
||||
}
|
||||
}
|
||||
|
@ -23,54 +23,89 @@ 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.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
public class DiscordGuildImpl implements DiscordGuild {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final int memberCount;
|
||||
private final Guild guild;
|
||||
|
||||
public DiscordGuildImpl(DiscordSRV discordSRV, Guild guild) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.id = guild.getIdLong();
|
||||
this.name = guild.getName();
|
||||
this.memberCount = guild.getMemberCount();
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
return guild.getIdLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
return guild.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMemberCount() {
|
||||
return memberCount;
|
||||
}
|
||||
|
||||
private Optional<Guild> guild() {
|
||||
return discordSRV.jda()
|
||||
.map(jda -> jda.getGuildById(id));
|
||||
return guild.getMemberCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DiscordGuildMember> getMemberById(long id) {
|
||||
return guild()
|
||||
.map(guild -> guild.getMemberById(id))
|
||||
.map(member -> new DiscordGuildMemberImpl(discordSRV, member));
|
||||
Member member = guild.getMemberById(id);
|
||||
if (member == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new DiscordGuildMemberImpl(discordSRV, member));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<DiscordGuildMember> getCachedMembers() {
|
||||
Set<DiscordGuildMember> members = new HashSet<>();
|
||||
for (Member member : guild.getMembers()) {
|
||||
members.add(new DiscordGuildMemberImpl(discordSRV, member));
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DiscordRole> getRoleById(long id) {
|
||||
return guild()
|
||||
.map(guild -> guild.getRoleById(id))
|
||||
.map(DiscordRoleImpl::new);
|
||||
Role role = guild.getRoleById(id);
|
||||
if (role == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new DiscordRoleImpl(role));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiscordRole> getRoles() {
|
||||
List<DiscordRole> roles = new ArrayList<>();
|
||||
for (Role role : guild.getRoles()) {
|
||||
roles.add(new DiscordRoleImpl(role));
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Guild getAsJDAGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DiscordGuildImpl that = (DiscordGuildImpl) o;
|
||||
return getId() == that.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getId());
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,10 @@ import com.discordsrv.api.discord.api.entity.guild.DiscordRole;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.discord.api.user.DiscordUserImpl;
|
||||
import com.discordsrv.common.discord.api.DiscordUserImpl;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
@ -39,15 +40,15 @@ import java.util.Optional;
|
||||
|
||||
public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGuildMember {
|
||||
|
||||
private final Member member;
|
||||
private final DiscordGuild guild;
|
||||
private final String nickname;
|
||||
private final List<DiscordRole> roles;
|
||||
private final Color color;
|
||||
|
||||
public DiscordGuildMemberImpl(DiscordSRV discordSRV, Member member) {
|
||||
super(member.getUser());
|
||||
super(discordSRV, member.getUser());
|
||||
this.member = member;
|
||||
this.guild = new DiscordGuildImpl(discordSRV, member.getGuild());
|
||||
this.nickname = member.getNickname();
|
||||
|
||||
List<DiscordRole> roles = new ArrayList<>();
|
||||
for (Role role : member.getRoles()) {
|
||||
@ -64,7 +65,7 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<String> getNickname() {
|
||||
return Optional.ofNullable(nickname);
|
||||
return Optional.ofNullable(member.getNickname());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,6 +78,20 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getAsJDAMember() {
|
||||
return member;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getAsJDAUser() {
|
||||
return member.getUser();
|
||||
}
|
||||
|
||||
//
|
||||
// Placeholders
|
||||
//
|
||||
|
||||
@Placeholder(value = "user_highest_role", relookup = "role")
|
||||
public DiscordRole _highestRole() {
|
||||
return !roles.isEmpty() ? roles.get(0) : null;
|
||||
@ -92,14 +107,8 @@ public class DiscordGuildMemberImpl extends DiscordUserImpl implements DiscordGu
|
||||
return null;
|
||||
}
|
||||
|
||||
@Placeholder(value = "user_roles")
|
||||
@Placeholder("user_roles_")
|
||||
public Component _allRoles(@PlaceholderRemainder String suffix) {
|
||||
if (suffix.startsWith("_")) {
|
||||
suffix = suffix.substring(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (DiscordRole role : getRoles()) {
|
||||
components.add(Component.text(role.getName()).color(TextColor.color(role.getColor().rgb())));
|
||||
|
@ -25,26 +25,22 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DiscordRoleImpl implements DiscordRole {
|
||||
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final Role role;
|
||||
private final Color color;
|
||||
private final boolean hoisted;
|
||||
|
||||
public DiscordRoleImpl(Role role) {
|
||||
this.id = role.getIdLong();
|
||||
this.name = role.getName();
|
||||
this.role = role;
|
||||
this.color = new Color(role.getColorRaw());
|
||||
this.hoisted = role.isHoisted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
return role.getIdLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
return role.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -54,6 +50,16 @@ public class DiscordRoleImpl implements DiscordRole {
|
||||
|
||||
@Override
|
||||
public boolean isHoisted() {
|
||||
return hoisted;
|
||||
return role.isHoisted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Role getAsJDARole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return role.getAsMention();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common.discord.api.message;
|
||||
|
||||
import club.minnced.discord.webhook.WebhookClient;
|
||||
import club.minnced.discord.webhook.receive.ReadonlyAttachment;
|
||||
import club.minnced.discord.webhook.receive.ReadonlyEmbed;
|
||||
import club.minnced.discord.webhook.receive.ReadonlyMessage;
|
||||
import club.minnced.discord.webhook.receive.ReadonlyUser;
|
||||
@ -33,15 +34,20 @@ import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
|
||||
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.message.impl.SendableDiscordMessageImpl;
|
||||
import com.discordsrv.api.discord.api.exception.UnknownChannelException;
|
||||
import com.discordsrv.api.discord.api.exception.RestErrorResponseException;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
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.user.DiscordUserImpl;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.requests.ErrorResponse;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -63,7 +69,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
String webhookAvatarUrl = webhookMessage ? message.getAuthor().getEffectiveAvatarUrl() : null;
|
||||
|
||||
DiscordMessageChannel channel = DiscordMessageChannelImpl.get(discordSRV, message.getChannel());
|
||||
DiscordUser user = new DiscordUserImpl(message.getAuthor());
|
||||
DiscordUser user = new DiscordUserImpl(discordSRV, message.getAuthor());
|
||||
|
||||
Member member = message.getMember();
|
||||
DiscordGuildMember apiMember = member != null ? new DiscordGuildMemberImpl(discordSRV, member) : null;
|
||||
@ -82,8 +88,14 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
self = user.isSelf();
|
||||
}
|
||||
|
||||
List<Attachment> attachments = new ArrayList<>();
|
||||
for (Message.Attachment attachment : message.getAttachments()) {
|
||||
attachments.add(new Attachment(attachment.getFileName(), attachment.getUrl()));
|
||||
}
|
||||
|
||||
return new ReceivedDiscordMessageImpl(
|
||||
discordSRV,
|
||||
attachments,
|
||||
self,
|
||||
channel,
|
||||
apiMember,
|
||||
@ -142,10 +154,16 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
webhookMessage.getAuthor().getId()).orElse(null);
|
||||
DiscordGuildMember member = channel instanceof DiscordTextChannel && user != null
|
||||
? ((DiscordTextChannel) channel).getGuild().getMemberById(user.getId()).orElse(null) : null;
|
||||
|
||||
List<Attachment> attachments = new ArrayList<>();
|
||||
for (ReadonlyAttachment attachment : webhookMessage.getAttachments()) {
|
||||
attachments.add(new Attachment(attachment.getFileName(), attachment.getUrl()));
|
||||
}
|
||||
|
||||
return new ReceivedDiscordMessageImpl(
|
||||
discordSRV,
|
||||
// These are always from rest responses
|
||||
true,
|
||||
attachments,
|
||||
true, // These are always from rest responses
|
||||
channel,
|
||||
member,
|
||||
user,
|
||||
@ -159,6 +177,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
}
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final List<Attachment> attachments;
|
||||
private final boolean fromSelf;
|
||||
private final DiscordMessageChannel channel;
|
||||
private final DiscordGuildMember member;
|
||||
@ -168,6 +187,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
|
||||
private ReceivedDiscordMessageImpl(
|
||||
DiscordSRV discordSRV,
|
||||
List<Attachment> attachments,
|
||||
boolean fromSelf,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordGuildMember member,
|
||||
@ -181,6 +201,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
) {
|
||||
super(content, embeds, Collections.emptySet(), webhookUsername, webhookAvatarUrl);
|
||||
this.discordSRV = discordSRV;
|
||||
this.attachments = attachments;
|
||||
this.fromSelf = fromSelf;
|
||||
this.channel = channel;
|
||||
this.member = member;
|
||||
@ -194,6 +215,11 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> getAttachments() {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFromSelf() {
|
||||
return fromSelf;
|
||||
@ -233,7 +259,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null);
|
||||
if (textChannel == null) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new UnknownChannelException());
|
||||
future.completeExceptionally(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
|
||||
return future;
|
||||
}
|
||||
|
||||
@ -249,10 +275,30 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
|
||||
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null);
|
||||
if (textChannel == null) {
|
||||
CompletableFuture<ReceivedDiscordMessage> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new UnknownChannelException());
|
||||
future.completeExceptionally(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
|
||||
return future;
|
||||
}
|
||||
|
||||
return textChannel.editMessageById(getId(), message);
|
||||
}
|
||||
|
||||
//
|
||||
// Placeholders
|
||||
//
|
||||
|
||||
@Placeholder("message_attachments")
|
||||
public Component _attachments() {
|
||||
// TODO: customizable
|
||||
|
||||
TextComponent.Builder builder = Component.text();
|
||||
for (Attachment attachment : attachments) {
|
||||
builder.append(
|
||||
Component.text()
|
||||
.content("[" + attachment.fileName() + "]")
|
||||
.clickEvent(ClickEvent.openUrl(attachment.url()))
|
||||
)
|
||||
.append(Component.text(" "));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import com.discordsrv.common.discord.api.guild.DiscordGuildImpl;
|
||||
import com.discordsrv.common.discord.api.guild.DiscordGuildMemberImpl;
|
||||
import com.discordsrv.common.discord.api.guild.DiscordRoleImpl;
|
||||
import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl;
|
||||
import com.discordsrv.common.discord.api.user.DiscordUserImpl;
|
||||
import com.discordsrv.common.discord.api.DiscordUserImpl;
|
||||
import com.discordsrv.common.discord.connection.DiscordConnectionManager;
|
||||
import com.discordsrv.common.scheduler.Scheduler;
|
||||
import com.discordsrv.common.scheduler.threadfactory.CountingThreadFactory;
|
||||
@ -156,7 +156,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
|
||||
CompletableFuture<DiscordUser> future = instance.retrieveApplicationInfo()
|
||||
.timeout(10, TimeUnit.SECONDS)
|
||||
.map(applicationInfo -> (DiscordUser) new DiscordUserImpl(applicationInfo.getOwner()))
|
||||
.map(applicationInfo -> (DiscordUser) new DiscordUserImpl(discordSRV, applicationInfo.getOwner()))
|
||||
.submit();
|
||||
|
||||
botOwnerRequest.set(future);
|
||||
@ -188,7 +188,7 @@ public class JDAConnectionManager implements DiscordConnectionManager {
|
||||
} else if (o instanceof ReceivedMessage) {
|
||||
converted = ReceivedDiscordMessageImpl.fromJDA(discordSRV, (Message) o);
|
||||
} else if (o instanceof User) {
|
||||
converted = new DiscordUserImpl((User) o);
|
||||
converted = new DiscordUserImpl(discordSRV, (User) o);
|
||||
} else {
|
||||
converted = o;
|
||||
isConversion = false;
|
||||
|
@ -16,22 +16,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.listener;
|
||||
package com.discordsrv.common.event.util;
|
||||
|
||||
import com.discordsrv.api.event.bus.EventListener;
|
||||
import com.discordsrv.api.event.events.Cancellable;
|
||||
import com.discordsrv.api.event.events.Processable;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
|
||||
public abstract class AbstractListener {
|
||||
public final class EventUtil {
|
||||
|
||||
protected final DiscordSRV discordSRV;
|
||||
private EventUtil() {}
|
||||
|
||||
public AbstractListener(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
public boolean checkProcessor(Processable event) {
|
||||
public static boolean checkProcessor(DiscordSRV discordSRV,Processable event) {
|
||||
if (!event.isProcessed()) {
|
||||
return false;
|
||||
}
|
||||
@ -45,7 +41,7 @@ public abstract class AbstractListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkCancellation(Cancellable event) {
|
||||
public static boolean checkCancellation(DiscordSRV discordSRV, Cancellable event) {
|
||||
if (!event.isCancelled()) {
|
||||
return false;
|
||||
}
|
@ -1,107 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.listener;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.message.forward.game.ChatMessageForwardedEvent;
|
||||
import com.discordsrv.api.event.events.message.receive.game.ChatMessageProcessingEvent;
|
||||
import com.discordsrv.api.placeholder.util.Placeholders;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig;
|
||||
import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageClusterImpl;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GameChatListener extends AbstractListener {
|
||||
|
||||
public GameChatListener(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.LAST)
|
||||
public void onChatReceive(ChatMessageProcessingEvent event) {
|
||||
if (checkProcessor(event) || checkCancellation(event) || !discordSRV.isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameChannel gameChannel = event.getGameChannel();
|
||||
|
||||
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
||||
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
||||
|
||||
SendableDiscordMessage.Builder builder = chatConfig.get(cfg -> cfg.format);
|
||||
if (builder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Component message = ComponentUtil.fromAPI(event.message());
|
||||
Placeholders serializedMessage = new Placeholders(discordSRV.componentFactory().discordSerializer().serialize(message));
|
||||
|
||||
chatConfig.opt(cfg -> cfg.contentRegexFilters)
|
||||
.ifPresent(patterns -> patterns.forEach(serializedMessage::replaceAll));
|
||||
|
||||
SendableDiscordMessage.Formatter formatter = builder.toFormatter()
|
||||
.addContext(event.getPlayer(), gameChannel)
|
||||
.addReplacement("%message%", serializedMessage.toString());
|
||||
|
||||
formatter.applyPlaceholderService();
|
||||
|
||||
SendableDiscordMessage discordMessage = formatter.build();
|
||||
|
||||
List<Long> channelIds = channelConfig.get(cfg -> cfg instanceof ChannelConfig ? ((ChannelConfig) cfg).channelIds : null);
|
||||
if (channelIds == null || channelIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CompletableFuture<ReceivedDiscordMessage>> futures = new ArrayList<>();
|
||||
for (Long channelId : channelIds) {
|
||||
discordSRV.discordAPI().getTextChannelById(channelId).ifPresent(textChannel ->
|
||||
futures.add(textChannel.sendMessage(discordMessage)));
|
||||
}
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.whenComplete((v, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to deliver message to Discord", t);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ReceivedDiscordMessage> messages = new ArrayList<>();
|
||||
for (CompletableFuture<ReceivedDiscordMessage> future : futures) {
|
||||
messages.add(future.join());
|
||||
}
|
||||
|
||||
discordSRV.eventBus().publish(
|
||||
new ChatMessageForwardedEvent(
|
||||
new ReceivedDiscordMessageClusterImpl(messages)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.module;
|
||||
|
||||
import com.discordsrv.api.event.events.Cancellable;
|
||||
import com.discordsrv.api.event.events.Processable;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.event.util.EventUtil;
|
||||
|
||||
public abstract class Module {
|
||||
|
||||
protected final DiscordSRV discordSRV;
|
||||
private boolean hasBeenEnabled = false;
|
||||
|
||||
public Module(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
protected boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void enable() {}
|
||||
protected void disable() {}
|
||||
protected void reload() {}
|
||||
|
||||
public final void enableModule() {
|
||||
if (hasBeenEnabled || !isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasBeenEnabled = true;
|
||||
enable();
|
||||
|
||||
try {
|
||||
discordSRV.eventBus().subscribe(this);
|
||||
// Ignore not having listener methods exception
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
|
||||
// Utility
|
||||
protected final boolean checkProcessor(Processable event) {
|
||||
return EventUtil.checkProcessor(discordSRV, event);
|
||||
}
|
||||
|
||||
protected final boolean checkCancellation(Cancellable event) {
|
||||
return EventUtil.checkCancellation(discordSRV, event);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.module;
|
||||
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
|
||||
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class ModuleManager {
|
||||
|
||||
private final Set<Module> modules = new CopyOnWriteArraySet<>();
|
||||
private final Map<String, Module> moduleLookupTable = new ConcurrentHashMap<>();
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public ModuleManager(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Module> T getModule(Class<T> moduleType) {
|
||||
return (T) moduleLookupTable.computeIfAbsent(moduleType.getName(), key -> {
|
||||
for (Module module : modules) {
|
||||
if (moduleType.isAssignableFrom(module.getClass())) {
|
||||
return module;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void register(Module module) {
|
||||
this.modules.add(module);
|
||||
this.moduleLookupTable.put(module.getClass().getName(), module);
|
||||
|
||||
enable(module);
|
||||
}
|
||||
|
||||
private void enable(Module module) {
|
||||
try {
|
||||
module.enableModule();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to enable " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Module module) {
|
||||
this.modules.remove(module);
|
||||
this.moduleLookupTable.values().removeIf(mod -> mod == module);
|
||||
|
||||
disable(module);
|
||||
}
|
||||
|
||||
private void disable(Module module) {
|
||||
try {
|
||||
module.disable();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to disable " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.EARLY)
|
||||
public void onShuttingDown(DiscordSRVShuttingDownEvent event) {
|
||||
for (Module module : modules) {
|
||||
unregister(module);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.EARLY)
|
||||
public void onReload(DiscordSRVReloadEvent event) {
|
||||
for (Module module : modules) {
|
||||
// Check if the module needs to be enabled due to reload
|
||||
enable(module);
|
||||
|
||||
try {
|
||||
module.reload();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error("Failed to reload " + module.getClass().getSimpleName(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,27 +16,27 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.listener;
|
||||
package com.discordsrv.common.module.modules;
|
||||
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.discord.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.module.Module;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
|
||||
public class DiscordAPIListener {
|
||||
public class DiscordAPIEventModule extends Module {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public DiscordAPIListener(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
public DiscordAPIEventModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMessageReceivedEvent(MessageReceivedEvent event) {
|
||||
discordSRV.eventBus().publish(new DiscordMessageReceivedEvent(
|
||||
ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()),
|
||||
DiscordMessageChannelImpl.get(discordSRV, event.getChannel())));
|
||||
DiscordMessageChannelImpl.get(discordSRV, event.getChannel()))
|
||||
);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.listener;
|
||||
package com.discordsrv.common.module.modules;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.component.EnhancedTextBuilder;
|
||||
@ -35,14 +35,15 @@ import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.module.Module;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class DiscordChatListener extends AbstractListener {
|
||||
public class DiscordToMinecraftModule extends Module {
|
||||
|
||||
public DiscordChatListener(DiscordSRV discordSRV) {
|
||||
public DiscordToMinecraftModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@ -53,10 +54,7 @@ public class DiscordChatListener extends AbstractListener {
|
||||
return;
|
||||
}
|
||||
|
||||
discordSRV.eventBus().publish(
|
||||
new DiscordMessageProcessingEvent(
|
||||
event.getMessage(),
|
||||
channel));
|
||||
discordSRV.eventBus().publish(new DiscordMessageProcessingEvent(event.getMessage(), channel));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -113,7 +111,7 @@ public class DiscordChatListener extends AbstractListener {
|
||||
chatConfig.opt(cfg -> cfg.contentRegexFilters)
|
||||
.ifPresent(filters -> filters.forEach(message::replaceAll));
|
||||
|
||||
Component messageComponent = DiscordSRVMinecraftRenderer.getWithGuildContext(channel.getGuild().getId(), () ->
|
||||
Component messageComponent = DiscordSRVMinecraftRenderer.getWithContext(event, chatConfig, () ->
|
||||
discordSRV.componentFactory().minecraftSerializer().serialize(message.toString()));
|
||||
|
||||
EnhancedTextBuilder componentBuilder = discordSRV.componentFactory()
|
@ -16,25 +16,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.listener;
|
||||
package com.discordsrv.common.module.modules;
|
||||
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.channel.GameChannelLookupEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
||||
import com.discordsrv.common.event.util.EventUtil;
|
||||
import com.discordsrv.common.module.Module;
|
||||
|
||||
public class ChannelLookupListener extends AbstractListener {
|
||||
public class GlobalChannelLookupModule extends Module {
|
||||
|
||||
public ChannelLookupListener(DiscordSRV discordSRV) {
|
||||
private final DefaultGlobalChannel defaultGlobalChannel;
|
||||
|
||||
public GlobalChannelLookupModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
defaultGlobalChannel = new DefaultGlobalChannel(discordSRV);
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.LAST)
|
||||
@Subscribe(priority = EventPriority.LATE)
|
||||
public void onGameChannelLookup(GameChannelLookupEvent event) {
|
||||
if (!event.getChannelName().equalsIgnoreCase("global") || checkProcessor(event)) {
|
||||
if (EventUtil.checkProcessor(discordSRV, event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.process(discordSRV.defaultGlobalChannel());
|
||||
if (event.getChannelName().equalsIgnoreCase("global")) {
|
||||
event.process(defaultGlobalChannel);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.module.modules;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
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.api.discord.api.util.DiscordFormattingUtil;
|
||||
import com.discordsrv.api.event.bus.EventPriority;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.message.forward.game.ChatMessageForwardedEvent;
|
||||
import com.discordsrv.api.event.events.message.receive.game.ChatMessageProcessingEvent;
|
||||
import com.discordsrv.api.placeholder.FormattedText;
|
||||
import com.discordsrv.api.placeholder.util.Placeholders;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig;
|
||||
import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageClusterImpl;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.module.Module;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.GuildChannel;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.events.channel.ChannelCreateEvent;
|
||||
import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.channel.update.ChannelUpdateNameEvent;
|
||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
|
||||
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent;
|
||||
import net.dv8tion.jda.api.events.role.RoleCreateEvent;
|
||||
import net.dv8tion.jda.api.events.role.RoleDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.role.update.RoleUpdateNameEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MinecraftToDiscordModule extends Module {
|
||||
|
||||
private final Map<Long, Map<Long, CachedMention>> memberMentions = new ConcurrentHashMap<>();
|
||||
private final Map<Long, Map<Long, CachedMention>> roleMentions = new ConcurrentHashMap<>();
|
||||
private final Map<Long, Map<Long, CachedMention>> channelMentions = new ConcurrentHashMap<>();
|
||||
|
||||
public MinecraftToDiscordModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
}
|
||||
|
||||
@Subscribe(priority = EventPriority.LAST)
|
||||
public void onChatReceive(ChatMessageProcessingEvent event) {
|
||||
if (checkProcessor(event) || checkCancellation(event) || !discordSRV.isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameChannel gameChannel = event.getGameChannel();
|
||||
|
||||
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
||||
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
||||
|
||||
SendableDiscordMessage.Builder builder = chatConfig.get(cfg -> cfg.format);
|
||||
if (builder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Long> channelIds = channelConfig.get(cfg -> cfg instanceof ChannelConfig ? ((ChannelConfig) cfg).channelIds : null);
|
||||
if (channelIds == null || channelIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Component message = ComponentUtil.fromAPI(event.message());
|
||||
Placeholders messagePlaceholders = new Placeholders(discordSRV.componentFactory().discordSerializer().serialize(message));
|
||||
|
||||
chatConfig.opt(cfg -> cfg.contentRegexFilters)
|
||||
.ifPresent(patterns -> patterns.forEach(messagePlaceholders::replaceAll));
|
||||
|
||||
Map<DiscordGuild, Set<DiscordTextChannel>> channels = new LinkedHashMap<>();
|
||||
for (Long channelId : channelIds) {
|
||||
discordSRV.discordAPI().getTextChannelById(channelId)
|
||||
.ifPresent(textChannel -> channels
|
||||
.computeIfAbsent(textChannel.getGuild(), key -> new LinkedHashSet<>())
|
||||
.add(textChannel));
|
||||
}
|
||||
|
||||
String serializedMessage = DiscordFormattingUtil.escapeContent(messagePlaceholders.toString());
|
||||
List<CompletableFuture<ReceivedDiscordMessage>> futures = new ArrayList<>();
|
||||
|
||||
OrDefault<MinecraftToDiscordChatConfig.Mentions> mentionConfig = chatConfig.map(cfg -> cfg.mentions);
|
||||
// Format messages per-Guild
|
||||
for (Map.Entry<DiscordGuild, Set<DiscordTextChannel>> entry : channels.entrySet()) {
|
||||
Guild guild = entry.getKey().getAsJDAGuild();
|
||||
|
||||
Placeholders channelMessagePlaceholders = new Placeholders(serializedMessage);
|
||||
List<CachedMention> mentions = new ArrayList<>();
|
||||
if (mentionConfig.get(cfg -> cfg.roles, false)) {
|
||||
mentions.addAll(getRoleMentions(guild).values());
|
||||
}
|
||||
if (mentionConfig.get(cfg -> cfg.users, false)) {
|
||||
mentions.addAll(getMemberMentions(guild).values());
|
||||
}
|
||||
if (mentionConfig.get(cfg -> cfg.roles, true)) {
|
||||
mentions.addAll(getChannelMentions(guild).values());
|
||||
}
|
||||
|
||||
// From longest to shortest
|
||||
mentions.stream()
|
||||
.sorted(Comparator.comparingInt(mention -> ((CachedMention) mention).searchLength).reversed())
|
||||
.forEachOrdered(mention -> channelMessagePlaceholders.replaceAll(mention.search, mention.mention));
|
||||
|
||||
SendableDiscordMessage discordMessage = builder.toFormatter()
|
||||
.addContext(event.getPlayer(), gameChannel)
|
||||
.addReplacement("%message%", new FormattedText(channelMessagePlaceholders.toString()))
|
||||
.applyPlaceholderService()
|
||||
.build();
|
||||
|
||||
for (DiscordTextChannel textChannel : entry.getValue()) {
|
||||
futures.add(textChannel.sendMessage(discordMessage));
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.whenComplete((v, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to deliver message to Discord", t);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ReceivedDiscordMessage> messages = new ArrayList<>();
|
||||
for (CompletableFuture<ReceivedDiscordMessage> future : futures) {
|
||||
messages.add(future.join());
|
||||
}
|
||||
|
||||
discordSRV.eventBus().publish(
|
||||
new ChatMessageForwardedEvent(
|
||||
new ReceivedDiscordMessageClusterImpl(messages)));
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Mention caching
|
||||
//
|
||||
|
||||
private Map<Long, CachedMention> getRoleMentions(Guild guild) {
|
||||
return roleMentions.computeIfAbsent(guild.getIdLong(), key -> {
|
||||
Map<Long, CachedMention> mentions = new LinkedHashMap<>();
|
||||
for (Role role : guild.getRoles()) {
|
||||
mentions.put(role.getIdLong(), convertRole(role));
|
||||
}
|
||||
return mentions;
|
||||
});
|
||||
}
|
||||
|
||||
private CachedMention convertRole(Role role) {
|
||||
return new CachedMention(
|
||||
"@" + role.getName(),
|
||||
role.getAsMention(),
|
||||
role.getIdLong()
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onRoleCreate(RoleCreateEvent event) {
|
||||
Role role = event.getRole();
|
||||
getRoleMentions(event.getGuild()).put(role.getIdLong(), convertRole(role));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onRoleUpdate(RoleUpdateNameEvent event) {
|
||||
Role role = event.getRole();
|
||||
getRoleMentions(event.getGuild()).put(role.getIdLong(), convertRole(role));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onRoleDelete(RoleDeleteEvent event) {
|
||||
Role role = event.getRole();
|
||||
getRoleMentions(event.getGuild()).remove(role.getIdLong());
|
||||
}
|
||||
|
||||
private Map<Long, CachedMention> getMemberMentions(Guild guild) {
|
||||
return channelMentions.computeIfAbsent(guild.getIdLong(), key -> {
|
||||
Map<Long, CachedMention> mentions = new LinkedHashMap<>();
|
||||
for (Member member : guild.getMembers()) {
|
||||
mentions.put(member.getIdLong(), convertMember(member));
|
||||
}
|
||||
return mentions;
|
||||
});
|
||||
}
|
||||
|
||||
private CachedMention convertMember(Member member) {
|
||||
return new CachedMention(
|
||||
"@" + member.getEffectiveName(),
|
||||
member.getAsMention(),
|
||||
member.getIdLong()
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMemberAdd(GuildMemberJoinEvent event) {
|
||||
Member member = event.getMember();
|
||||
getMemberMentions(event.getGuild()).put(member.getIdLong(), convertMember(member));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMemberUpdate(GuildMemberUpdateNicknameEvent event) {
|
||||
Member member = event.getMember();
|
||||
getMemberMentions(event.getGuild()).put(member.getIdLong(), convertMember(member));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMemberDelete(GuildMemberRemoveEvent event) {
|
||||
Member member = event.getMember();
|
||||
if (member == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getMemberMentions(event.getGuild()).remove(member.getIdLong());
|
||||
}
|
||||
|
||||
private Map<Long, CachedMention> getChannelMentions(Guild guild) {
|
||||
return memberMentions.computeIfAbsent(guild.getIdLong(), key -> {
|
||||
Map<Long, CachedMention> mentions = new LinkedHashMap<>();
|
||||
for (GuildChannel channel : guild.getChannels()) {
|
||||
mentions.put(channel.getIdLong(), convertChannel(channel));
|
||||
}
|
||||
return mentions;
|
||||
});
|
||||
}
|
||||
|
||||
private CachedMention convertChannel(GuildChannel channel) {
|
||||
return new CachedMention(
|
||||
"#" + channel.getName(),
|
||||
channel.getAsMention(),
|
||||
channel.getIdLong()
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChannelCreate(ChannelCreateEvent event) {
|
||||
if (!event.getChannelType().isGuild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GuildChannel channel = (GuildChannel) event.getChannel();
|
||||
getMemberMentions(event.getGuild()).put(channel.getIdLong(), convertChannel(channel));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChannelUpdate(ChannelUpdateNameEvent event) {
|
||||
if (!event.getChannelType().isGuild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GuildChannel channel = (GuildChannel) event.getChannel();
|
||||
getMemberMentions(event.getGuild()).put(channel.getIdLong(), convertChannel(channel));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChannelDelete(ChannelDeleteEvent event) {
|
||||
if (!event.getChannelType().isGuild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GuildChannel channel = (GuildChannel) event.getChannel();
|
||||
getMemberMentions(event.getGuild()).remove(channel.getIdLong());
|
||||
}
|
||||
|
||||
public static class CachedMention {
|
||||
|
||||
private final Pattern search;
|
||||
private final int searchLength;
|
||||
private final String mention;
|
||||
private final long id;
|
||||
|
||||
public CachedMention(String search, String mention, long id) {
|
||||
this.search = Pattern.compile(search, Pattern.LITERAL);
|
||||
this.searchLength = search.length();
|
||||
this.mention = mention;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CachedMention that = (CachedMention) o;
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ public class StandardScheduler implements Scheduler {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
discordSRV.logger().error(Thread.currentThread().getName() + " caught a exception", t);
|
||||
discordSRV.logger().error(Thread.currentThread().getName() + " ran into an exception", t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user