impl welcomer feature
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 40s

This commit is contained in:
Lee 2024-07-03 19:49:19 +01:00
parent f62a022ed5
commit e4183b4882
13 changed files with 671 additions and 0 deletions

@ -0,0 +1,50 @@
package cc.fascinated.bat.features.welcomer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import net.dv8tion.jda.api.EmbedBuilder;
/**
* @author Fascinated (fascinated7)
*/
@AllArgsConstructor
@Getter
@Setter
public class WelcomerEmbed {
/**
* The title of the embed
*/
private String title;
/**
* The description of the embed
*/
@NonNull private String description;
/**
* The color of the embed
*/
@NonNull private String color;
/**
* Should we ping the user before sending the message?
*/
private boolean pingBeforeSend;
/**
* Builds the embed and replaces the placeholders
*
* @return The built embed
*/
public EmbedBuilder buildEmbed(Object... replacements) {
EmbedBuilder embedBuilder = new EmbedBuilder();
if (title != null) {
embedBuilder.setTitle(WelcomerPlaceholders.replaceAllPlaceholders(title, replacements));
}
embedBuilder.setDescription(WelcomerPlaceholders.replaceAllPlaceholders(description, replacements));
embedBuilder.setColor(Integer.parseInt(color, 16));
return embedBuilder;
}
}

@ -0,0 +1,23 @@
package cc.fascinated.bat.features.welcomer;
import cc.fascinated.bat.command.Category;
import cc.fascinated.bat.features.Feature;
import cc.fascinated.bat.features.welcomer.command.WelcomerCommand;
import cc.fascinated.bat.service.CommandService;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class WelcomerFeature extends Feature {
@Autowired
public WelcomerFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) {
super("Welcomer", true,Category.SERVER);
super.registerCommand(commandService, context.getBean(WelcomerCommand.class));
}
}

@ -0,0 +1,32 @@
package cc.fascinated.bat.features.welcomer;
import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class WelcomerListener implements EventListener {
private final WelcomerFeature welcomerFeature;
@Autowired
public WelcomerListener(WelcomerFeature welcomerFeature) {
this.welcomerFeature = welcomerFeature;
}
@Override
public void onGuildMemberJoin(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberJoinEvent event) {
if (guild.getFeatureProfile().isFeatureDisabled(welcomerFeature)) { // Check if the feature is disabled
return;
}
WelcomerProfile profile = guild.getWelcomerProfile();
profile.sendWelcomeMessage(user);
}
}

@ -0,0 +1,27 @@
package cc.fascinated.bat.features.welcomer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
/**
* @author Fascinated (fascinated7)
*/
@AllArgsConstructor
@Getter
@Setter
public class WelcomerMessage {
/**
* The message to send
*/
private String message;
/**
* Builds the message and replaces the placeholders
*
* @return The built message
*/
public String buildMessage(Object... replacements) {
return WelcomerPlaceholders.replaceAllPlaceholders(message, replacements);
}
}

@ -0,0 +1,82 @@
package cc.fascinated.bat.features.welcomer;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Date;
/**
* @author Fascinated (fascinated7)
*/
@AllArgsConstructor
@Getter
public enum WelcomerPlaceholders {
USER_MENTION("{user_mention}", BatUser.class) {
@Override
public String replacePlaceholder(Object object) {
return ((BatUser) object).getDiscordUser().getAsMention();
}
},
USER_NAME("{user_name}", BatUser.class) {
@Override
public String replacePlaceholder(Object object) {
return ((BatUser) object).getName();
}
},
GUILD_NAME("{guild_name}", BatGuild.class) {
@Override
public String replacePlaceholder(Object object) {
return ((BatGuild) object).getName();
}
},
JOIN_DATE("{join_date}", null) {
@Override
public String replacePlaceholder(Object object) {
return "<t:%s>".formatted(new Date().toInstant().getEpochSecond());
}
};
/**
* The placeholder string that will get replaced
*/
private final String placeholder;
/**
* The class that the placeholder is associated with
*/
private final Class<?> clazz;
/**
* Replaces the placeholder with the string based on the overridden method
*
* @param object The object to replace the placeholder with
* @return The string with the placeholder replaced
*/
public String replacePlaceholder(Object object) {
if (clazz != null && !clazz.isInstance(object)) {
throw new IllegalArgumentException("Object is not an instance of " + clazz.getName());
}
return null;
}
/**
* Replaces all placeholders in a message with the objects provided
*
* @param message The message to replace the placeholders in
* @param objects The objects to replace the placeholders with
* @return The message with the placeholders replaced
*/
public static String replaceAllPlaceholders(String message, Object... objects) {
for (WelcomerPlaceholders placeholder : values()) {
for (Object object : objects) {
if (placeholder.getClazz() != null && !placeholder.getClazz().isInstance(object)) {
continue;
}
message = message.replace(placeholder.getPlaceholder(), placeholder.replacePlaceholder(object));
}
}
return message;
}
}

@ -0,0 +1,144 @@
package cc.fascinated.bat.features.welcomer;
import cc.fascinated.bat.common.Serializable;
import cc.fascinated.bat.model.BatUser;
import cc.fascinated.bat.service.DiscordService;
import com.google.gson.Gson;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.bson.Document;
/**
* @author Fascinated (fascinated7)
*/
@Getter @Setter
public class WelcomerProfile extends Serializable {
/**
* The welcomer message, null if we're using an embed
*/
private WelcomerMessage welcomerMessage;
/**
* The welcomer embed, null if we're using a message
*/
private WelcomerEmbed welcomerEmbed;
/**
* The channel to send the welcomer messages to
*/
private TextChannel channel;
/**
* Gets the welcomer message
*
* @return The welcomer message, false if we're using an embed
*/
public boolean isEmbed() {
return welcomerEmbed != null;
}
/**
* Gets the welcomer message
*
* @return The welcomer message, false if we're using an embed
*/
public boolean isMessage() {
return welcomerMessage != null;
}
/**
* Sets the welcomer message
* <p>
* This will disable the embed if it's enabled
* </p>
*/
public void setMessage(String message) {
welcomerMessage = new WelcomerMessage(message);
welcomerEmbed = null;
}
/**
* Sets the welcomer embed
* <p>
* This will disable the message if it's enabled
* <p>
*/
public void setEmbed(String title, String description, String color, boolean pingBeforeSend) {
welcomerEmbed = new WelcomerEmbed(title, description, color, pingBeforeSend);
welcomerMessage = null;
}
/**
* Sends the welcome message to the user
*
* @param user The user to send the message to
*/
public void sendWelcomeMessage(BatUser user) {
if (this.channel == null || (!this.isMessage() && !this.isEmbed())) {
return;
}
if (welcomerEmbed != null) {
if (welcomerEmbed.isPingBeforeSend()) { // Ping the user before sending the message
this.channel.sendMessage(user.getDiscordUser().getAsMention()).queue();
}
this.channel.sendMessageEmbeds(welcomerEmbed.buildEmbed(user).build()).queue();
return;
}
if (welcomerMessage != null) {
this.channel.sendMessage(welcomerMessage.buildMessage(user)).queue();
}
}
@Override
public void load(Document document, Gson gson) {
Document welcomerMessageDocument = document.get("welcomerMessage", Document.class);
if (welcomerMessageDocument != null) {
welcomerMessage = new WelcomerMessage(
welcomerMessageDocument.getString("message")
);
}
Document welcomerEmbedDocument = document.get("welcomerEmbed", Document.class);
if (welcomerEmbedDocument != null) {
welcomerEmbed = new WelcomerEmbed(
welcomerEmbedDocument.getString("title"),
welcomerEmbedDocument.getString("description"),
welcomerEmbedDocument.getString("color"),
welcomerEmbedDocument.getBoolean("pingBeforeSend")
);
}
String channelId = document.getString("channelId");
if (channelId != null) {
TextChannel textChannel = DiscordService.JDA.getTextChannelById(channelId);
if (textChannel != null) {
channel = textChannel;
}
}
}
@Override
public Document serialize(Gson gson) {
Document document = new Document();
if (welcomerMessage != null) {
document.put("welcomerMessage", new Document("message", welcomerMessage.getMessage()));
}
if (welcomerEmbed != null) {
document.put("welcomerEmbed", new Document()
.append("title", welcomerEmbed.getTitle())
.append("description", welcomerEmbed.getDescription())
.append("color", welcomerEmbed.getColor())
.append("pingBeforeSend", welcomerEmbed.isPingBeforeSend())
);
}
if (channel != null) {
document.put("channelId", channel.getIdLong());
}
return document;
}
@Override
public void reset() {
welcomerMessage = null;
welcomerEmbed = null;
}
}

@ -0,0 +1,30 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component("welcomer:channel.sub")
@CommandInfo(name = "channel", description = "Set the welcomer channel")
public class ChannelSubCommand extends BatSubCommand {
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
WelcomerProfile profile = guild.getWelcomerProfile();
profile.setChannel(guild.getDiscordGuild().getTextChannelById(channel.getId()));
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription("The welcomer channel has been set to %s".formatted(channel.getAsMention()))
.build()).queue();
}
}

@ -0,0 +1,64 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.welcomer.WelcomerPlaceholders;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component("welcomer:current.sub")
@CommandInfo(name = "current", description = "View the current welcomer configuration")
public class CurrentSubCommand extends BatSubCommand {
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
WelcomerProfile profile = guild.getWelcomerProfile();
if (!profile.isEmbed() && !profile.isMessage()) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The welcomer is not configured.\n\n" + getPlaceholders(guild, user))
.build()).queue();
return;
}
if (profile.isEmbed()) {
event.replyEmbeds(profile.getWelcomerEmbed().buildEmbed(guild, user)
.appendDescription("\n\n" + getPlaceholders(guild, user))
.build()).queue();
return;
}
if (profile.isMessage()) {
event.replyEmbeds(EmbedUtils.genericEmbed()
.setDescription("**Preview:** %s\n*note: the real message won't be an embed*\n\n%s".formatted(
profile.getWelcomerMessage().buildMessage(guild, user),
getPlaceholders(guild, user)
))
.build()).queue();
}
}
/**
* Get the placeholders that the user can use
*
* @param replacements What to replace the placeholders using
* @return The placeholders
*/
public String getPlaceholders(Object... replacements) {
StringBuilder builder = new StringBuilder();
builder.append("**Available Placeholders:**\n");
for (WelcomerPlaceholders placeholder : WelcomerPlaceholders.values()) {
String placeholderString = placeholder.getPlaceholder();
builder.append("`").append(placeholderString).append("` - ")
.append(WelcomerPlaceholders.replaceAllPlaceholders(placeholderString, replacements)).append("\n");
}
return builder.toString();
}
}

@ -0,0 +1,96 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedDescriptionBuilder;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import org.springframework.stereotype.Component;
import java.awt.*;
/**
* @author Fascinated (fascinated7)
*/
@Component("welcomer:embed.sub")
@CommandInfo(name = "embed", description = "Set the welcomer embed (this will remove the welcomer plain message if set)")
public class EmbedSubCommand extends BatSubCommand {
public EmbedSubCommand() {
super.addOption(OptionType.BOOLEAN, "ping-before-send", "Should we ping the user before sending the message?", true);
super.addOption(OptionType.STRING, "description", "The description of the embed", true);
super.addOption(OptionType.STRING, "color", "The color of the embed", true);
super.addOption(OptionType.STRING, "title", "The title of the embed (only set if you want a title)", false);
}
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
WelcomerProfile profile = guild.getWelcomerProfile();
OptionMapping titleOption = event.getOption("title");
OptionMapping descriptionOption = event.getOption("description");
OptionMapping colorOption = event.getOption("color");
OptionMapping pingBeforeSendOption = event.getOption("ping-before-send");
if (descriptionOption == null || colorOption == null || pingBeforeSendOption == null) {
return;
}
String title = titleOption == null ? null : titleOption.getAsString();
String description = descriptionOption.getAsString();
String color = colorOption.getAsString();
boolean pingBeforeSend = pingBeforeSendOption.getAsBoolean();
// Validate the input
if (color.length() != 6 || Color.decode("#" + color).getRGB() == -1){
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The color must be a valid hex color code\n" +
"You can use this website to get a hex color code: https://htmlcolorcodes.com")
.build()).queue();
return;
}
if (title != null && title.length() > 128) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The title must be less than 128 characters")
.build()).queue();
return;
}
if (description.length() > 512) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The description must be less than 512 characters")
.build()).queue();
return;
}
color = color.replace("#", ""); // Remove # if the user added it
boolean isMessageEnabled = profile.isMessage();
profile.setEmbed(title, description, color, pingBeforeSend);
EmbedDescriptionBuilder successDescription = new EmbedDescriptionBuilder("Welcomer Embed")
.appendLine("%s Successfully set the welcomer embed!".formatted(Emojis.CHECK_MARK_EMOJI), false);
if (isMessageEnabled) {
successDescription.appendLine("*This has removed the plain message welcomer*", false);
}
successDescription.emptyLine();
successDescription.appendLine("**Configuration:**", false);
if (title != null) {
successDescription.appendLine("Title: `%s`".formatted(title), true);
}
successDescription.appendLine("Description: `%s`".formatted(description), true);
successDescription.appendLine("Color: `#%s`".formatted(color), true);
successDescription.appendLine("Ping Before Send: %s".formatted(pingBeforeSend ? Emojis.CHECK_MARK_EMOJI.getFormatted() + " *(Preview won't ping you)*" : Emojis.CROSS_MARK_EMOJI), true);
successDescription.emptyLine();
successDescription.appendLine("**Preview Below:**", false);
event.replyEmbeds(
EmbedUtils.successEmbed()
.setDescription(successDescription.build())
.build(),
profile.getWelcomerEmbed().buildEmbed(guild, user).build()
).queue();
}
}

@ -0,0 +1,52 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.Emojis;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedDescriptionBuilder;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.welcomer.WelcomerPlaceholders;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component("welcomer:message.sub")
@CommandInfo(name = "message", description = "Set the welcomer message (this will remove the welcomer embed if set)")
public class MessageSubCommand extends BatSubCommand {
public MessageSubCommand() {
super.addOption(OptionType.STRING, "message", "The message to send", true);
}
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
WelcomerProfile profile = guild.getWelcomerProfile();
OptionMapping messageOption = event.getOption("message");
if (messageOption == null) {
return;
}
String message = messageOption.getAsString();
boolean isEmbedEnabled = profile.isEmbed();
profile.setMessage(message);
EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Welcomer Message")
.appendLine("%s Set the message to `%s`".formatted(Emojis.CHECK_MARK_EMOJI, message), false);
if (isEmbedEnabled) {
description.appendLine("*This has removed the embed welcomer*", false);
}
description.emptyLine();
description.appendLine("**Preview:** %s".formatted(WelcomerPlaceholders.replaceAllPlaceholders(message, guild, user)), false);
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription(description.build())
.build()).queue();
}
}

@ -0,0 +1,36 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component("welcomer:reset.sub")
@CommandInfo(name = "reset", description = "Clear the welcomer configuration")
public class ResetSubCommand extends BatSubCommand {
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
WelcomerProfile profile = guild.getWelcomerProfile();
if (!profile.isEmbed() && !profile.isMessage()) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The welcomer is not configured")
.build()).queue();
return;
}
profile.reset();
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription("The welcomer configuration has been reset")
.build()).queue();
}
}

@ -0,0 +1,25 @@
package cc.fascinated.bat.features.welcomer.command;
import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.CommandInfo;
import lombok.NonNull;
import net.dv8tion.jda.api.Permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
@CommandInfo(name = "welcomer", description = "Configure the welcomer on your server", requiredPermissions = Permission.MANAGE_SERVER)
public class WelcomerCommand extends BatCommand {
@Autowired
public WelcomerCommand(@NonNull ApplicationContext context) {
super.addSubCommand(context.getBean(MessageSubCommand.class));
super.addSubCommand(context.getBean(EmbedSubCommand.class));
super.addSubCommand(context.getBean(CurrentSubCommand.class));
super.addSubCommand(context.getBean(ChannelSubCommand.class));
super.addSubCommand(context.getBean(ResetSubCommand.class));
}
}

@ -8,6 +8,7 @@ import cc.fascinated.bat.features.birthday.profile.BirthdayProfile;
import cc.fascinated.bat.features.logging.LogProfile;
import cc.fascinated.bat.features.namehistory.profile.guild.NameHistoryProfile;
import cc.fascinated.bat.features.reminder.ReminderProfile;
import cc.fascinated.bat.features.welcomer.WelcomerProfile;
import cc.fascinated.bat.premium.PremiumProfile;
import cc.fascinated.bat.service.DiscordService;
import cc.fascinated.bat.service.MongoService;
@ -128,6 +129,15 @@ public class BatGuild extends ProfileHolder {
return getProfile(ReminderProfile.class);
}
/**
* Gets the welcomer profile
*
* @return the welcomer profile
*/
public WelcomerProfile getWelcomerProfile() {
return getProfile(WelcomerProfile.class);
}
/**
* Saves the user
*/