Skip to content

Commit

Permalink
Move custom pings to their own module
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Aug 28, 2024
1 parent 09ea9bd commit 2ce941a
Show file tree
Hide file tree
Showing 18 changed files with 240 additions and 136 deletions.
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ evaluationDependsOn(':config')
configurations {
library.extendsFrom(implementation)
module
runtimeClasspath.extendsFrom(module)
runtimeClasspath.extendsFrom(module, library)
}

dependencies {
Expand All @@ -103,7 +103,6 @@ dependencies {

implementation libs.bundles.graal
implementation libs.args4j
implementation libs.re2j
implementation libs.fastutil

implementation libs.semver
Expand Down
4 changes: 0 additions & 4 deletions config/src/sample/groovy/sample.camelot.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ camelot {
// auth = patAuthentication('ghp_')
}

module(CustomPings) {
pingThreadsChannel = 123
}

module(InfoChannels) {
auth = appAuthentication {
appId = '123'
Expand Down
13 changes: 13 additions & 0 deletions modules/custom-pings/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
id 'camelot-module'
}

camelotModule {
id = 'custom-pings'
}

dependencies {
library(libs.bundles.database)
library(libs.re2j)
library(libs.fastutil)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import groovy.transform.CompileStatic
*/
@CompileStatic
class CustomPings extends ModuleConfiguration {
/**
* The channel in which to create ping private threads if a member does not have DMs enabled.
*/
long pingThreadsChannel

/**
* The maximum amount of custom pings an user can have.
* {@code -1} means indefinite
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package net.neoforged.camelot.listener;
package net.neoforged.camelot.module.custompings;

import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
Expand All @@ -23,13 +23,11 @@
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.neoforged.camelot.BotMain;
import net.neoforged.camelot.module.CustomPingsModule;
import net.neoforged.camelot.module.custompings.db.Ping;
import net.neoforged.camelot.module.custompings.db.PingsDAO;
import net.neoforged.camelot.util.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.neoforged.camelot.Database;
import net.neoforged.camelot.db.schemas.Ping;
import net.neoforged.camelot.db.transactionals.PingsDAO;
import net.neoforged.camelot.util.Utils;

import java.util.List;
import java.util.Objects;
Expand All @@ -48,7 +46,7 @@ public class CustomPingListener implements EventListener {

public static void requestRefresh() {
synchronized (CACHE) {
final var newValue = Database.pings().withExtension(PingsDAO.class, PingsDAO::getAllPings);
final var newValue = BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, PingsDAO::getAllPings);
CACHE.clear();
CACHE.putAll(newValue);
}
Expand All @@ -73,72 +71,82 @@ private void sendPing(Message message, Ping ping) {
message.getGuild().retrieveMemberById(ping.user())
.flatMap(pinged -> canViewChannel(pinged, message.getGuildChannel()), pinged -> pinged.getUser().openPrivateChannel()
.flatMap(channel -> sendPingMessage(ping, message, channel))
.onErrorFlatMap(ex -> getPingThread(message.getJDA(), pinged.getIdLong()).flatMap(channel -> sendPingMessage(ping, message, channel))))
.queue(null, new ErrorHandler().handle(ErrorResponse.UNKNOWN_MEMBER, err -> {
.onErrorFlatMap(_ -> getPingThread(message.getJDA(), message.getGuildIdLong(), pinged.getIdLong()).flatMap(channel -> sendPingMessage(ping, message, channel))))
.queue(null, new ErrorHandler().handle(ErrorResponse.UNKNOWN_MEMBER, _ -> {
// User left the guild, dump their pings in the thread, then delete from database
final List<Ping> pings = Database.pings().withExtension(PingsDAO.class, db -> db.getAllPingsOf(ping.user(), message.getGuild().getIdLong()));
getPingThread(message.getJDA(), ping.user())
final List<Ping> pings = BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getAllPingsOf(ping.user(), message.getGuild().getIdLong()));
getPingThread(message.getJDA(), message.getGuildIdLong(), ping.user())
.flatMap(thread -> thread.sendMessage(MessageCreateData.fromEmbeds(new EmbedBuilder()
.setTitle("Custom pings dump")
.setFooter("User left the guild")
.setDescription(pings.stream()
.map(p -> p.id() + ". `" + p.regex().toString() + "` | " + p.message())
.collect(Collectors.joining("\n")))
.build())))
.queue($ -> Database.pings().useExtension(PingsDAO.class, db -> db.deletePingsOf(ping.user(), message.getGuild().getIdLong())));
.queue($ -> BotMain.getModule(CustomPingsModule.class).db().useExtension(PingsDAO.class, db -> db.deletePingsOf(ping.user(), message.getGuild().getIdLong())));
}));
}

private static RestAction<? extends MessageChannel> getPingThread(JDA jda, long memberId) {
final Long threadId = Database.pings().withExtension(PingsDAO.class, db -> db.getThread(memberId));
private static RestAction<? extends MessageChannel> getPingThread(JDA jda, long guildId, long memberId) {
final Long threadId = BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getThread(memberId, guildId));
if (threadId == null) {
return createNewThread(jda, memberId);
return createNewThread(jda, guildId, memberId);
} else {
final ThreadChannel channel = jda.getThreadChannelById(threadId);
if (channel != null) {
return new RestAction<>() {
@NotNull
@Override
public JDA getJDA() {
return channel.getJDA();
}

@NotNull
@Override
public RestAction<MessageChannel> setCheck(@Nullable BooleanSupplier checks) {
return this;
}

@Override
public void queue(@Nullable Consumer<? super MessageChannel> success, @Nullable Consumer<? super Throwable> failure) {
if (success != null) {
success.accept(channel);
}
}

@Override
public MessageChannel complete(boolean shouldQueue) throws RateLimitedException {
return channel;
}

@NotNull
@Override
public CompletableFuture<MessageChannel> submit(boolean shouldQueue) {
return CompletableFuture.completedFuture(channel);
}
};
return singleton(channel);
} else {
return createNewThread(jda, memberId);
var pingsChannel = jda.getChannelById(IThreadContainer.class, BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getPingThreadsChannel(guildId)).longValue());
//noinspection rawtypes, unchecked this is so dirty
return pingsChannel.retrieveArchivedPrivateJoinedThreadChannels()
.flatMap(threadChannels -> threadChannels.stream().filter(c -> c.getIdLong() == threadId)
.findFirst()
.map(CustomPingListener::singleton)
.orElseGet(() -> (RestAction<MessageChannel>) (RestAction) createNewThread(jda, guildId, memberId)));
}
}
}

private static RestAction<ThreadChannel> createNewThread(JDA jda, long memberId) {
return Objects.requireNonNull(jda.getChannelById(IThreadContainer.class, BotMain.getModule(CustomPingsModule.class).config().getPingThreadsChannel()))
private static RestAction<MessageChannel> singleton(MessageChannel channel) {
return new RestAction<>() {
@NotNull
@Override
public JDA getJDA() {
return channel.getJDA();
}

@NotNull
@Override
public RestAction<MessageChannel> setCheck(@Nullable BooleanSupplier checks) {
return this;
}

@Override
public void queue(@Nullable Consumer<? super MessageChannel> success, @Nullable Consumer<? super Throwable> failure) {
if (success != null) {
success.accept(channel);
}
}

@Override
public MessageChannel complete(boolean shouldQueue) throws RateLimitedException {
return channel;
}

@NotNull
@Override
public CompletableFuture<MessageChannel> submit(boolean shouldQueue) {
return CompletableFuture.completedFuture(channel);
}
};
}

private static RestAction<ThreadChannel> createNewThread(JDA jda, long guildId, long memberId) {
return Objects.requireNonNull(jda.getChannelById(IThreadContainer.class, BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getPingThreadsChannel(guildId)).longValue()))
.createThreadChannel("Custom ping notifications of " + memberId, true)
.setInvitable(false)
.setAutoArchiveDuration(ThreadChannel.AutoArchiveDuration.TIME_1_WEEK)
.onSuccess(channel -> Database.pings().useExtension(PingsDAO.class, db -> db.insertThread(memberId, channel.getIdLong())));
.onSuccess(channel -> BotMain.getModule(CustomPingsModule.class).db().useExtension(PingsDAO.class, db -> db.insertThread(memberId, guildId, channel.getIdLong())));
}

private static MessageCreateAction sendPingMessage(final Ping ping, final Message message, final MessageChannel channel) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package net.neoforged.camelot.commands.utility;
package net.neoforged.camelot.module.custompings;

import com.google.re2j.Pattern;
import com.google.re2j.PatternSyntaxException;
Expand All @@ -14,11 +14,9 @@
import net.dv8tion.jda.api.utils.messages.MessageEditBuilder;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import net.neoforged.camelot.BotMain;
import net.neoforged.camelot.Database;
import net.neoforged.camelot.commands.PaginatableCommand;
import net.neoforged.camelot.db.schemas.Ping;
import net.neoforged.camelot.db.transactionals.PingsDAO;
import net.neoforged.camelot.module.CustomPingsModule;
import net.neoforged.camelot.module.custompings.db.Ping;
import net.neoforged.camelot.module.custompings.db.PingsDAO;
import org.jetbrains.annotations.Nullable;

import java.util.List;
Expand Down Expand Up @@ -67,7 +65,7 @@ protected void execute(SlashCommandEvent event) {
return;
}

final var pingAmount = Database.pings().withExtension(PingsDAO.class,
final var pingAmount = BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class,
db -> db.getAllPingsOf(event.getUser().getIdLong(), event.getGuild().getIdLong()))
.size();

Expand All @@ -77,7 +75,7 @@ protected void execute(SlashCommandEvent event) {
return;
}

Database.pings().useExtension(PingsDAO.class, db -> db.insert(
BotMain.getModule(CustomPingsModule.class).db().useExtension(PingsDAO.class, db -> db.insert(
event.getGuild().getIdLong(), event.getUser().getIdLong(),
regex, event.getOption("message", "", OptionMapping::getAsString)
));
Expand All @@ -99,7 +97,7 @@ public Delete() {

@Override
protected void execute(SlashCommandEvent event) {
final Ping ping = Database.pings().withExtension(PingsDAO.class, db -> db.getPing(event.getOption("ping", 0, OptionMapping::getAsInt)));
final Ping ping = BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getPing(event.getOption("ping", 0, OptionMapping::getAsInt)));
if (ping == null) {
event.reply("Unknown ping!").setEphemeral(true).queue();
return;
Expand All @@ -109,7 +107,7 @@ protected void execute(SlashCommandEvent event) {
return;
}

Database.pings().useExtension(PingsDAO.class, db -> db.deletePing(ping.id()));
BotMain.getModule(CustomPingsModule.class).db().useExtension(PingsDAO.class, db -> db.deletePing(ping.id()));
event.reply("Ping deleted!").setEphemeral(true).queue();
}
}
Expand Down Expand Up @@ -138,7 +136,7 @@ public Data collectData(SlashCommandEvent event) {
return null;
}

return new Data(Database.pings().withExtension(PingsDAO.class, db -> db.getAllPingsOf(
return new Data(BotMain.getModule(CustomPingsModule.class).db().withExtension(PingsDAO.class, db -> db.getAllPingsOf(
target.getIdLong(), event.getGuild().getIdLong()
)));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package net.neoforged.camelot.module.custompings;

import com.google.auto.service.AutoService;
import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.command.SlashCommand;
import com.jagrosh.jdautilities.command.SlashCommandEvent;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.neoforged.camelot.Database;
import net.neoforged.camelot.config.module.CustomPings;
import net.neoforged.camelot.db.impl.PostCallbackDecorator;
import net.neoforged.camelot.module.BuiltInModule;
import net.neoforged.camelot.module.api.CamelotModule;
import net.neoforged.camelot.module.custompings.db.PingsDAO;

import java.util.Objects;

@AutoService(CamelotModule.class)
public class CustomPingsModule extends CamelotModule.WithDatabase<CustomPings> {
public static volatile boolean isMigrating;

public CustomPingsModule() {
super(CustomPings.class);
accept(BuiltInModule.DB_MIGRATION_CALLBACKS, builder -> builder
.add(BuiltInModule.DatabaseSource.PINGS, 4, stmt -> {
logger.info("Moving custom ping threads from pings.db to custom-pings.db");
var threads = stmt.executeQuery("select * from ping_threads");
db().useExtension(PingsDAO.class, db -> {
while (threads.next()) {
db.insertThread(threads.getLong(1), 0, threads.getLong(2));
}
});

logger.info("Moving custom pings from pings.db to custom-pings.db");
isMigrating = true;
var pings = stmt.executeQuery("select * from pings");
db().useExtension(PingsDAO.class, db -> {
while (pings.next()) {
db.insert(pings.getLong(2), pings.getLong(3), pings.getString(4), pings.getString(5));
}
});
isMigrating = false;

CustomPingListener.requestRefresh();
}));

accept(BuiltInModule.CONFIGURATION_COMMANDS, builder -> builder
.accept(new SlashCommand() {
{
name = "custom-pings-threads-channel";
help = "Sets the channel for custom pings threads in this guild to this channel";
}
@Override
protected void execute(SlashCommandEvent event) {
if (event.getChannel().getType() != ChannelType.TEXT) {
event.reply("This command can only be used in text channels.").setEphemeral(true).queue();
return;
}
if (!event.getGuild().getSelfMember().hasPermission(event.getTextChannel(), Permission.CREATE_PRIVATE_THREADS)) {
event.reply("I cannot create private threads in this channel!").setEphemeral(true).queue();
return;
}

db().useExtension(PingsDAO.class, db -> {
var old = Objects.requireNonNullElse(db.getPingThreadsChannel(event.getGuild().getIdLong()), 0L);
db.setPingThreadsChannel(event.getGuild().getIdLong(), event.getChannel().getIdLong());
event.reply("Set ping threads channel to this channel!" + (old != 0 ? " (was <#" + old + ">)" : "")).queue();
});
}
}));
}

@Override
public void init() {
super.init();
CustomPingListener.requestRefresh();
}

@Override
public String id() {
return "custom-pings";
}

@Override
public void registerCommands(CommandClientBuilder builder) {
builder.addSlashCommand(new CustomPingsCommand());
}

@Override
public void registerListeners(JDABuilder builder) {
builder.addEventListeners(new CustomPingListener());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package net.neoforged.camelot.db.schemas;
package net.neoforged.camelot.module.custompings.db;

import com.google.re2j.Pattern;
import org.jdbi.v3.core.mapper.RowMapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package net.neoforged.camelot.db.callback;
package net.neoforged.camelot.module.custompings.db;

import net.neoforged.camelot.db.api.ExecutionCallback;
import net.neoforged.camelot.db.transactionals.PingsDAO;
import net.neoforged.camelot.listener.CustomPingListener;
import net.neoforged.camelot.module.custompings.CustomPingListener;
import net.neoforged.camelot.module.custompings.CustomPingsModule;

public class PingsCallbacks {
@ExecutionCallback(methodName = "insert")
public static void onInsert(PingsDAO dao, long guild, long user, String regex, String message) {
CustomPingListener.requestRefresh();
if (!CustomPingsModule.isMigrating) {
CustomPingListener.requestRefresh();
}
}

@ExecutionCallback(methodName = "deletePing")
Expand Down
Loading

0 comments on commit 2ce941a

Please sign in to comment.