Skip to content

Commit 19865d5

Browse files
Merge pull request #361 from danthe1st/springify
Use Spring's Dependency Injection inside bot logic
2 parents 4a25ebe + 934c4f8 commit 19865d5

File tree

163 files changed

+3060
-2471
lines changed

Some content is hidden

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

163 files changed

+3060
-2471
lines changed

build.gradle.kts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.*
44
plugins {
55
java
66
id("com.github.johnrengelman.shadow") version "7.1.2"
7+
id("org.springframework.boot") version "2.7.3"
78
id("io.spring.dependency-management") version "1.0.11.RELEASE"
89
checkstyle
910
}
@@ -29,8 +30,8 @@ dependencies {
2930
compileOnly("org.jetbrains:annotations:23.0.0")
3031

3132
// DIH4JDA (Interaction Framework) & JDA
32-
implementation("com.github.DynxstyGIT:DIH4JDA:c8f7928efc")
33-
implementation("com.github.DV8FromTheWorld:JDA:86af73d377") {
33+
implementation("com.github.DynxstyGIT:DIH4JDA:9e02a171e8")
34+
implementation("net.dv8tion:JDA:5.0.0-alpha.21") {
3435
exclude(module = "opus-java")
3536
}
3637

@@ -49,9 +50,6 @@ dependencies {
4950
implementation("com.h2database:h2:2.1.212")
5051
implementation("com.zaxxer:HikariCP:5.0.1")
5152

52-
// Quartz Scheduler
53-
implementation("org.quartz-scheduler:quartz:2.3.2")
54-
5553
// Webhooks
5654
implementation("com.github.DynxstyGIT:discord-webhooks:74301a46a0")
5755

@@ -64,8 +62,9 @@ dependencies {
6462
// Sentry
6563
implementation("io.sentry:sentry:6.3.0")
6664

67-
// Spring Boot
68-
implementation("org.springframework.boot:spring-boot-starter-web:2.7.0")
65+
// Spring
66+
implementation("org.springframework.boot:spring-boot-starter-web")
67+
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
6968
}
7069

7170
tasks.withType<Jar> {
Lines changed: 79 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,90 @@
11
package net.javadiscord.javabot;
22

3+
import java.time.ZoneOffset;
4+
import java.util.Arrays;
5+
import java.util.EnumSet;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.TimeZone;
10+
11+
import javax.annotation.PostConstruct;
12+
13+
import org.springframework.boot.SpringApplication;
14+
import org.springframework.boot.autoconfigure.SpringBootApplication;
15+
import org.springframework.context.ApplicationContext;
16+
import org.springframework.context.annotation.ComponentScan;
17+
import org.springframework.context.annotation.FilterType;
18+
import org.springframework.scheduling.annotation.EnableScheduling;
19+
320
import com.dynxsty.dih4jda.DIH4JDA;
4-
import com.dynxsty.dih4jda.DIH4JDABuilder;
5-
import com.dynxsty.dih4jda.DIH4JDALogger;
621
import com.dynxsty.dih4jda.interactions.commands.ContextCommand;
7-
import com.dynxsty.dih4jda.interactions.commands.RegistrationType;
822
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
9-
import com.zaxxer.hikari.HikariDataSource;
23+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand.Subcommand;
24+
import com.dynxsty.dih4jda.interactions.components.ButtonHandler;
25+
import com.dynxsty.dih4jda.interactions.components.ModalHandler;
26+
import com.dynxsty.dih4jda.interactions.components.SelectMenuHandler;
27+
1028
import io.sentry.Sentry;
11-
import lombok.Getter;
12-
import lombok.extern.slf4j.Slf4j;
13-
import net.dv8tion.jda.api.JDA;
14-
import net.dv8tion.jda.api.JDABuilder;
15-
import net.dv8tion.jda.api.OnlineStatus;
29+
import lombok.RequiredArgsConstructor;
1630
import net.dv8tion.jda.api.entities.Message;
17-
import net.dv8tion.jda.api.requests.GatewayIntent;
18-
import net.dv8tion.jda.api.utils.ChunkingFilter;
19-
import net.dv8tion.jda.api.utils.MemberCachePolicy;
20-
import net.dv8tion.jda.api.utils.cache.CacheFlag;
31+
import net.dv8tion.jda.api.hooks.ListenerAdapter;
2132
import net.dv8tion.jda.api.utils.messages.MessageRequest;
2233
import net.javadiscord.javabot.data.config.BotConfig;
23-
import net.javadiscord.javabot.data.h2db.DbHelper;
24-
import net.javadiscord.javabot.data.h2db.commands.QuickMigrateSubcommand;
25-
import net.javadiscord.javabot.data.h2db.message_cache.MessageCache;
26-
import net.javadiscord.javabot.data.h2db.message_cache.MessageCacheListener;
27-
import net.javadiscord.javabot.listener.*;
28-
import net.javadiscord.javabot.systems.help.HelpChannelInteractionManager;
29-
import net.javadiscord.javabot.systems.help.HelpChannelListener;
30-
import net.javadiscord.javabot.systems.help.forum.ForumHelpListener;
31-
import net.javadiscord.javabot.systems.help.forum.ForumHelpManager;
32-
import net.javadiscord.javabot.systems.moderation.AutoMod;
33-
import net.javadiscord.javabot.systems.moderation.report.ReportManager;
34-
import net.javadiscord.javabot.systems.moderation.server_lock.ServerLockManager;
35-
import net.javadiscord.javabot.systems.qotw.commands.questions_queue.AddQuestionSubcommand;
36-
import net.javadiscord.javabot.systems.qotw.commands.view.QOTWQuerySubcommand;
37-
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionInteractionManager;
38-
import net.javadiscord.javabot.systems.staff_commands.embeds.AddEmbedFieldSubcommand;
39-
import net.javadiscord.javabot.systems.staff_commands.embeds.CreateEmbedSubcommand;
40-
import net.javadiscord.javabot.systems.staff_commands.embeds.EditEmbedSubcommand;
41-
import net.javadiscord.javabot.systems.staff_commands.self_roles.SelfRoleInteractionManager;
42-
import net.javadiscord.javabot.systems.staff_commands.tags.CustomTagManager;
43-
import net.javadiscord.javabot.systems.staff_commands.tags.commands.CreateCustomTagSubcommand;
44-
import net.javadiscord.javabot.systems.staff_commands.tags.commands.EditCustomTagSubcommand;
45-
import net.javadiscord.javabot.systems.starboard.StarboardManager;
46-
import net.javadiscord.javabot.systems.user_commands.leaderboard.ExperienceLeaderboardSubcommand;
47-
import net.javadiscord.javabot.tasks.MetricsUpdater;
34+
import net.javadiscord.javabot.systems.AutoDetectableComponentHandler;
4835
import net.javadiscord.javabot.tasks.PresenceUpdater;
49-
import net.javadiscord.javabot.tasks.ScheduledTasks;
5036
import net.javadiscord.javabot.util.ExceptionLogger;
51-
import net.javadiscord.javabot.util.InteractionUtils;
52-
import org.jetbrains.annotations.NotNull;
53-
import org.quartz.SchedulerException;
54-
import org.springframework.beans.factory.annotation.Autowired;
55-
import org.springframework.boot.SpringApplication;
56-
import org.springframework.boot.autoconfigure.SpringBootApplication;
57-
import org.springframework.context.annotation.ComponentScan;
58-
import org.springframework.context.annotation.FilterType;
59-
60-
import java.nio.file.Path;
61-
import java.time.ZoneOffset;
62-
import java.util.EnumSet;
63-
import java.util.List;
64-
import java.util.Map;
65-
import java.util.TimeZone;
66-
import java.util.concurrent.Executors;
67-
import java.util.concurrent.ScheduledExecutorService;
6837

6938
/**
7039
* The main class where the bot is initialized.
7140
*/
72-
@Slf4j
7341
@SpringBootApplication
74-
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SlashCommand.class, ContextCommand.class}))
42+
@ComponentScan(
43+
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { SlashCommand.class, ContextCommand.class, ListenerAdapter.class, Subcommand.class }),
44+
excludeFilters = @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, classes = PresenceUpdater.class)
45+
)
46+
@EnableScheduling
47+
@RequiredArgsConstructor
7548
public class Bot {
7649

77-
@Getter
78-
private static BotConfig config;
79-
80-
@Getter
81-
private static AutoMod autoMod;
50+
private final DIH4JDA dih4jda;
51+
private final BotConfig config;
52+
private final List<SlashCommand> commands;
53+
private final List<ContextCommand> contextCommands;
54+
private final List<ListenerAdapter> listeners;
55+
private final ApplicationContext ctx;
8256

83-
@Getter
84-
private static DIH4JDA dih4jda;
8557

86-
@Getter
87-
private static MessageCache messageCache;
88-
89-
@Getter
90-
private static ServerLockManager serverLockManager;
91-
92-
@Getter
93-
private static CustomTagManager customTagManager;
94-
95-
@Getter
96-
private static HikariDataSource dataSource;
97-
98-
@Getter
99-
private static ScheduledExecutorService asyncPool;
58+
private void addEventListeners(final List<ListenerAdapter> listeners) {
59+
for (ListenerAdapter listener : listeners) {
60+
dih4jda.getJDA().addEventListener(listener);
61+
}
62+
dih4jda.getJDA().addEventListener(dih4jda);
63+
}
10064

10165
/**
102-
* The constructor of this class, which also adds all {@link SlashCommand} and
103-
* {@link ContextCommand} to the {@link DIH4JDA} instance.
104-
*
105-
* @param commands The {@link Autowired} list of {@link SlashCommand}s.
106-
* @param contexts The {@link Autowired} list of {@link ContextCommand}s.
66+
* Initializes Sentry, interactions and listeners.
10767
*/
108-
@Autowired
109-
public Bot(final List<SlashCommand> commands, final List<ContextCommand> contexts) {
68+
@PostConstruct
69+
public void init() {
70+
Sentry.init(options -> {
71+
options.setDsn(config.getSystems().getSentryDsn());
72+
options.setTracesSampleRate(1.0);
73+
options.setDebug(false);
74+
});
11075
if (!commands.isEmpty()) {
111-
getDih4jda().addSlashCommands(commands.toArray(SlashCommand[]::new));
76+
dih4jda.addSlashCommands(commands.toArray(SlashCommand[]::new));
11277
}
113-
if (!contexts.isEmpty()) {
114-
getDih4jda().addContextCommands(contexts.toArray(ContextCommand[]::new));
78+
if (!contextCommands.isEmpty()) {
79+
dih4jda.addContextCommands(contextCommands.toArray(ContextCommand[]::new));
11580
}
11681
try {
117-
getDih4jda().registerInteractions();
82+
dih4jda.registerInteractions();
11883
} catch (ReflectiveOperationException e) {
11984
ExceptionLogger.capture(e, getClass().getSimpleName());
12085
}
86+
addEventListeners(listeners);
87+
registerComponentHandlers(ctx);
12188
}
12289

12390
/**
@@ -135,100 +102,34 @@ public Bot(final List<SlashCommand> commands, final List<ContextCommand> context
135102
*/
136103
public static void main(String[] args) throws Exception {
137104
TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
138-
config = new BotConfig(Path.of("config"));
139-
dataSource = DbHelper.initDataSource(config);
140-
asyncPool = Executors.newScheduledThreadPool(config.getSystems().getAsyncPoolSize());
141-
autoMod = new AutoMod();
142-
JDA jda = JDABuilder.createDefault(config.getSystems().getJdaBotToken())
143-
.setStatus(OnlineStatus.DO_NOT_DISTURB)
144-
.setChunkingFilter(ChunkingFilter.ALL)
145-
.setMemberCachePolicy(MemberCachePolicy.ALL)
146-
.enableCache(CacheFlag.ACTIVITY)
147-
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, GatewayIntent.MESSAGE_CONTENT)
148-
.addEventListeners(autoMod, new StateListener())
149-
.build();
150105
MessageRequest.setDefaultMentions(EnumSet.of(Message.MentionType.ROLE, Message.MentionType.CHANNEL, Message.MentionType.USER, Message.MentionType.EMOJI));
151-
dih4jda = DIH4JDABuilder.setJDA(jda)
152-
.setDefaultCommandType(RegistrationType.GLOBAL)
153-
.disableLogging(DIH4JDALogger.Type.SMART_QUEUE_IGNORED)
154-
.disableAutomaticCommandRegistration()
155-
.build();
156-
customTagManager = new CustomTagManager(jda, dataSource);
157-
messageCache = new MessageCache();
158-
serverLockManager = new ServerLockManager(jda);
159-
addEventListeners(jda, dih4jda);
160-
addComponentHandler(dih4jda);
161-
// initialize Sentry
162-
Sentry.init(options -> {
163-
options.setDsn(config.getSystems().getSentryDsn());
164-
options.setTracesSampleRate(1.0);
165-
options.setDebug(false);
166-
});
167-
try {
168-
ScheduledTasks.init(jda);
169-
log.info("Initialized scheduled tasks.");
170-
} catch (SchedulerException e) {
171-
ExceptionLogger.capture(e, Bot.class.getSimpleName());
172-
log.error("Could not initialize all scheduled tasks.", e);
173-
jda.shutdown();
174-
}
175106
SpringApplication.run(Bot.class, args);
176107
}
177108

178-
/**
179-
* Adds all the bots' event listeners to the JDA instance, except for
180-
* the {@link AutoMod} instance.
181-
*
182-
* @param jda The JDA bot instance to add listeners to.
183-
* @param dih4jda The {@link DIH4JDA} instance.
184-
*/
185-
private static void addEventListeners(@NotNull JDA jda, @NotNull DIH4JDA dih4jda) {
186-
jda.addEventListener(
187-
serverLockManager,
188-
PresenceUpdater.standardActivities(),
189-
new MessageCacheListener(),
190-
new GitHubLinkListener(),
191-
new MessageLinkListener(),
192-
new GuildJoinListener(),
193-
new UserLeaveListener(),
194-
new MetricsUpdater(),
195-
new SuggestionListener(),
196-
new StarboardManager(),
197-
new ForumHelpListener(),
198-
new HelpChannelListener(),
199-
new ShareKnowledgeVoteListener(),
200-
new JobChannelVoteListener(),
201-
new PingableNameListener(),
202-
new HugListener()
203-
);
204-
dih4jda.addEventListener(new DIH4JDAListener());
109+
private void registerComponentHandlers(ApplicationContext ctx) {
110+
Map<String, Object> interactionHandlers = ctx.getBeansWithAnnotation(AutoDetectableComponentHandler.class);
111+
Map<List<String>, ButtonHandler> buttonHandlers = new HashMap<>();
112+
Map<List<String>, ModalHandler> modalHandlers = new HashMap<>();
113+
Map<List<String>, SelectMenuHandler> selectMenuHandlers = new HashMap<>();
114+
for (Object handler : interactionHandlers.values()) {
115+
AutoDetectableComponentHandler annotation = handler.getClass().getAnnotation(AutoDetectableComponentHandler.class);
116+
List<String> keys = Arrays.asList(annotation.value());
117+
addComponentHandler(buttonHandlers, keys, handler, ButtonHandler.class);
118+
addComponentHandler(modalHandlers, keys, handler, ModalHandler.class);
119+
addComponentHandler(selectMenuHandlers, keys, handler, SelectMenuHandler.class);
120+
}
121+
dih4jda.addButtonHandlers(buttonHandlers);
122+
dih4jda.addModalHandlers(modalHandlers);
123+
dih4jda.addSelectMenuHandlers(selectMenuHandlers);
205124
}
206125

207-
private static void addComponentHandler(@NotNull DIH4JDA dih4jda) {
208-
dih4jda.addButtonHandlers(Map.of(
209-
List.of("experience-leaderboard"), new ExperienceLeaderboardSubcommand(),
210-
List.of("utils"), new InteractionUtils(),
211-
List.of("resolve-report"), new ReportManager(),
212-
List.of("self-role"), new SelfRoleInteractionManager(),
213-
List.of("qotw-submission"), new SubmissionInteractionManager(),
214-
List.of("help-channel", "help-thank"), new HelpChannelInteractionManager(),
215-
List.of("qotw-list-questions"), new QOTWQuerySubcommand(),
216-
List.of(ForumHelpManager.HELP_THANKS_IDENTIFIER, ForumHelpManager.HELP_CLOSE_IDENTIFIER, ForumHelpManager.HELP_GUIDELINES_IDENTIFIER), new ForumHelpListener()
217-
));
218-
dih4jda.addModalHandlers(Map.of(
219-
List.of("qotw-add-question"), new AddQuestionSubcommand(),
220-
List.of("embed-create"), new CreateEmbedSubcommand(),
221-
List.of(EditEmbedSubcommand.EDIT_EMBED_ID), new EditEmbedSubcommand(),
222-
List.of("embed-addfield"), new AddEmbedFieldSubcommand(),
223-
List.of("quick-migrate"), new QuickMigrateSubcommand(),
224-
List.of("report"), new ReportManager(),
225-
List.of("self-role"), new SelfRoleInteractionManager(),
226-
List.of("tag-create"), new CreateCustomTagSubcommand(),
227-
List.of("tag-edit"), new EditCustomTagSubcommand()
228-
));
229-
dih4jda.addSelectMenuHandlers(Map.of(
230-
List.of("qotw-submission-select"), new SubmissionInteractionManager()
231-
));
126+
private <T> void addComponentHandler(Map<List<String>, T> handlers, List<String> keys, Object handler, Class<T> type) {
127+
if (type.isInstance(handler)) {
128+
T old = handlers.putIfAbsent(keys, type.cast(handler));
129+
if(old!=null) {
130+
throw new IllegalStateException("The same interaction name was registered multiple times");
131+
}
132+
}
232133
}
233134
}
234135

0 commit comments

Comments
 (0)