add message snipe feature
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 39s
All checks were successful
Deploy to Dokku / docker (ubuntu-latest) (push) Successful in 39s
This commit is contained in:
parent
727a4c9a6f
commit
8b451c6ee5
16
pom.xml
16
pom.xml
@ -108,6 +108,22 @@
|
|||||||
<version>5.2.4</version>
|
<version>5.2.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Redis for caching -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>io.lettuce</groupId>
|
||||||
|
<artifactId>lettuce-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>redis.clients</groupId>
|
||||||
|
<artifactId>jedis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- Libraries -->
|
<!-- Libraries -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -18,6 +18,7 @@ public enum Category {
|
|||||||
SERVER(Emoji.fromFormatted("U+1F5A5"), "Server", false),
|
SERVER(Emoji.fromFormatted("U+1F5A5"), "Server", false),
|
||||||
UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false),
|
UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false),
|
||||||
MUSIC(Emoji.fromFormatted("U+1F3B5"), "Music", false),
|
MUSIC(Emoji.fromFormatted("U+1F3B5"), "Music", false),
|
||||||
|
SNIPE(Emoji.fromFormatted("U+1F4A3"), "Snipe", false),
|
||||||
BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false),
|
BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false),
|
||||||
BOT_ADMIN(null, null, true);
|
BOT_ADMIN(null, null, true);
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import cc.fascinated.bat.BatApplication;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -13,6 +15,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public abstract class ProfileHolder {
|
public abstract class ProfileHolder {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ProfileHolder.class);
|
||||||
/**
|
/**
|
||||||
* The profiles for the holder
|
* The profiles for the holder
|
||||||
*/
|
*/
|
||||||
@ -38,6 +41,8 @@ public abstract class ProfileHolder {
|
|||||||
Serializable profile = getProfiles().get(clazz.getSimpleName());
|
Serializable profile = getProfiles().get(clazz.getSimpleName());
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
T newProfile = clazz.cast(clazz.getDeclaredConstructors()[0].newInstance());
|
T newProfile = clazz.cast(clazz.getDeclaredConstructors()[0].newInstance());
|
||||||
|
|
||||||
|
log.info("instance of profiles: {}", document.get("profiles").getClass().getSimpleName());
|
||||||
Document profiles = document.get("profiles", new org.bson.Document());
|
Document profiles = document.get("profiles", new org.bson.Document());
|
||||||
Document profileDocument = (Document) profiles.get(clazz.getSimpleName());
|
Document profileDocument = (Document) profiles.get(clazz.getSimpleName());
|
||||||
|
|
||||||
|
73
src/main/java/cc/fascinated/bat/config/RedisConfig.java
Normal file
73
src/main/java/cc/fascinated/bat/config/RedisConfig.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package cc.fascinated.bat.config;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||||
|
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@Log4j2(topic = "Redis")
|
||||||
|
public class RedisConfig {
|
||||||
|
/**
|
||||||
|
* The Redis server host.
|
||||||
|
*/
|
||||||
|
@Value("${spring.data.redis.host}")
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Redis server port.
|
||||||
|
*/
|
||||||
|
@Value("${spring.data.redis.port}")
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Redis database index.
|
||||||
|
*/
|
||||||
|
@Value("${spring.data.redis.database}")
|
||||||
|
private int database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The optional Redis password.
|
||||||
|
*/
|
||||||
|
@Value("${spring.data.redis.auth}")
|
||||||
|
private String auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the config to use for Redis.
|
||||||
|
*
|
||||||
|
* @return the config
|
||||||
|
* @see RedisTemplate for config
|
||||||
|
*/
|
||||||
|
@Bean @NonNull
|
||||||
|
public RedisTemplate<String, Object> redisTemplate() {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(jedisConnectionFactory());
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the connection factory to use
|
||||||
|
* when making connections to Redis.
|
||||||
|
*
|
||||||
|
* @return the built factory
|
||||||
|
* @see JedisConnectionFactory for factory
|
||||||
|
*/
|
||||||
|
@Bean @NonNull
|
||||||
|
public JedisConnectionFactory jedisConnectionFactory() {
|
||||||
|
log.info("Connecting to Redis at {}:{}/{}", host, port, database);
|
||||||
|
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port);
|
||||||
|
config.setDatabase(database);
|
||||||
|
if (!auth.trim().isEmpty()) { // Auth with our provided password
|
||||||
|
log.info("Using auth...");
|
||||||
|
config.setPassword(auth);
|
||||||
|
}
|
||||||
|
return new JedisConnectionFactory(config);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package cc.fascinated.bat.event;
|
|||||||
|
|
||||||
import cc.fascinated.bat.model.BatGuild;
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
import cc.fascinated.bat.model.BatUser;
|
import cc.fascinated.bat.model.BatUser;
|
||||||
|
import cc.fascinated.bat.model.DiscordMessage;
|
||||||
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken;
|
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberLeaderboardToken;
|
||||||
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken;
|
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberPlayerScoreToken;
|
||||||
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken;
|
import cc.fascinated.bat.model.token.beatsaber.scoresaber.ScoreSaberScoreToken;
|
||||||
@ -12,7 +13,9 @@ import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameE
|
|||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||||
import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent;
|
import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +60,24 @@ public interface EventListener {
|
|||||||
default void onGuildMessageReceive(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageReceivedEvent event) {
|
default void onGuildMessageReceive(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageReceivedEvent event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a user updates a message
|
||||||
|
*
|
||||||
|
* @param guild the guild that the message was updated in
|
||||||
|
* @param user the user that updated the message
|
||||||
|
*/
|
||||||
|
default void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageUpdateEvent event) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a user deletes a message
|
||||||
|
*
|
||||||
|
* @param guild the guild that the message was deleted in
|
||||||
|
* @param user the user that deleted the message
|
||||||
|
*/
|
||||||
|
default void onGuildMessageDelete(@NonNull BatGuild guild, BatUser user, DiscordMessage message, @NonNull MessageDeleteEvent event) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a user selects a string
|
* Called when a user selects a string
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,6 @@ import cc.fascinated.bat.common.NumberFormatter;
|
|||||||
import cc.fascinated.bat.model.BatGuild;
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
import cc.fascinated.bat.model.BatUser;
|
import cc.fascinated.bat.model.BatUser;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import net.dv8tion.jda.api.EmbedBuilder;
|
|
||||||
import net.dv8tion.jda.api.OnlineStatus;
|
import net.dv8tion.jda.api.OnlineStatus;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
@ -0,0 +1,123 @@
|
|||||||
|
package cc.fascinated.bat.features.messagesnipe;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.command.Category;
|
||||||
|
import cc.fascinated.bat.event.EventListener;
|
||||||
|
import cc.fascinated.bat.features.Feature;
|
||||||
|
import cc.fascinated.bat.features.messagesnipe.command.MessageSnipeCommand;
|
||||||
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
|
import cc.fascinated.bat.model.BatUser;
|
||||||
|
import cc.fascinated.bat.model.DiscordMessage;
|
||||||
|
import cc.fascinated.bat.service.CommandService;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MessageSnipeFeature extends Feature implements EventListener {
|
||||||
|
/**
|
||||||
|
* The sniped messages for each guild
|
||||||
|
*/
|
||||||
|
private static final Map<BatGuild, List<SnipedMessage>> snipedMessages = new HashMap<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MessageSnipeFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) {
|
||||||
|
super("Message Snipe", false, Category.SNIPE);
|
||||||
|
|
||||||
|
super.registerCommand(commandService, context.getBean(MessageSnipeCommand.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the sniped messages for the given guild
|
||||||
|
*
|
||||||
|
* @param guild the guild
|
||||||
|
* @return if the sniped messages were cleared
|
||||||
|
*/
|
||||||
|
public static boolean clearSnipedMessages(BatGuild guild) {
|
||||||
|
if (snipedMessages.containsKey(guild)) {
|
||||||
|
snipedMessages.remove(guild);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sniped messages for the given guild
|
||||||
|
*
|
||||||
|
* @param guild the guild
|
||||||
|
* @return the sniped messages for the given guild
|
||||||
|
*/
|
||||||
|
public static SnipedMessage getDeletedMessage(BatGuild guild, String channelId) {
|
||||||
|
List<SnipedMessage> messages = snipedMessages.getOrDefault(guild, new ArrayList<>());
|
||||||
|
for (SnipedMessage message : messages) {
|
||||||
|
if (message.getDeletedDate() != null // Check if the message was deleted
|
||||||
|
&& message.getMessage().getChannel().getId().equals(channelId)) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the sniped message with the given id
|
||||||
|
*
|
||||||
|
* @param guild the guild
|
||||||
|
* @param messageId the id of the message
|
||||||
|
* @return the sniped message with the given id
|
||||||
|
*/
|
||||||
|
private SnipedMessage getSnipedMessage(BatGuild guild, String messageId) {
|
||||||
|
List<SnipedMessage> messages = snipedMessages.getOrDefault(guild, new ArrayList<>());
|
||||||
|
for (SnipedMessage message : messages) {
|
||||||
|
if (message.getMessage().getId().equals(messageId)) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildMessageReceive(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageReceivedEvent event) {
|
||||||
|
if (event.getAuthor().isBot()) return;
|
||||||
|
|
||||||
|
List<SnipedMessage> messages = snipedMessages.getOrDefault(guild, new ArrayList<>());
|
||||||
|
if (messages.size() >= 10) {
|
||||||
|
messages.remove(0);
|
||||||
|
}
|
||||||
|
messages.add(new SnipedMessage(event.getMessage(), null));
|
||||||
|
snipedMessages.put(guild, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildMessageDelete(@NonNull BatGuild guild, BatUser user, DiscordMessage message, @NonNull MessageDeleteEvent event) {
|
||||||
|
List<SnipedMessage> messages = snipedMessages.getOrDefault(guild, new ArrayList<>());
|
||||||
|
if (messages.size() >= 10) {
|
||||||
|
messages.remove(0);
|
||||||
|
}
|
||||||
|
SnipedMessage snipedMessage = getSnipedMessage(guild, event.getMessageId());
|
||||||
|
if (snipedMessage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snipedMessage.setDeletedDate(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageUpdateEvent event) {
|
||||||
|
List<SnipedMessage> messages = snipedMessages.getOrDefault(guild, new ArrayList<>());
|
||||||
|
if (messages.size() >= 10) {
|
||||||
|
messages.remove(0);
|
||||||
|
}
|
||||||
|
SnipedMessage snipedMessage = getSnipedMessage(guild, event.getMessageId());
|
||||||
|
if (snipedMessage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snipedMessage.setMessage(event.getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package cc.fascinated.bat.features.messagesnipe;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SnipedMessage {
|
||||||
|
/**
|
||||||
|
* The message that was sniped
|
||||||
|
*/
|
||||||
|
private Message message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date when the message was deleted
|
||||||
|
*/
|
||||||
|
private Date deletedDate;
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package cc.fascinated.bat.features.messagesnipe.command;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.command.BatSubCommand;
|
||||||
|
import cc.fascinated.bat.command.CommandInfo;
|
||||||
|
import cc.fascinated.bat.common.EmbedUtils;
|
||||||
|
import cc.fascinated.bat.features.messagesnipe.MessageSnipeFeature;
|
||||||
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
|
import cc.fascinated.bat.model.BatUser;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
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("messagesnipe:clear.sub")
|
||||||
|
@CommandInfo(name = "clear", description = "Clears the known sniped messages for this guild", requiredPermissions = Permission.MESSAGE_MANAGE)
|
||||||
|
public class ClearSubCommand extends BatSubCommand {
|
||||||
|
@Override
|
||||||
|
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
|
||||||
|
boolean cleared = MessageSnipeFeature.clearSnipedMessages(guild);
|
||||||
|
if (!cleared) {
|
||||||
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
|
.setDescription("There are no messages to clear in this guild")
|
||||||
|
.build()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.replyEmbeds(EmbedUtils.successEmbed()
|
||||||
|
.setDescription("Successfully cleared the sniped messages for this guild")
|
||||||
|
.build()).queue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package cc.fascinated.bat.features.messagesnipe.command;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.command.BatSubCommand;
|
||||||
|
import cc.fascinated.bat.command.CommandInfo;
|
||||||
|
import cc.fascinated.bat.common.EmbedUtils;
|
||||||
|
import cc.fascinated.bat.features.messagesnipe.MessageSnipeFeature;
|
||||||
|
import cc.fascinated.bat.features.messagesnipe.SnipedMessage;
|
||||||
|
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.User;
|
||||||
|
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
|
||||||
|
@CommandInfo(name = "deleted", description = "Snipe the last deleted message in this channel")
|
||||||
|
public class DeletedSubCommand extends BatSubCommand {
|
||||||
|
@Override
|
||||||
|
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
|
||||||
|
SnipedMessage message = MessageSnipeFeature.getDeletedMessage(guild, channel.getId());
|
||||||
|
if (message == null) {
|
||||||
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
||||||
|
.setDescription("There are no deleted messages to snipe in this channel")
|
||||||
|
.build()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
User author = message.getMessage().getAuthor();
|
||||||
|
event.replyEmbeds(EmbedUtils.genericEmbed()
|
||||||
|
.setDescription("""
|
||||||
|
**Deleted Message Snipe**
|
||||||
|
➜ Author: **%s** (%s)
|
||||||
|
➜ Deleted: <t:%d:R>
|
||||||
|
➜ Content:
|
||||||
|
```
|
||||||
|
%s
|
||||||
|
```
|
||||||
|
""".formatted(
|
||||||
|
author.getAsMention(),
|
||||||
|
author.getId(),
|
||||||
|
message.getDeletedDate().getTime() / 1000,
|
||||||
|
message.getMessage().getContentRaw()
|
||||||
|
)).build()).queue();
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/cc/fascinated/bat/features/messagesnipe/command/MessageSnipeCommand.java
Normal file
19
src/main/java/cc/fascinated/bat/features/messagesnipe/command/MessageSnipeCommand.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package cc.fascinated.bat.features.messagesnipe.command;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.command.BatCommand;
|
||||||
|
import cc.fascinated.bat.command.CommandInfo;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@CommandInfo(name = "snipe", description = "Snipe messages")
|
||||||
|
public class MessageSnipeCommand extends BatCommand {
|
||||||
|
public MessageSnipeCommand(@NonNull ApplicationContext context) {
|
||||||
|
super.addSubCommand(context.getBean(DeletedSubCommand.class));
|
||||||
|
super.addSubCommand(context.getBean(ClearSubCommand.class));
|
||||||
|
}
|
||||||
|
}
|
49
src/main/java/cc/fascinated/bat/model/DiscordMessage.java
Normal file
49
src/main/java/cc/fascinated/bat/model/DiscordMessage.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package cc.fascinated.bat.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.data.redis.core.RedisHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter @Setter
|
||||||
|
@RedisHash(value = "DiscordMessage", timeToLive = 86400) // 24 hours
|
||||||
|
public class DiscordMessage {
|
||||||
|
/**
|
||||||
|
* The snowflake ID of the message
|
||||||
|
*/
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp when the message was sent
|
||||||
|
*/
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The snowflake ID of the channel the message was sent in
|
||||||
|
*/
|
||||||
|
private final String channelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The snowflake ID of the guild the message was sent in
|
||||||
|
*/
|
||||||
|
private final String guildId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The snowflake ID of the author of the message
|
||||||
|
*/
|
||||||
|
private final String authorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The content of the message
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the message was deleted
|
||||||
|
*/
|
||||||
|
private boolean deleted;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package cc.fascinated.bat.repository;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.model.DiscordMessage;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
public interface DiscordMessageRepository extends CrudRepository<DiscordMessage, String> {}
|
@ -0,0 +1,95 @@
|
|||||||
|
package cc.fascinated.bat.service;
|
||||||
|
|
||||||
|
import cc.fascinated.bat.event.EventListener;
|
||||||
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
|
import cc.fascinated.bat.model.BatUser;
|
||||||
|
import cc.fascinated.bat.model.DiscordMessage;
|
||||||
|
import cc.fascinated.bat.repository.DiscordMessageRepository;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.DependsOn;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Fascinated (fascinated7)
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Log4j2
|
||||||
|
@DependsOn("discordService")
|
||||||
|
public class DiscordMessageService extends ListenerAdapter {
|
||||||
|
private final DiscordMessageRepository discordMessageRepository;
|
||||||
|
private final GuildService guildService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DiscordMessageService(DiscordMessageRepository discordMessageRepository, @NonNull GuildService guildService,
|
||||||
|
@NonNull UserService userService) {
|
||||||
|
this.discordMessageRepository = discordMessageRepository;
|
||||||
|
this.guildService = guildService;
|
||||||
|
this.userService = userService;
|
||||||
|
|
||||||
|
DiscordService.JDA.addEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the message with the given id
|
||||||
|
*
|
||||||
|
* @param messageId the id of the message
|
||||||
|
* @return the message with the given id
|
||||||
|
*/
|
||||||
|
public DiscordMessage getMessage(String messageId) {
|
||||||
|
Optional<DiscordMessage> optionalMessage = discordMessageRepository.findById(messageId);
|
||||||
|
return optionalMessage.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
|
||||||
|
discordMessageRepository.save(new DiscordMessage(
|
||||||
|
event.getMessageId(),
|
||||||
|
event.getMessage().getTimeCreated().toInstant().toEpochMilli(),
|
||||||
|
event.getChannel().getId(),
|
||||||
|
event.getGuild().getId(),
|
||||||
|
event.getAuthor().getId(),
|
||||||
|
event.getMessage().getContentRaw(),
|
||||||
|
false
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageUpdate(@NotNull MessageUpdateEvent event) {
|
||||||
|
Optional<DiscordMessage> message = discordMessageRepository.findById(event.getMessageId());
|
||||||
|
if (message.isPresent()) {
|
||||||
|
DiscordMessage discordMessage = message.get();
|
||||||
|
if (discordMessage.getContent().equals(event.getMessage().getContentRaw())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
discordMessage.setContent(event.getMessage().getContentRaw());
|
||||||
|
discordMessageRepository.save(discordMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
BatGuild guild = guildService.getGuild(event.getGuild().getId());
|
||||||
|
BatUser user = userService.getUser(event.getAuthor().getId());
|
||||||
|
for (EventListener listener : EventService.LISTENERS) {
|
||||||
|
listener.onGuildMessageEdit(guild, user, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageDelete(@NotNull MessageDeleteEvent event) {
|
||||||
|
Optional<DiscordMessage> optionalMessage = discordMessageRepository.findById(event.getMessageId());
|
||||||
|
if (optionalMessage.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DiscordMessage message = optionalMessage.get();
|
||||||
|
message.setDeleted(true);
|
||||||
|
discordMessageRepository.save(message);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package cc.fascinated.bat.service;
|
|||||||
import cc.fascinated.bat.event.EventListener;
|
import cc.fascinated.bat.event.EventListener;
|
||||||
import cc.fascinated.bat.model.BatGuild;
|
import cc.fascinated.bat.model.BatGuild;
|
||||||
import cc.fascinated.bat.model.BatUser;
|
import cc.fascinated.bat.model.BatUser;
|
||||||
|
import cc.fascinated.bat.model.DiscordMessage;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
|
||||||
@ -11,7 +12,9 @@ import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameE
|
|||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||||
import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent;
|
import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent;
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -36,11 +39,14 @@ public class EventService extends ListenerAdapter {
|
|||||||
public static final Set<EventListener> LISTENERS = new HashSet<>();
|
public static final Set<EventListener> LISTENERS = new HashSet<>();
|
||||||
private final GuildService guildService;
|
private final GuildService guildService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final DiscordMessageService discordMessageService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EventService(@NonNull GuildService guildService, @NonNull UserService userService, @NonNull ApplicationContext context) {
|
public EventService(@NonNull ApplicationContext context, @NonNull GuildService guildService, @NonNull UserService userService,
|
||||||
|
@NonNull DiscordMessageService discordMessageService) {
|
||||||
this.guildService = guildService;
|
this.guildService = guildService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
|
this.discordMessageService = discordMessageService;
|
||||||
DiscordService.JDA.addEventListener(this);
|
DiscordService.JDA.addEventListener(this);
|
||||||
|
|
||||||
context.getBeansOfType(EventListener.class).values().forEach(this::registerListeners);
|
context.getBeansOfType(EventListener.class).values().forEach(this::registerListeners);
|
||||||
@ -95,6 +101,30 @@ public class EventService extends ListenerAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageUpdate(@NotNull MessageUpdateEvent event) {
|
||||||
|
if (event.getAuthor().isBot()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BatGuild guild = guildService.getGuild(event.getGuild().getId());
|
||||||
|
BatUser user = userService.getUser(event.getAuthor().getId());
|
||||||
|
|
||||||
|
for (EventListener listener : LISTENERS) {
|
||||||
|
listener.onGuildMessageEdit(guild, user, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageDelete(@NotNull MessageDeleteEvent event) {
|
||||||
|
BatGuild guild = guildService.getGuild(event.getGuild().getId());
|
||||||
|
DiscordMessage message = discordMessageService.getMessage(event.getMessageId());
|
||||||
|
BatUser user = message == null ? null : userService.getUser(message.getAuthorId());
|
||||||
|
|
||||||
|
for (EventListener listener : LISTENERS) {
|
||||||
|
listener.onGuildMessageDelete(guild, user, message, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStringSelectInteraction(StringSelectInteractionEvent event) {
|
public void onStringSelectInteraction(StringSelectInteractionEvent event) {
|
||||||
if (event.getUser().isBot()) {
|
if (event.getUser().isBot()) {
|
||||||
|
@ -28,3 +28,10 @@ spring:
|
|||||||
uri: "mongodb://bat:p4$$w0rd@localhost:27017"
|
uri: "mongodb://bat:p4$$w0rd@localhost:27017"
|
||||||
database: "bat"
|
database: "bat"
|
||||||
auto-index-creation: true # Automatically create collection indexes
|
auto-index-creation: true # Automatically create collection indexes
|
||||||
|
|
||||||
|
# Redis - This is used for caching
|
||||||
|
redis:
|
||||||
|
host: "localhost"
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
auth: "" # Leave blank for no auth
|
Loading…
Reference in New Issue
Block a user