Skip to content

Commit 8217362

Browse files
Merge pull request #293 from Java-Discord/dynxsty/dih4jda
DIH4JDA, Sentry & Major Cleanup
2 parents cf3cd90 + 70ba555 commit 8217362

File tree

314 files changed

+8239
-9948
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

314 files changed

+8239
-9948
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ build/
55
/config.json
66
/config
77
/purgeArchives
8+
/logs
89

910
# Eclipse settings
1011
.classpath

Diff for: README.md

+37-52
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
# JavaBot
1+
# JavaBot — General Utility Bot for the [JavaDiscord Community](https://join.javadiscord.net/)
22

3-
General utility bot for the [JavaDiscord Community](https://join.javadiscord.net/)
3+
![Banner](https://user-images.githubusercontent.com/48297101/174893242-c8fc553a-e36b-4c5f-91d3-9c3bc659a7c9.png)
44

55
# Usage
66

77
To start up, run the bot once, and it will generate a `config` directory. Stop the bot, and set the up **all of the following values**:
88
- in `systems.json`
99
- `jdaBotToken` to your bot's token
10+
- (some `adminUsers` which, e.g., can manipulate the database)
1011
- in `{guildId}.json`
1112
- `moderation.logChannelId` to a channelId
1213
- `moderation.staffRoleId` to a roleId
@@ -15,65 +16,49 @@ To start up, run the bot once, and it will generate a `config` directory. Stop t
1516

1617
Note that this is just what is required for the bot to start. Certain features may require other values to be set.
1718

19+
# Configuration
1820

19-
# Commands
20-
Commands are defined in this bot using a `.yaml` configuration file located in `src/main/resources/commands`. The data in this file is transformed at startup time into an array of `net.javadiscord.javabot.command.data.slash_commands.SlashCommandConfig` objects using JSON deserialization.
21-
22-
These commands are then used by `net.javadiscord.javabot.command.InteractionHandler#registerSlashCommands(Guild)` to register the defined commands as Discord slash commands which become available to users in guilds and private messages with the bot.
23-
24-
**Each command MUST define a `handler` property, whose name is the fully-qualified class name of a `SlashCommand`.** When registering commands, the bot will look for such a class, and attempt to create a new instance of it using a no-args constructor. Therefore, make sure that your handler class has a no-args constructor.
25-
26-
### Privileges
27-
To specify that a command should only be allowed to be executed by certain people, you can specify a list of privileges. For example:
28-
```yaml
29-
- name: jam-admin
30-
description: Administrator actions for configuring the Java Jam.
31-
handler: net.javadiscord.javabot.systems.jam.JamAdminCommandHandler
32-
enabledByDefault: false
33-
privileges:
34-
- type: ROLE
35-
id: jam.adminRoleId
36-
- type: USER
37-
id: 235439851263098880
38-
```
39-
In this example, we define that the `jam-admin` command is first of all, *not enabled by default*, and also we say that anyone from the `jam.adminRoleId` role (as found using `Bot.config.getJam().getAdminRoleId()`). Additionally, we also say that the user whose id is `235439851263098880` is allowed to use this command. See `BotConfig#resolve(String)` for more information about how role names are resolved at runtime.
40-
41-
*Context-Commands work in almost the same way, follow the steps above but replace `SlashCommand` with `MessageContextCommand` or `UserContextCommand`.*
21+
The bot's configuration consists of a collection of simple JSON files:
22+
- `systems.json` contains global settings for the bot's core systems.
23+
- For every guild, a `{guildId}.json` file exists, which contains any guild-specific configuration settings.
4224

43-
### Autocomplete
44-
To enable Autocomplete for a certain Slash Command Option, head to the command's entry in the corresponding
45-
YAML-File and set the `autocomplete` value.
25+
At startup, the bot will initially start by loading just the global settings, and then when the Discord ready event is received, the bot will add configuration for each guild it's in, loading it from the matching JSON file, or creating a new file if needed.
4626

47-
```yaml
48-
- name: remove
49-
description: Removes a question from the queue.
50-
options:
51-
- name: id
52-
description: The id of the question to remove.
53-
required: true
54-
autocomplete: true
55-
type: INTEGER
56-
```
27+
# Commands
5728

58-
Now, you just need to implement `Autocompletable` in your handler class.
29+
We're using [DIH4JDA](https://github.com/DynxstyGIT/DIH4JDA) as our Command/Interaction framework, which makes it quite easy to add new commands.
5930

31+
[PingCommand.java](https://github.com/Java-Discord/JavaBot/blob/main/src/main/java/net/javadiscord/javabot/systems/commands/PingCommand.java)
6032
```java
61-
@Override
62-
public AutoCompleteCallbackAction handleAutocomplete(CommandAutoCompleteInteractionEvent event) {
63-
return switch (event.getSubcommandName()) {
64-
case "remove" -> ListQuestionsSubcommand.replyQuestions(event);
65-
default -> event.replyChoices();
66-
};
33+
/**
34+
* <h3>This class represents the /ping command.</h3>
35+
*/
36+
public class PingCommand extends SlashCommand {
37+
/**
38+
* The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}.
39+
*/
40+
public PingCommand() {
41+
setSlashCommandData(Commands.slash("ping", "Shows the bot's gateway ping.")
42+
.setGuildOnly(true)
43+
);
44+
}
45+
46+
@Override
47+
public void execute(@NotNull SlashCommandInteractionEvent event) {
48+
event.replyEmbeds(new EmbedBuilder()
49+
.setAuthor(event.getJDA().getGatewayPing() + "ms", null, event.getJDA().getSelfUser().getAvatarUrl())
50+
.setColor(Responses.Type.DEFAULT.getColor())
51+
.build()
52+
).queue();
53+
}
6754
}
6855
```
6956

70-
The `handleAutocomplete` method of your handler class now gets fired once someone is focusing any Slash Command Option that has the `autocomplete`
71-
property set to true.
57+
For more information on how this works, visit the [DIH4JDA Wiki!](https://github.com/DynxstyGIT/DIH4JDA/wiki)
7258

59+
# Credits
7360

74-
# Configuration
75-
The bot's configuration consists of a collection of simple JSON files:
76-
- `systems.json` contains global settings for the bot's core systems.
77-
- For every guild, a `{guildId}.json` file exists, which contains any guild-specific configuration settings.
61+
Inspiration we took from other communities:
7862

79-
At startup, the bot will initially start by loading just the global settings, and then when the Discord ready event is received, the bot will add configuration for each guild it's in, loading it from the matching JSON file, or creating a new file if needed.
63+
- We designed our [Help Channel System](https://github.com/Java-Discord/JavaBot/tree/main/src/main/java/net/javadiscord/javabot/systems/help) similar to the one on the [Python Discord](https://discord.gg/python).
64+
- [`/move-conversation`](https://github.com/Java-Discord/JavaBot/blob/main/src/main/java/net/javadiscord/javabot/systems/user_commands/MoveConversationCommand.java) is heavily inspired by the [Rust Programming Language Community Server](https://discord.gg/rust-lang-community)

Diff for: build.gradle.kts

+9-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ dependencies {
2222
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
2323
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
2424

25-
implementation("net.dv8tion:JDA:5.0.0-alpha.12")
25+
// DIH4JDA (Interaction Framework) (includes JDA (jda5.0.0-alpha.17))
26+
implementation("com.github.DynxstyGIT:DIH4JDA:f87f54eb42")
27+
2628
implementation("com.google.code.gson:gson:2.9.0")
2729
implementation("org.yaml:snakeyaml:1.30")
2830
implementation("com.google.re2j:re2j:1.6")
@@ -32,20 +34,23 @@ dependencies {
3234
implementation("com.mashape.unirest:unirest-java:1.4.9")
3335

3436
// H2 Database
35-
implementation("com.h2database:h2:1.4.200")
37+
implementation("com.h2database:h2:2.1.212")
3638
implementation("com.zaxxer:HikariCP:5.0.1")
3739

38-
// Quartz scheduler
40+
// Quartz Scheduler
3941
implementation("org.quartz-scheduler:quartz:2.3.2")
4042

4143
// Webhooks
42-
implementation("club.minnced:discord-webhooks:0.8.0")
44+
implementation("com.github.DynxstyGIT:discord-webhooks:74301a46a0")
4345

4446
// Lombok Annotations
4547
compileOnly("org.projectlombok:lombok:1.18.24")
4648
annotationProcessor("org.projectlombok:lombok:1.18.24")
4749
testCompileOnly("org.projectlombok:lombok:1.18.24")
4850
testAnnotationProcessor("org.projectlombok:lombok:1.18.24")
51+
52+
// Sentry
53+
implementation("io.sentry:sentry:6.3.0")
4954
}
5055

5156
tasks.withType<Jar> {

Diff for: src/main/java/net/javadiscord/javabot/Bot.java

+100-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package net.javadiscord.javabot;
22

3+
import com.dynxsty.dih4jda.DIH4JDA;
4+
import com.dynxsty.dih4jda.DIH4JDABuilder;
5+
import com.dynxsty.dih4jda.interactions.commands.RegistrationType;
36
import com.zaxxer.hikari.HikariDataSource;
7+
import io.sentry.Sentry;
48
import lombok.extern.slf4j.Slf4j;
59
import net.dv8tion.jda.api.JDA;
610
import net.dv8tion.jda.api.JDABuilder;
@@ -11,25 +15,41 @@
1115
import net.dv8tion.jda.api.utils.ChunkingFilter;
1216
import net.dv8tion.jda.api.utils.MemberCachePolicy;
1317
import net.dv8tion.jda.api.utils.cache.CacheFlag;
14-
import net.javadiscord.javabot.command.InteractionHandler;
1518
import net.javadiscord.javabot.data.config.BotConfig;
1619
import net.javadiscord.javabot.data.h2db.DbHelper;
20+
import net.javadiscord.javabot.data.h2db.commands.QuickMigrateSubcommand;
1721
import net.javadiscord.javabot.data.h2db.message_cache.MessageCache;
1822
import net.javadiscord.javabot.data.h2db.message_cache.MessageCacheListener;
1923
import net.javadiscord.javabot.listener.*;
24+
import net.javadiscord.javabot.systems.help.HelpChannelInteractionManager;
2025
import net.javadiscord.javabot.systems.help.HelpChannelListener;
2126
import net.javadiscord.javabot.systems.moderation.AutoMod;
22-
import net.javadiscord.javabot.systems.moderation.ServerLock;
27+
import net.javadiscord.javabot.systems.moderation.report.ReportManager;
28+
import net.javadiscord.javabot.systems.moderation.server_lock.ServerLockManager;
29+
import net.javadiscord.javabot.systems.qotw.commands.questions_queue.AddQuestionSubcommand;
30+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionInteractionManager;
31+
import net.javadiscord.javabot.systems.staff_commands.self_roles.SelfRoleInteractionManager;
32+
import net.javadiscord.javabot.systems.staff_commands.embeds.AddEmbedFieldSubcommand;
33+
import net.javadiscord.javabot.systems.staff_commands.embeds.CreateEmbedSubcommand;
34+
import net.javadiscord.javabot.systems.staff_commands.embeds.EditEmbedSubcommand;
2335
import net.javadiscord.javabot.systems.starboard.StarboardManager;
36+
import net.javadiscord.javabot.systems.staff_commands.tags.CustomTagManager;
37+
import net.javadiscord.javabot.systems.staff_commands.tags.commands.CreateCustomTagSubcommand;
38+
import net.javadiscord.javabot.systems.staff_commands.tags.commands.EditCustomTagSubcommand;
39+
import net.javadiscord.javabot.systems.user_commands.leaderboard.ExperienceLeaderboardSubcommand;
40+
import net.javadiscord.javabot.tasks.MetricsUpdater;
2441
import net.javadiscord.javabot.tasks.PresenceUpdater;
2542
import net.javadiscord.javabot.tasks.ScheduledTasks;
26-
import net.javadiscord.javabot.tasks.StatsUpdater;
27-
import net.javadiscord.javabot.util.ImageCacheUtils;
43+
import net.javadiscord.javabot.util.ExceptionLogger;
44+
import net.javadiscord.javabot.util.InteractionUtils;
45+
import org.jetbrains.annotations.NotNull;
2846
import org.quartz.SchedulerException;
2947

3048
import java.nio.file.Path;
3149
import java.time.ZoneOffset;
3250
import java.util.EnumSet;
51+
import java.util.List;
52+
import java.util.Map;
3353
import java.util.TimeZone;
3454
import java.util.concurrent.Executors;
3555
import java.util.concurrent.ScheduledExecutorService;
@@ -44,36 +64,44 @@ public class Bot {
4464
* The set of configuration properties that this bot uses.
4565
*/
4666
public static BotConfig config;
47-
/**
48-
* A reference to the slash command listener that's the main point of
49-
* interaction for users with this bot. It's marked as a publicly accessible
50-
* reference so that {@link InteractionHandler#registerCommands} can
51-
* be called wherever it's needed.
52-
*/
53-
public static InteractionHandler interactionHandler;
67+
5468
/**
5569
* An instance of {@link AutoMod}.
56-
* */
70+
*/
5771
public static AutoMod autoMod;
72+
73+
/**
74+
* A reference to the Bot's {@link DIH4JDA}.
75+
*/
76+
public static DIH4JDA dih4jda;
77+
5878
/**
5979
* The Bots {@link MessageCache}, which handles logging of deleted and edited messages.
6080
*/
6181
public static MessageCache messageCache;
82+
83+
/**
84+
* A reference to the Bot's {@link ServerLockManager}.
85+
*/
86+
public static ServerLockManager serverLockManager;
87+
88+
/**
89+
* A static reference to the {@link CustomTagManager} which handles and loads all registered Custom Commands.
90+
*/
91+
public static CustomTagManager customTagManager;
92+
6293
/**
6394
* A reference to the data source that provides access to the relational
6495
* database that this bot users for certain parts of the application. Use
6596
* this to obtain a connection and perform transactions.
6697
*/
6798
public static HikariDataSource dataSource;
99+
68100
/**
69101
* A general-purpose thread pool that can be used by the bot to execute
70102
* tasks outside the main event processing thread.
71103
*/
72104
public static ScheduledExecutorService asyncPool;
73-
/**
74-
* A reference to the Bot's {@link ImageCacheUtils}.
75-
*/
76-
public static ImageCacheUtils imageCache;
77105

78106
private Bot() {
79107
}
@@ -83,8 +111,8 @@ private Bot() {
83111
* <ol>
84112
* <li>Setting the time zone to UTC, to keep our sanity when working with times.</li>
85113
* <li>Loading the configuration JSON file.</li>
86-
* <li>Initializing the {@link InteractionHandler} listener (which reads command data from a YAML file).</li>
87114
* <li>Creating and configuring the {@link JDA} instance that enables the bot's Discord connectivity.</li>
115+
* <li>Initializing the {@link DIH4JDA} instance.</li>
88116
* <li>Adding event listeners to the bot.</li>
89117
* </ol>
90118
*
@@ -95,56 +123,93 @@ public static void main(String[] args) throws Exception {
95123
TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
96124
config = new BotConfig(Path.of("config"));
97125
dataSource = DbHelper.initDataSource(config);
98-
interactionHandler = new InteractionHandler();
99-
messageCache = new MessageCache();
100-
autoMod = new AutoMod();
101-
imageCache = new ImageCacheUtils();
102126
asyncPool = Executors.newScheduledThreadPool(config.getSystems().getAsyncPoolSize());
103-
var jda = JDABuilder.createDefault(config.getSystems().getJdaBotToken())
127+
autoMod = new AutoMod();
128+
JDA jda = JDABuilder.createDefault(config.getSystems().getJdaBotToken())
104129
.setStatus(OnlineStatus.DO_NOT_DISTURB)
105130
.setChunkingFilter(ChunkingFilter.ALL)
106131
.setMemberCachePolicy(MemberCachePolicy.ALL)
107132
.enableCache(CacheFlag.ACTIVITY)
108-
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES)
109-
.addEventListeners(interactionHandler, autoMod)
133+
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, GatewayIntent.MESSAGE_CONTENT)
134+
.addEventListeners(autoMod, new StateListener())
110135
.build();
111-
AllowedMentions.setDefaultMentions(EnumSet.of(Message.MentionType.ROLE, Message.MentionType.CHANNEL, Message.MentionType.USER, Message.MentionType.EMOTE));
112-
addEventListeners(jda);
136+
AllowedMentions.setDefaultMentions(EnumSet.of(Message.MentionType.ROLE, Message.MentionType.CHANNEL, Message.MentionType.USER, Message.MentionType.EMOJI));
137+
dih4jda = DIH4JDABuilder.setJDA(jda)
138+
.setCommandsPackage("net.javadiscord.javabot")
139+
.setDefaultCommandType(RegistrationType.GUILD)
140+
.build();
141+
customTagManager = new CustomTagManager(jda, dataSource);
142+
messageCache = new MessageCache();
143+
serverLockManager = new ServerLockManager(jda);
144+
addEventListeners(jda, dih4jda);
145+
addComponentHandler(dih4jda);
146+
// initialize Sentry
147+
Sentry.init(options -> {
148+
options.setDsn(config.getSystems().getSentryDsn());
149+
options.setTracesSampleRate(1.0);
150+
options.setDebug(false);
151+
});
113152
try {
114153
ScheduledTasks.init(jda);
115154
log.info("Initialized scheduled tasks.");
116155
} catch (SchedulerException e) {
156+
ExceptionLogger.capture(e, Bot.class.getSimpleName());
117157
log.error("Could not initialize all scheduled tasks.", e);
118158
jda.shutdown();
119159
}
120160
}
121161

122162
/**
123-
* Adds all the bot's event listeners to the JDA instance, except for the
124-
* main {@link InteractionHandler} listener and {@link AutoMod}.
163+
* Adds all the bot's event listeners to the JDA instance, except for
164+
* the {@link AutoMod} instance.
125165
*
126-
* @param jda The JDA bot instance to add listeners to.
166+
* @param jda The JDA bot instance to add listeners to.
167+
* @param dih4jda The {@link DIH4JDA} instance.
127168
*/
128-
private static void addEventListeners(JDA jda) {
169+
private static void addEventListeners(@NotNull JDA jda, @NotNull DIH4JDA dih4jda) {
129170
jda.addEventListener(
171+
serverLockManager,
172+
PresenceUpdater.standardActivities(),
130173
new MessageCacheListener(),
131174
new GitHubLinkListener(),
132175
new MessageLinkListener(),
133176
new GuildJoinListener(),
134-
new ServerLock(jda),
135177
new UserLeaveListener(),
136-
new StartupListener(),
137-
PresenceUpdater.standardActivities(),
138-
new StatsUpdater(),
178+
new MetricsUpdater(),
139179
new SuggestionListener(),
140180
new StarboardManager(),
141-
new InteractionListener(),
142181
new HelpChannelListener(),
143182
new ShareKnowledgeVoteListener(),
144183
new JobChannelVoteListener(),
145184
new PingableNameListener(),
146185
new HugListener()
147186
);
187+
dih4jda.addListener(new DIH4JDAListener());
188+
}
189+
190+
private static void addComponentHandler(@NotNull DIH4JDA dih4jda) {
191+
dih4jda.addButtonHandlers(Map.of(
192+
List.of("experience-leaderboard"), new ExperienceLeaderboardSubcommand(),
193+
List.of("utils"), new InteractionUtils(),
194+
List.of("resolve-report"), new ReportManager(),
195+
List.of("self-role"), new SelfRoleInteractionManager(),
196+
List.of("qotw-submission"), new SubmissionInteractionManager(),
197+
List.of("help-channel", "help-thank"), new HelpChannelInteractionManager()
198+
));
199+
dih4jda.addModalHandlers(Map.of(
200+
List.of("qotw-add-question"), new AddQuestionSubcommand(),
201+
List.of("embed-create"), new CreateEmbedSubcommand(),
202+
List.of(EditEmbedSubcommand.EDIT_EMBED_ID), new EditEmbedSubcommand(),
203+
List.of("embed-addfield"), new AddEmbedFieldSubcommand(),
204+
List.of("quick-migrate"), new QuickMigrateSubcommand(),
205+
List.of("report"), new ReportManager(),
206+
List.of("self-role"), new SelfRoleInteractionManager(),
207+
List.of("tag-create"), new CreateCustomTagSubcommand(),
208+
List.of("tag-edit"), new EditCustomTagSubcommand()
209+
));
210+
dih4jda.addSelectMenuHandlers(Map.of(
211+
List.of("qotw-submission-select"), new SubmissionInteractionManager()
212+
));
148213
}
149214
}
150215

0 commit comments

Comments
 (0)