Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit 417fe27

Browse files
committed
Added new schema stuff and some more cleanup.
1 parent 12445a6 commit 417fe27

9 files changed

Lines changed: 226 additions & 53 deletions

File tree

‎build.gradle‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ dependencies {
2222
implementation 'org.yaml:snakeyaml:1.29'
2323
implementation 'com.google.code.gson:gson:2.8.9'
2424

25+
implementation 'org.openpnp:opencv:4.5.1-2'
26+
2527
// Persistence Dependencies
2628
implementation 'org.mongodb:mongodb-driver:3.12.10'
2729
implementation 'com.h2database:h2:1.4.200'

‎src/main/java/net/javadiscord/javabot2/Bot.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private Bot() {}
6767
*/
6868
public static void main(String[] args) {
6969
TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
70+
nu.pattern.OpenCV.loadShared();
7071
initDataSources();
7172
asyncPool = Executors.newScheduledThreadPool(config.getSystems().getAsyncPoolSize());
7273
DiscordApi api = new DiscordApiBuilder()
@@ -78,7 +79,7 @@ public static void main(String[] args) {
7879
SlashCommandListener commandListener = new SlashCommandListener(
7980
api,
8081
args.length > 0 && args[0].equalsIgnoreCase("--register-commands"),
81-
"commands/moderation.yaml"
82+
"commands/moderation.yaml", "commands/activity.yaml"
8283
);
8384
api.addSlashCommandCreateListener(commandListener);
8485
initScheduledTasks(api);
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package net.javadiscord.javabot2.command;
2+
3+
import org.javacord.api.interaction.SlashCommandInteraction;
4+
import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder;
5+
6+
import java.util.Collections;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
10+
/**
11+
* Abstract command handler which is useful for commands which consist of lots
12+
* of subcommands. A child class will supply a map of subcommand handlers, so
13+
* that this parent handler can do the logic of finding the right subcommand to
14+
* invoke depending on the event received.
15+
*/
16+
public class DelegatingCommandHandler implements SlashCommandHandler {
17+
private final Map<String, SlashCommandHandler> subcommandHandlers;
18+
private final Map<String, SlashCommandHandler> subcommandGroupHandlers;
19+
20+
/**
21+
* Constructs the handler with an already-initialized map of subcommands.
22+
* @param subcommandHandlers The map of subcommands to use.
23+
*/
24+
public DelegatingCommandHandler(Map<String, SlashCommandHandler> subcommandHandlers) {
25+
this.subcommandHandlers = subcommandHandlers;
26+
this.subcommandGroupHandlers = new HashMap<>();
27+
}
28+
29+
/**
30+
* Constructs the handler with an empty map, which subcommands can be added
31+
* to via {@link DelegatingCommandHandler#addSubcommand(String, SlashCommandHandler)}.
32+
*/
33+
public DelegatingCommandHandler() {
34+
this.subcommandHandlers = new HashMap<>();
35+
this.subcommandGroupHandlers = new HashMap<>();
36+
}
37+
38+
/**
39+
* Gets an unmodifiable map of the subcommand handlers this delegating
40+
* handler has registered.
41+
* @return An unmodifiable map containing all registered subcommands.
42+
*/
43+
public Map<String, SlashCommandHandler> getSubcommandHandlers() {
44+
return Collections.unmodifiableMap(this.subcommandHandlers);
45+
}
46+
47+
/**
48+
* Gets an unmodifiable map of the subcommand group handlers that this
49+
* handler has registered.
50+
* @return An unmodifiable map containing all registered group handlers.
51+
*/
52+
public Map<String, SlashCommandHandler> getSubcommandGroupHandlers() {
53+
return Collections.unmodifiableMap(this.subcommandGroupHandlers);
54+
}
55+
56+
/**
57+
* Adds a subcommand to this handler.
58+
* @param name The name of the subcommand. <em>This is case-sensitive.</em>
59+
* @param handler The handler that will be called to handle subcommands with
60+
* the given name.
61+
* @throws UnsupportedOperationException If this handler was initialized
62+
* with an unmodifiable map of subcommand handlers.
63+
*/
64+
protected void addSubcommand(String name, SlashCommandHandler handler) {
65+
this.subcommandHandlers.put(name, handler);
66+
}
67+
68+
/**
69+
* Adds a subcommand group handler to this handler.
70+
* @param name The name of the subcommand group. <em>This is case-sensitive.</em>
71+
* @param handler The handler that will be called to handle commands within
72+
* the given subcommand's name.
73+
* @throws UnsupportedOperationException If this handler was initialized
74+
* with an unmodifiable map of subcommand group handlers.
75+
*/
76+
protected void addSubcommandGroup(String name, SlashCommandHandler handler) {
77+
this.subcommandGroupHandlers.put(name, handler);
78+
}
79+
80+
/**
81+
* Handles the case where the main command is called without any subcommand.
82+
* @param interaction The event.
83+
* @return The reply action that is sent to the user.
84+
*/
85+
protected InteractionImmediateResponseBuilder handleNonSubcommand(SlashCommandInteraction interaction) {
86+
return Responses.warning(interaction, "Missing Subcommand", "Please specify a subcommand.");
87+
}
88+
89+
/**
90+
* Handles a slash command interaction.
91+
*
92+
* @param interaction The interaction.
93+
* @return An immediate response to the interaction.
94+
* @throws ResponseException If an error occurs while handling the event.
95+
*/
96+
@Override
97+
public InteractionImmediateResponseBuilder handle(SlashCommandInteraction interaction) throws ResponseException {
98+
// First we check if the event has specified a subcommand group, and if we have a group handler for it.
99+
var subCommandOption = interaction.getOptionByIndex(0);
100+
if (subCommandOption.isPresent() && subCommandOption.get().isSubcommandOrGroup()) {
101+
SlashCommandHandler groupHandler = this.getSubcommandGroupHandlers().get(subCommandOption.get().getName());
102+
if (groupHandler != null) return groupHandler.handle(interaction);
103+
104+
}
105+
return Responses.warning(interaction, "Delegating command handler is not yet implemented.");
106+
107+
// if (interaction.getCommandName() != null) {
108+
// SlashCommandHandler groupHandler = this.getSubcommandGroupHandlers().get(event.getSubcommandGroup());
109+
// if (groupHandler != null) {
110+
// return groupHandler.handle(event);
111+
// }
112+
// }
113+
// // If the event doesn't have a subcommand group, or no handler was found for the group, we just move on to the subcommand.
114+
// if (event.getSubcommandName() == null) {
115+
// return this.handleNonSubcommand(event);
116+
// } else {
117+
// SlashCommandHandler handler = this.getSubcommandHandlers().get(event.getSubcommandName());
118+
// if (handler != null) {
119+
// return handler.handle(event);
120+
// } else {
121+
// return Responses.warning(event, "Unknown Subcommand", "The subcommand you entered could not be found.");
122+
// }
123+
// }
124+
}
125+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package net.javadiscord.javabot2.db;
2+
3+
import net.javadiscord.javabot2.Bot;
4+
5+
import java.sql.Connection;
6+
import java.sql.SQLException;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.function.Function;
9+
10+
/**
11+
* Utility that provides some convenience methods for performing database
12+
* actions.
13+
*/
14+
public class DbActions {
15+
// Hide the constructor.
16+
private DbActions () {}
17+
18+
/**
19+
* Does an asynchronous database action using the bot's async pool.
20+
* @param consumer The consumer that will use a connection.
21+
* @return A future that completes when the action is complete.
22+
*/
23+
public static CompletableFuture<Void> doAction(ConnectionConsumer consumer) {
24+
CompletableFuture<Void> future = new CompletableFuture<>();
25+
Bot.asyncPool.submit(() -> {
26+
try (var c = Bot.hikariDataSource.getConnection()) {
27+
consumer.consume(c);
28+
future.complete(null);
29+
} catch (SQLException e) {
30+
future.completeExceptionally(e);
31+
}
32+
});
33+
return future;
34+
}
35+
36+
/**
37+
* Does an asynchronous database action using the bot's async pool, and
38+
* wraps access to the connection behind a data access object that can be
39+
* built using the provided dao constructor.
40+
* @param daoConstructor A function to build a DAO using a connection.
41+
* @param consumer The consumer that does something with the DAO.
42+
* @param <T> The type of data access object. Usually some kind of repository.
43+
* @return A future that completes when the action is complete.
44+
*/
45+
public static <T> CompletableFuture<Void> doDaoAction(Function<Connection, T> daoConstructor, DaoConsumer<T> consumer) {
46+
CompletableFuture<Void> future = new CompletableFuture<>();
47+
Bot.asyncPool.submit(() -> {
48+
try (var c = Bot.hikariDataSource.getConnection()) {
49+
var dao = daoConstructor.apply(c);
50+
consumer.consume(dao);
51+
future.complete(null);
52+
} catch (SQLException e) {
53+
future.completeExceptionally(e);
54+
}
55+
});
56+
return future;
57+
}
58+
}

‎src/main/java/net/javadiscord/javabot2/db/DbHelper.java‎

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,15 @@
33
import com.zaxxer.hikari.HikariConfig;
44
import com.zaxxer.hikari.HikariDataSource;
55
import lombok.extern.slf4j.Slf4j;
6-
import net.javadiscord.javabot2.Bot;
76
import net.javadiscord.javabot2.config.BotConfig;
87
import org.h2.tools.Server;
98

109
import java.io.IOException;
1110
import java.io.InputStream;
1211
import java.nio.file.Files;
1312
import java.nio.file.Path;
14-
import java.sql.Connection;
1513
import java.sql.SQLException;
1614
import java.util.Arrays;
17-
import java.util.concurrent.CompletableFuture;
18-
import java.util.function.Function;
1915
import java.util.regex.Pattern;
2016

2117
/**
@@ -65,47 +61,6 @@ public static HikariDataSource initDataSource(BotConfig config) {
6561
return ds;
6662
}
6763

68-
/**
69-
* Does an asynchronous database action using the bot's async pool.
70-
* @param consumer The consumer that will use a connection.
71-
* @return A future that completes when the action is complete.
72-
*/
73-
public static CompletableFuture<Void> doDbAction(ConnectionConsumer consumer) {
74-
CompletableFuture<Void> future = new CompletableFuture<>();
75-
Bot.asyncPool.submit(() -> {
76-
try (var c = Bot.hikariDataSource.getConnection()) {
77-
consumer.consume(c);
78-
future.complete(null);
79-
} catch (SQLException e) {
80-
future.completeExceptionally(e);
81-
}
82-
});
83-
return future;
84-
}
85-
86-
/**
87-
* Does an asynchronous database action using the bot's async pool, and
88-
* wraps access to the connection behind a data access object that can be
89-
* built using the provided dao constructor.
90-
* @param daoConstructor A function to build a DAO using a connection.
91-
* @param consumer The consumer that does something with the DAO.
92-
* @param <T> The type of data access object. Usually some kind of repository.
93-
* @return A future that completes when the action is complete.
94-
*/
95-
public static <T> CompletableFuture<Void> doDaoAction(Function<Connection, T> daoConstructor, DaoConsumer<T> consumer) {
96-
CompletableFuture<Void> future = new CompletableFuture<>();
97-
Bot.asyncPool.submit(() -> {
98-
try (var c = Bot.hikariDataSource.getConnection()) {
99-
var dao = daoConstructor.apply(c);
100-
consumer.consume(dao);
101-
future.complete(null);
102-
} catch (SQLException e) {
103-
future.completeExceptionally(e);
104-
}
105-
});
106-
return future;
107-
}
108-
10964
private static boolean shouldInitSchema(String jdbcUrl) {
11065
var p = Pattern.compile("jdbc:h2:tcp://localhost:\\d+/(.*)");
11166
var m = p.matcher(jdbcUrl);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* The "activity" system contains commands and services pertaining to events and
3+
* activities that the server manages.
4+
*/
5+
package net.javadiscord.javabot2.systems.activity;

‎src/main/java/net/javadiscord/javabot2/systems/moderation/ModerationService.java‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import net.javadiscord.javabot2.Bot;
66
import net.javadiscord.javabot2.command.ResponseException;
77
import net.javadiscord.javabot2.config.guild.ModerationConfig;
8-
import net.javadiscord.javabot2.db.DbHelper;
8+
import net.javadiscord.javabot2.db.DbActions;
99
import net.javadiscord.javabot2.systems.moderation.dao.MuteRepository;
1010
import net.javadiscord.javabot2.systems.moderation.dao.WarnRepository;
1111
import net.javadiscord.javabot2.systems.moderation.model.Mute;
@@ -73,8 +73,7 @@ public ModerationService(SlashCommandInteraction interaction) throws ResponseExc
7373
* @return A future that completes when all warn operations are complete.
7474
*/
7575
public CompletableFuture<Void> warn(User user, WarnSeverity severity, String reason, User warnedBy, ServerTextChannel channel, boolean quiet) {
76-
return DbHelper.doDbAction(con -> {
77-
var repo = new WarnRepository(con);
76+
return DbActions.doDaoAction(WarnRepository::new, repo -> {
7877
var warn = repo.insert(new Warn(user.getId(), warnedBy.getId(), severity, reason));
7978
LocalDateTime cutoff = LocalDateTime.now().minusDays(config.getWarnTimeoutDays());
8079
int totalWeight = repo.getTotalSeverityWeight(user.getId(), cutoff);
@@ -97,7 +96,7 @@ public CompletableFuture<Void> warn(User user, WarnSeverity severity, String rea
9796
* @return A future that completes when the warns have been cleared.
9897
*/
9998
public CompletableFuture<Void> clearWarns(User user, User clearedBy) {
100-
return DbHelper.doDaoAction(WarnRepository::new, dao -> {
99+
return DbActions.doDaoAction(WarnRepository::new, dao -> {
101100
dao.discardAll(user.getId());
102101
var embed = buildClearWarnsEmbed(user, clearedBy);
103102
user.openPrivateChannel().thenAcceptAsync(pc -> pc.sendMessage(embed));
@@ -142,7 +141,7 @@ public CompletableFuture<Void> ban(User user, String reason, User bannedBy, Serv
142141
* @return A future that completes when muting is done.
143142
*/
144143
public CompletableFuture<Void> mute(User user, String reason, User mutedBy, Duration duration, ServerTextChannel channel, boolean quiet) {
145-
return DbHelper.doDbAction(con -> {
144+
return DbActions.doAction(con -> {
146145
con.setAutoCommit(false);
147146
var repo = new MuteRepository(con);
148147
var embed = buildMuteEmbed(user, reason, duration, mutedBy);
@@ -193,7 +192,7 @@ public CompletableFuture<Void> mute(User user, String reason, User mutedBy, Dura
193192
* @return A future that completes when the user is unmuted.
194193
*/
195194
public CompletableFuture<Void> unmute(User user, User unmutedBy) {
196-
return DbHelper.doDbAction(con -> {
195+
return DbActions.doAction(con -> {
197196
var repo = new MuteRepository(con);
198197
repo.discardAllActive(user.getId());
199198
user.removeRole(config.getMuteRole());
@@ -208,7 +207,7 @@ public CompletableFuture<Void> unmute(User user, User unmutedBy) {
208207
* @return A future that completes when all expired mutes have been processed.
209208
*/
210209
public CompletableFuture<Void> unmuteExpired() {
211-
return DbHelper.doDbAction(con -> {
210+
return DbActions.doAction(con -> {
212211
con.setAutoCommit(false);
213212
var repo = new MuteRepository(con);
214213
ServerUpdater updater = new ServerUpdater(config.getGuild());

‎src/main/resources/commands/activity.yaml‎

Whitespace-only changes.

‎src/main/resources/db/schema.sql‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
-- Moderation Tables
2+
13
CREATE TABLE warn (
24
id BIGINT PRIMARY KEY AUTO_INCREMENT,
35
user_id BIGINT NOT NULL,
@@ -18,3 +20,29 @@ CREATE TABLE mute (
1820
ends_at TIMESTAMP(0) NOT NULL,
1921
discarded BOOL NOT NULL DEFAULT FALSE
2022
);
23+
24+
-- Activity Tables
25+
26+
CREATE TABLE qotw_question (
27+
id BIGINT PRIMARY KEY AUTO_INCREMENT,
28+
created_by BIGINT NOT NULL,
29+
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
30+
question VARCHAR(1024) NOT NULL,
31+
priority INT NOT NULL DEFAULT 0,
32+
active BOOL NOT NULL DEFAULT FALSE,
33+
activated_at TIMESTAMP(0) NULL DEFAULT NULL,
34+
used BOOL NOT NULL DEFAULT FALSE
35+
);
36+
37+
CREATE TABLE qotw_answer (
38+
id BIGINT PRIMARY KEY AUTO_INCREMENT,
39+
created_by BIGINT NOT NULL,
40+
created_at TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
41+
answer CLOB NOT NULL,
42+
question_id BIGINT NOT NULL,
43+
accepted BOOL NULL DEFAULT NULL,
44+
FOREIGN KEY (question_id) REFERENCES qotw_question(id)
45+
ON UPDATE CASCADE ON DELETE CASCADE
46+
);
47+
48+

0 commit comments

Comments
 (0)