mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-12-24 16:58:44 +01:00
Add PlaceholderPrefix
This commit is contained in:
parent
f0c5c7f1af
commit
5652a7d544
@ -23,11 +23,15 @@
|
||||
|
||||
package com.discordsrv.api.color;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Simple helper class for handling rgb colors, that is compatible with headless java installations.
|
||||
*/
|
||||
@PlaceholderPrefix("color_")
|
||||
public class Color {
|
||||
|
||||
/**
|
||||
@ -69,22 +73,27 @@ public class Color {
|
||||
this.rgb = Integer.parseInt(hex, 16);
|
||||
}
|
||||
|
||||
@Placeholder("rgb")
|
||||
public int rgb() {
|
||||
return rgb;
|
||||
}
|
||||
|
||||
@Placeholder("hex")
|
||||
public String hex() {
|
||||
return Integer.toHexString(0xF000000 | rgb).substring(1);
|
||||
}
|
||||
|
||||
@Placeholder("red")
|
||||
public int red() {
|
||||
return (rgb & 0xFF0000) >> 16;
|
||||
}
|
||||
|
||||
@Placeholder("green")
|
||||
public int green() {
|
||||
return (rgb & 0x00FF00) >> 8;
|
||||
}
|
||||
|
||||
@Placeholder("blue")
|
||||
public int blue() {
|
||||
return rgb & 0x0000FF;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ package com.discordsrv.api.discord.entity;
|
||||
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordDMChannel;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -34,6 +35,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
/**
|
||||
* A Discord user.
|
||||
*/
|
||||
@PlaceholderPrefix("user_")
|
||||
public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
|
||||
/**
|
||||
@ -52,7 +54,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* Gets the username of the Discord user.
|
||||
* @return the user's username
|
||||
*/
|
||||
@Placeholder("user_name")
|
||||
@Placeholder("name")
|
||||
@NotNull
|
||||
String getUsername();
|
||||
|
||||
@ -60,7 +62,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* Gets the effective display name of the Discord user.
|
||||
* @return the user's effective display name
|
||||
*/
|
||||
@Placeholder("user_effective_name")
|
||||
@Placeholder("effective_name")
|
||||
@NotNull
|
||||
String getEffectiveName();
|
||||
|
||||
@ -68,7 +70,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* Gets the Discord user's discriminator.
|
||||
* @return the user's discriminator
|
||||
*/
|
||||
@Placeholder("user_discriminator")
|
||||
@Placeholder("discriminator")
|
||||
@NotNull
|
||||
String getDiscriminator();
|
||||
|
||||
@ -76,7 +78,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* Gets the Discord user's avatar url, if an avatar is set.
|
||||
* @return the user's avatar url or {@code null}
|
||||
*/
|
||||
@Placeholder("user_avatar_url")
|
||||
@Placeholder("avatar_url")
|
||||
@Nullable
|
||||
String getAvatarUrl();
|
||||
|
||||
@ -85,7 +87,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* if an avatar isn't set it'll be the url to the default avatar provided by Discord.
|
||||
* @return the user's avatar url
|
||||
*/
|
||||
@Placeholder("user_effective_avatar_url")
|
||||
@Placeholder("effective_avatar_url")
|
||||
@NotNull
|
||||
String getEffectiveAvatarUrl();
|
||||
|
||||
@ -93,7 +95,7 @@ public interface DiscordUser extends JDAEntity<User>, Snowflake, Mentionable {
|
||||
* Gets the Discord user's username, including discriminator if any.
|
||||
* @return the Discord user's username
|
||||
*/
|
||||
@Placeholder("user_tag")
|
||||
@Placeholder("tag")
|
||||
default String getAsTag() {
|
||||
String username = getUsername();
|
||||
String discriminator = getDiscriminator();
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
package com.discordsrv.api.discord.entity;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
|
||||
/**
|
||||
* A snowflake identifier.
|
||||
*/
|
||||
@ -32,5 +34,6 @@ public interface Snowflake {
|
||||
* Gets the id of this entity.
|
||||
* @return the id of this entity
|
||||
*/
|
||||
@Placeholder("id")
|
||||
long getId();
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package com.discordsrv.api.discord.entity.channel;
|
||||
|
||||
import com.discordsrv.api.discord.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
|
||||
@PlaceholderPrefix("channel_")
|
||||
public interface DiscordChannel extends Snowflake {
|
||||
|
||||
/**
|
||||
|
@ -26,8 +26,10 @@ package com.discordsrv.api.discord.entity.channel;
|
||||
import com.discordsrv.api.discord.entity.Snowflake;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@PlaceholderPrefix("channel_")
|
||||
public interface DiscordGuildChannel extends Snowflake {
|
||||
|
||||
/**
|
||||
@ -35,13 +37,14 @@ public interface DiscordGuildChannel extends Snowflake {
|
||||
* @return the name of the channel
|
||||
*/
|
||||
@NotNull
|
||||
@Placeholder("channel_name")
|
||||
@Placeholder("name")
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets the Discord server that this channel is in.
|
||||
* @return the Discord server that contains this channel
|
||||
*/
|
||||
@Placeholder(value = "server", relookup = "server")
|
||||
@NotNull
|
||||
DiscordGuild getGuild();
|
||||
|
||||
@ -50,6 +53,6 @@ public interface DiscordGuildChannel extends Snowflake {
|
||||
* @return the https url to go to this channel
|
||||
*/
|
||||
@NotNull
|
||||
@Placeholder("channel_jump_url")
|
||||
@Placeholder("jump_url")
|
||||
String getJumpUrl();
|
||||
}
|
||||
|
@ -25,8 +25,10 @@ package com.discordsrv.api.discord.entity.guild;
|
||||
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import com.discordsrv.api.discord.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import net.dv8tion.jda.api.entities.emoji.CustomEmoji;
|
||||
|
||||
@PlaceholderPrefix("emoji_")
|
||||
public interface DiscordCustomEmoji extends JDAEntity<CustomEmoji>, Snowflake {
|
||||
|
||||
String getName();
|
||||
|
@ -26,6 +26,7 @@ package com.discordsrv.api.discord.entity.guild;
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import com.discordsrv.api.discord.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -37,13 +38,14 @@ import java.util.concurrent.CompletableFuture;
|
||||
/**
|
||||
* A Discord server.
|
||||
*/
|
||||
@PlaceholderPrefix("server_")
|
||||
public interface DiscordGuild extends JDAEntity<Guild>, Snowflake {
|
||||
|
||||
/**
|
||||
* Gets the name of this Discord guild.
|
||||
* @return the guild's name
|
||||
*/
|
||||
@Placeholder("server_name")
|
||||
@Placeholder("name")
|
||||
@NotNull
|
||||
String getName();
|
||||
|
||||
@ -51,7 +53,7 @@ public interface DiscordGuild extends JDAEntity<Guild>, Snowflake {
|
||||
* Gets the member count of the guild.
|
||||
* @return the guild's member count
|
||||
*/
|
||||
@Placeholder("server_member_count")
|
||||
@Placeholder("member_count")
|
||||
int getMemberCount();
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import com.discordsrv.api.discord.entity.Mentionable;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -39,6 +40,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
/**
|
||||
* A Discord server member.
|
||||
*/
|
||||
@PlaceholderPrefix("user_")
|
||||
public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
|
||||
/**
|
||||
@ -52,6 +54,7 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* Gets the Discord server this member is from.
|
||||
* @return the Discord server this member is from.
|
||||
*/
|
||||
@Placeholder(value = "server", relookup = "server")
|
||||
@NotNull
|
||||
DiscordGuild getGuild();
|
||||
|
||||
@ -101,7 +104,7 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* Gets the effective name of this Discord server member.
|
||||
* @return the Discord server member's effective name
|
||||
*/
|
||||
@Placeholder("user_effective_server_name")
|
||||
@Placeholder("effective_server_name")
|
||||
@NotNull
|
||||
default String getEffectiveServerName() {
|
||||
String nickname = getNickname();
|
||||
@ -112,7 +115,7 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* Gets the avatar url that is active for this user in this server.
|
||||
* @return the user's avatar url in this server
|
||||
*/
|
||||
@Placeholder("user_effective_server_avatar_url")
|
||||
@Placeholder("effective_server_avatar_url")
|
||||
@NotNull
|
||||
String getEffectiveServerAvatarUrl();
|
||||
|
||||
@ -120,13 +123,14 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* Gets the color of this user's highest role that has a color.
|
||||
* @return the color that will be used for this user
|
||||
*/
|
||||
@Placeholder("user_color")
|
||||
@Placeholder("color")
|
||||
Color getColor();
|
||||
|
||||
/**
|
||||
* Gets the time the member joined the server.
|
||||
* @return the time the member joined the server
|
||||
*/
|
||||
@Placeholder(value = "time_joined", relookup = "date")
|
||||
@NotNull
|
||||
OffsetDateTime getTimeJoined();
|
||||
|
||||
@ -134,6 +138,7 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* Time the member started boosting.
|
||||
* @return the time the member started boosting or {@code null}
|
||||
*/
|
||||
@Placeholder(value = "time_boosted", relookup = "date")
|
||||
@Nullable
|
||||
OffsetDateTime getTimeBoosted();
|
||||
|
||||
@ -141,7 +146,7 @@ public interface DiscordGuildMember extends JDAEntity<Member>, Mentionable {
|
||||
* If the Discord server member is boosted.
|
||||
* @return {@code true} if this Discord server member is boosting
|
||||
*/
|
||||
@Placeholder("user_isboosting")
|
||||
@Placeholder("isboosting")
|
||||
default boolean isBoosting() {
|
||||
return getTimeBoosted() != null;
|
||||
}
|
||||
|
@ -28,12 +28,14 @@ import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import com.discordsrv.api.discord.entity.Mentionable;
|
||||
import com.discordsrv.api.discord.entity.Snowflake;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A Discord server role.
|
||||
*/
|
||||
@PlaceholderPrefix("role_")
|
||||
public interface DiscordRole extends JDAEntity<Role>, Snowflake, Mentionable {
|
||||
|
||||
/**
|
||||
@ -45,6 +47,7 @@ public interface DiscordRole extends JDAEntity<Role>, Snowflake, Mentionable {
|
||||
* The Discord server this role is from.
|
||||
* @return the Discord server
|
||||
*/
|
||||
@Placeholder(value = "server", relookup = "server")
|
||||
@NotNull
|
||||
DiscordGuild getGuild();
|
||||
|
||||
@ -52,7 +55,7 @@ public interface DiscordRole extends JDAEntity<Role>, Snowflake, Mentionable {
|
||||
* Gets the name of the Discord role.
|
||||
* @return the role name
|
||||
*/
|
||||
@Placeholder("role_name")
|
||||
@Placeholder("name")
|
||||
@NotNull
|
||||
String getName();
|
||||
|
||||
@ -69,7 +72,7 @@ public interface DiscordRole extends JDAEntity<Role>, Snowflake, Mentionable {
|
||||
* @return the color of this role, or {@link #DEFAULT_COLOR} if there is no color set
|
||||
* @see #hasColor()
|
||||
*/
|
||||
@Placeholder("role_color")
|
||||
@Placeholder(value = "color", relookup = "color")
|
||||
@NotNull
|
||||
Color getColor();
|
||||
|
||||
|
@ -31,6 +31,7 @@ import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@ -41,6 +42,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
/**
|
||||
* A message received from Discord.
|
||||
*/
|
||||
@PlaceholderPrefix("message_")
|
||||
public interface ReceivedDiscordMessage extends Snowflake {
|
||||
|
||||
/**
|
||||
@ -49,6 +51,7 @@ public interface ReceivedDiscordMessage extends Snowflake {
|
||||
* @return the message content or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
@Placeholder("content")
|
||||
String getContent();
|
||||
|
||||
/**
|
||||
@ -70,7 +73,7 @@ public interface ReceivedDiscordMessage extends Snowflake {
|
||||
* @return the jump url
|
||||
*/
|
||||
@NotNull
|
||||
@Placeholder("message_jump_url")
|
||||
@Placeholder("jump_url")
|
||||
String getJumpUrl();
|
||||
|
||||
/**
|
||||
@ -92,6 +95,7 @@ public interface ReceivedDiscordMessage extends Snowflake {
|
||||
* @return the user that sent the message
|
||||
*/
|
||||
@NotNull
|
||||
@Placeholder(value = "user", relookup = "user")
|
||||
DiscordUser getAuthor();
|
||||
|
||||
/**
|
||||
@ -99,6 +103,7 @@ public interface ReceivedDiscordMessage extends Snowflake {
|
||||
* @return the channel the message was sent in
|
||||
*/
|
||||
@NotNull
|
||||
@Placeholder(value = "channel", relookup = "channel")
|
||||
DiscordMessageChannel getChannel();
|
||||
|
||||
/**
|
||||
@ -135,6 +140,7 @@ public interface ReceivedDiscordMessage extends Snowflake {
|
||||
* @return an optional potentially containing the Discord server the message was posted in
|
||||
*/
|
||||
@Nullable
|
||||
@Placeholder(value = "server", relookup = "server")
|
||||
default DiscordGuild getGuild() {
|
||||
DiscordTextChannel textChannel = getTextChannel();
|
||||
|
||||
|
@ -44,7 +44,7 @@ public @interface Placeholder {
|
||||
String value();
|
||||
|
||||
/**
|
||||
* Creates a new lookup with {@link #value()} replaced with this.
|
||||
* Creates a new lookup with {@link #value()} replaced with this, if the placeholder is longer than the given {@link #value()}.
|
||||
* The object returned by the {@link Placeholder} method/field will be added as context.
|
||||
* @return the prefix used for the next lookup
|
||||
*/
|
||||
|
@ -0,0 +1,27 @@
|
||||
package com.discordsrv.api.placeholder.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies a prefix for all placeholders declared in this type.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface PlaceholderPrefix {
|
||||
|
||||
/**
|
||||
* The prefix for all placeholders in this type
|
||||
* @return the prefix
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* If this prefix should not follow {@link PlaceholderPrefix} of classes that are using this class/interface as a superclass.
|
||||
* @return {@code true} to not allow overwriting this prefix
|
||||
*/
|
||||
boolean ignoreParents() default false;
|
||||
|
||||
}
|
@ -81,6 +81,7 @@ import com.discordsrv.common.storage.Storage;
|
||||
import com.discordsrv.common.storage.StorageType;
|
||||
import com.discordsrv.common.storage.impl.MemoryStorage;
|
||||
import com.discordsrv.common.update.UpdateChecker;
|
||||
import com.discordsrv.common.uuid.util.UUIDUtil;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
@ -561,6 +562,7 @@ public abstract class AbstractDiscordSRV<
|
||||
placeholderService().addResultMapper(new ComponentResultStringifier(this));
|
||||
placeholderService().addGlobalContext(new GlobalTextHandlingContext(this));
|
||||
placeholderService().addGlobalContext(new GlobalDateFormattingContext(this));
|
||||
placeholderService().addGlobalContext(UUIDUtil.class);
|
||||
|
||||
// Modules
|
||||
registerModule(ConsoleModule::new);
|
||||
|
@ -15,5 +15,5 @@ public class AvatarProviderConfig {
|
||||
@Comment("The template for URLs of player avatars\n" +
|
||||
"This will be used for official Java players only if auto-decide-avatar-url is set to true\n" +
|
||||
"This will be used ALWAYS if auto-decide-avatar-url is set to false")
|
||||
public String avatarUrlTemplate = "https://crafatar.com/avatars/%player_uuid_nodashes%.png?size=128&overlay#%player_texture%";
|
||||
public String avatarUrlTemplate = "https://crafatar.com/avatars/%player_uuid_short%.png?size=128&overlay#%player_skin_texture_id%";
|
||||
}
|
||||
|
@ -46,15 +46,9 @@ public class LogEntry {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
@Placeholder(value = "log_time", relookup = "date")
|
||||
public ZonedDateTime logTime() {
|
||||
return logTime;
|
||||
}
|
||||
|
||||
@Placeholder("log_time")
|
||||
public PlaceholderLookupResult _logTimePlaceholder(@PlaceholderRemainder String format) {
|
||||
Set<Object> extras = new LinkedHashSet<>();
|
||||
extras.add(logTime());
|
||||
|
||||
return PlaceholderLookupResult.newLookup("date:'" + format + "'", extras);
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ public class DiscordUserImpl implements DiscordUser {
|
||||
return user.getEffectiveName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public @NotNull String getDiscriminator() {
|
||||
return user.getDiscriminator();
|
||||
|
@ -130,12 +130,12 @@ public class DiscordGuildMemberImpl implements DiscordGuildMember {
|
||||
// Placeholders
|
||||
//
|
||||
|
||||
@Placeholder(value = "user_highest_role", relookup = "role")
|
||||
@Placeholder(value = "highest_role", relookup = "role")
|
||||
public DiscordRole _highestRole() {
|
||||
return !roles.isEmpty() ? roles.get(0) : null;
|
||||
}
|
||||
|
||||
@Placeholder(value = "user_hoisted_role", relookup = "role")
|
||||
@Placeholder(value = "hoisted_role", relookup = "role")
|
||||
public DiscordRole _hoistedRole() {
|
||||
for (DiscordRole role : roles) {
|
||||
if (role.isHoisted()) {
|
||||
@ -145,7 +145,7 @@ public class DiscordGuildMemberImpl implements DiscordGuildMember {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Placeholder("user_roles")
|
||||
@Placeholder("roles")
|
||||
public Component _allRoles(@PlaceholderRemainder String suffix) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (DiscordRole role : getRoles()) {
|
||||
|
@ -268,7 +268,7 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
|
||||
// Placeholders
|
||||
//
|
||||
|
||||
@Placeholder("message_reply")
|
||||
@Placeholder("reply")
|
||||
public Component _reply(BaseChannelConfig config) {
|
||||
if (replyingTo == null) {
|
||||
return null;
|
||||
@ -291,7 +291,7 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
|
||||
);
|
||||
}
|
||||
|
||||
@Placeholder("message_attachments")
|
||||
@Placeholder("attachments")
|
||||
public Component _attachments(BaseChannelConfig config, @PlaceholderRemainder String suffix) {
|
||||
String attachmentFormat = config.discordToMinecraft.attachmentFormat;
|
||||
List<Component> components = new ArrayList<>();
|
||||
|
@ -22,13 +22,14 @@ import com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent;
|
||||
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
|
||||
import com.discordsrv.api.placeholder.mapper.PlaceholderResultMapper;
|
||||
import com.discordsrv.api.placeholder.provider.PlaceholderProvider;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.logging.Logger;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.placeholder.provider.AnnotationPlaceholderProvider;
|
||||
import com.discordsrv.api.placeholder.provider.PlaceholderProvider;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -43,6 +44,7 @@ import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -225,6 +227,8 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
private Object getResultRepresentation(List<PlaceholderLookupResult> results, String placeholder, Matcher matcher) {
|
||||
Map<String, AtomicInteger> preventInfiniteLoop = new HashMap<>();
|
||||
|
||||
Object best = null;
|
||||
for (PlaceholderLookupResult result : results) {
|
||||
while (result != null) {
|
||||
@ -253,7 +257,15 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
replacement = "Error";
|
||||
break;
|
||||
case NEW_LOOKUP:
|
||||
result = lookupPlaceholder((String) result.getValue(), result.getExtras());
|
||||
String placeholderKey = (String) result.getValue();
|
||||
|
||||
AtomicInteger infiniteLoop = preventInfiniteLoop.computeIfAbsent(placeholderKey, key -> new AtomicInteger(0));
|
||||
if (infiniteLoop.incrementAndGet() > 10) {
|
||||
replacement = "Infinite Loop";
|
||||
break;
|
||||
}
|
||||
|
||||
result = lookupPlaceholder(placeholderKey, result.getExtras());
|
||||
newLookup = true;
|
||||
break;
|
||||
}
|
||||
@ -273,59 +285,65 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
|
||||
private static class ClassProviderLoader implements CacheLoader<Class<?>, Set<PlaceholderProvider>> {
|
||||
|
||||
private Set<Class<?>> getAll(Class<?> clazz) {
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
classes.add(clazz);
|
||||
private Set<PlaceholderProvider> loadProviders(Class<?> clazz, PlaceholderPrefix prefix) {
|
||||
Set<PlaceholderProvider> providers = new LinkedHashSet<>();
|
||||
|
||||
for (Class<?> anInterface : clazz.getInterfaces()) {
|
||||
classes.addAll(getAll(anInterface));
|
||||
}
|
||||
Class<?> currentClass = clazz;
|
||||
while (currentClass != null) {
|
||||
PlaceholderPrefix currentPrefix = currentClass.getAnnotation(PlaceholderPrefix.class);
|
||||
if (currentPrefix != null && prefix == null) {
|
||||
prefix = currentPrefix;
|
||||
}
|
||||
PlaceholderPrefix usePrefix = (currentPrefix != null && currentPrefix.ignoreParents()) ? currentPrefix : prefix;
|
||||
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null) {
|
||||
classes.addAll(getAll(superClass));
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Set<PlaceholderProvider> load(@NonNull Class<?> key) {
|
||||
Set<PlaceholderProvider> providers = new HashSet<>();
|
||||
|
||||
Set<Class<?>> classes = getAll(key);
|
||||
for (Class<?> clazz : classes) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (!method.getDeclaringClass().equals(currentClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Placeholder annotation = method.getAnnotation(Placeholder.class);
|
||||
if (annotation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean startsWith = !annotation.relookup().isEmpty();
|
||||
if (!startsWith) {
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
if (parameter.getAnnotation(PlaceholderRemainder.class) != null) {
|
||||
startsWith = true;
|
||||
break;
|
||||
}
|
||||
PlaceholderRemainder remainder = null;
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
remainder = parameter.getAnnotation(PlaceholderRemainder.class);
|
||||
if (remainder != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
providers.add(new AnnotationPlaceholderProvider(annotation, isStatic ? null : clazz, startsWith, method));
|
||||
providers.add(new AnnotationPlaceholderProvider(annotation, usePrefix, remainder, isStatic ? null : clazz, method));
|
||||
}
|
||||
for (Field field : clazz.getFields()) {
|
||||
if (!field.getDeclaringClass().equals(currentClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Placeholder annotation = field.getAnnotation(Placeholder.class);
|
||||
if (annotation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
providers.add(new AnnotationPlaceholderProvider(annotation, isStatic ? null : clazz, !annotation.relookup().isEmpty(), field));
|
||||
providers.add(new AnnotationPlaceholderProvider(annotation, usePrefix, isStatic ? null : clazz, field));
|
||||
}
|
||||
|
||||
for (Class<?> anInterface : currentClass.getInterfaces()) {
|
||||
providers.addAll(loadProviders(anInterface, prefix));
|
||||
}
|
||||
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Set<PlaceholderProvider> load(@NonNull Class<?> key) {
|
||||
return loadProviders(key, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ package com.discordsrv.common.placeholder.provider;
|
||||
|
||||
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
|
||||
import com.discordsrv.api.placeholder.provider.PlaceholderProvider;
|
||||
import com.discordsrv.common.placeholder.provider.util.PlaceholderMethodUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -32,31 +34,35 @@ import java.util.Set;
|
||||
public class AnnotationPlaceholderProvider implements PlaceholderProvider {
|
||||
|
||||
private final Placeholder annotation;
|
||||
private final PlaceholderPrefix prefixAnnotation;
|
||||
private final PlaceholderRemainder remainderAnnotation;
|
||||
|
||||
private final Class<?> type;
|
||||
private final Method method;
|
||||
private final boolean startsWith;
|
||||
private final Field field;
|
||||
|
||||
public AnnotationPlaceholderProvider(Placeholder annotation, Class<?> type, boolean startsWith, Method method) {
|
||||
this.annotation = annotation;
|
||||
this.type = type;
|
||||
this.startsWith = startsWith;
|
||||
this.method = method;
|
||||
this.field = null;
|
||||
public AnnotationPlaceholderProvider(Placeholder annotation, PlaceholderPrefix prefixAnnotation, PlaceholderRemainder remainderAnnotation, Class<?> type, Method method) {
|
||||
this(annotation, prefixAnnotation, remainderAnnotation, type, method, null);
|
||||
}
|
||||
|
||||
public AnnotationPlaceholderProvider(Placeholder annotation, Class<?> type, boolean startsWith, Field field) {
|
||||
public AnnotationPlaceholderProvider(Placeholder annotation, PlaceholderPrefix prefixAnnotation, Class<?> type, Field field) {
|
||||
this(annotation, prefixAnnotation, null, type, null, field);
|
||||
}
|
||||
|
||||
private AnnotationPlaceholderProvider(Placeholder annotation, PlaceholderPrefix prefixAnnotation, PlaceholderRemainder remainderAnnotation, Class<?> type, Method method, Field field) {
|
||||
this.annotation = annotation;
|
||||
this.prefixAnnotation = prefixAnnotation;
|
||||
this.remainderAnnotation = remainderAnnotation;
|
||||
this.type = type;
|
||||
this.startsWith = startsWith;
|
||||
this.method = null;
|
||||
this.method = method;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlaceholderLookupResult lookup(@NotNull String placeholder, @NotNull Set<Object> context) {
|
||||
String annotationPlaceholder = annotation.value();
|
||||
String annotationPlaceholder = (prefixAnnotation != null ? prefixAnnotation.value() : "") + annotation.value();
|
||||
String reLookup = annotation.relookup();
|
||||
boolean startsWith = !reLookup.isEmpty() || remainderAnnotation != null;
|
||||
if (annotationPlaceholder.isEmpty()
|
||||
|| !(startsWith ? placeholder.startsWith(annotationPlaceholder) : placeholder.equals(annotationPlaceholder))
|
||||
|| (type != null && context.isEmpty())) {
|
||||
@ -75,7 +81,7 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
|
||||
}
|
||||
}
|
||||
|
||||
String remainder = placeholder.replace(annotationPlaceholder, "");
|
||||
String remainder = placeholder.substring(annotationPlaceholder.length());
|
||||
|
||||
Object result;
|
||||
try {
|
||||
@ -89,8 +95,10 @@ public class AnnotationPlaceholderProvider implements PlaceholderProvider {
|
||||
return PlaceholderLookupResult.lookupFailed(t);
|
||||
}
|
||||
|
||||
String reLookup = annotation.relookup();
|
||||
if (!reLookup.isEmpty()) {
|
||||
if (reLookup.isEmpty() && remainderAnnotation == null) {
|
||||
reLookup = annotation.value();
|
||||
}
|
||||
if (!reLookup.isEmpty() && !remainder.isEmpty()) {
|
||||
if (result == null) {
|
||||
return PlaceholderLookupResult.success(null);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common.player;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.player.provider.model.SkinInfo;
|
||||
import com.discordsrv.common.profile.Profile;
|
||||
@ -30,6 +31,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@PlaceholderPrefix("player_")
|
||||
public interface IOfflinePlayer extends Identified {
|
||||
|
||||
DiscordSRV discordSRV();
|
||||
@ -39,12 +41,12 @@ public interface IOfflinePlayer extends Identified {
|
||||
return discordSRV().profileManager().lookupProfile(uniqueId());
|
||||
}
|
||||
|
||||
@Placeholder("player_name")
|
||||
@Placeholder("name")
|
||||
@Nullable
|
||||
String username();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_uuid")
|
||||
@Placeholder(value = "uuid", relookup = "uuid")
|
||||
@NotNull
|
||||
default UUID uniqueId() {
|
||||
return identity().uuid();
|
||||
@ -53,14 +55,14 @@ public interface IOfflinePlayer extends Identified {
|
||||
@Nullable
|
||||
SkinInfo skinInfo();
|
||||
|
||||
@Placeholder("player_skin_texture_id")
|
||||
@Placeholder("skin_texture_id")
|
||||
@Nullable
|
||||
default String skinTextureId() {
|
||||
SkinInfo info = skinInfo();
|
||||
return info != null ? info.textureId() : null;
|
||||
}
|
||||
|
||||
@Placeholder("player_skin_model")
|
||||
@Placeholder("skin_model")
|
||||
@Nullable
|
||||
default String skinModel() {
|
||||
SkinInfo info = skinInfo();
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.discordsrv.common.player;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.api.player.DiscordSRVPlayer;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@PlaceholderPrefix("player_")
|
||||
public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSender {
|
||||
|
||||
@Override
|
||||
@ -47,29 +49,23 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Placeholder("player_name")
|
||||
@Placeholder("name")
|
||||
String username();
|
||||
|
||||
@Override
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_uuid")
|
||||
@Placeholder(value = "uuid", relookup = "uuid")
|
||||
default @NotNull UUID uniqueId() {
|
||||
return identity().uuid();
|
||||
}
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_uuid_nodashes")
|
||||
default @NotNull String uniqueIdNoDashes() {
|
||||
return uniqueId().toString().replace("-", "");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Placeholder("player_display_name")
|
||||
@Placeholder("display_name")
|
||||
Component displayName();
|
||||
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_avatar_url")
|
||||
@Placeholder("avatar_url")
|
||||
default String getAvatarUrl() {
|
||||
AvatarProviderConfig avatarConfig = discordSRV().config().avatarProvider;
|
||||
String avatarUrlTemplate = avatarConfig.avatarUrlTemplate;
|
||||
@ -78,7 +74,7 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
|
||||
// Offline mode
|
||||
if (uniqueId().version() == 3) avatarUrlTemplate = "https://cravatar.eu/helmavatar/%player_name%/128.png#%player_skin_texture_id%";
|
||||
// Bedrock
|
||||
else if (uniqueId().getLeastSignificantBits() == 0) avatarUrlTemplate = "https://api.tydiumcraft.net/skin?uuid=%player_uuid_nodashes%&type=avatar&size=128";
|
||||
else if (uniqueId().getLeastSignificantBits() == 0) avatarUrlTemplate = "https://api.tydiumcraft.net/skin?uuid=%player_uuid_short%&type=avatar&size=128";
|
||||
}
|
||||
|
||||
if (avatarUrlTemplate == null) {
|
||||
@ -90,28 +86,28 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
|
||||
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_meta_prefix")
|
||||
@Placeholder("meta_prefix")
|
||||
default Component getMetaPrefix() {
|
||||
return PermissionUtil.getMetaPrefix(discordSRV(), uniqueId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_meta_suffix")
|
||||
@Placeholder("meta_suffix")
|
||||
default Component getMetaSuffix() {
|
||||
return PermissionUtil.getMetaSuffix(discordSRV(), uniqueId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_prefix")
|
||||
@Placeholder("prefix")
|
||||
default Component getPrefix() {
|
||||
return PermissionUtil.getPrefix(discordSRV(), uniqueId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_suffix")
|
||||
@Placeholder("suffix")
|
||||
default Component getSuffix() {
|
||||
return PermissionUtil.getSuffix(discordSRV(), uniqueId());
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package com.discordsrv.common.uuid.util;
|
||||
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@PlaceholderPrefix("uuid_")
|
||||
public final class UUIDUtil {
|
||||
|
||||
private UUIDUtil() {}
|
||||
@ -31,6 +34,7 @@ public final class UUIDUtil {
|
||||
return UUID.fromString(fullLengthUUID);
|
||||
}
|
||||
|
||||
@Placeholder("short")
|
||||
public static String toShort(@NotNull UUID uuid) {
|
||||
return uuid.toString().replace("-", "");
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package com.discordsrv.common.placeholder;
|
||||
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.api.placeholder.annotation.Placeholder;
|
||||
import com.discordsrv.api.placeholder.annotation.PlaceholderPrefix;
|
||||
import com.discordsrv.common.MockDiscordSRV;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -69,6 +70,26 @@ public class PlaceholderServiceTest {
|
||||
assertEquals("b", service.replacePlaceholders("%empty|static_method%", PlaceholderContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixFailTest() {
|
||||
assertEquals("%placeholder%", service.replacePlaceholders("%placeholder%", PrefixContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixTest() {
|
||||
assertEquals("value", service.replacePlaceholders("%prefix_placeholder%", PrefixContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixInheritFailTest() {
|
||||
assertEquals("%prefix_noprefix%", service.replacePlaceholders("%prefix_noprefix%", PrefixInheritanceContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefixInheritTest() {
|
||||
assertEquals("value", service.replacePlaceholders("%noprefix%", PrefixInheritanceContext.class));
|
||||
}
|
||||
|
||||
public static class PlaceholderContext {
|
||||
|
||||
@Placeholder("static_field")
|
||||
@ -95,4 +116,16 @@ public class PlaceholderServiceTest {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@PlaceholderPrefix("prefix_")
|
||||
public static class PrefixContext {
|
||||
|
||||
@Placeholder("placeholder")
|
||||
public static String placeholder = "value";
|
||||
}
|
||||
public static class PrefixInheritanceContext extends PrefixContext {
|
||||
|
||||
@Placeholder("noprefix")
|
||||
public static String noPrefix = "value";
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user