Skip to content

Commit 7ce98d8

Browse files
committed
Use Spring's Dependency Injection inside bot logic
1 parent b18888a commit 7ce98d8

File tree

71 files changed

+611
-446
lines changed

Some content is hidden

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

71 files changed

+611
-446
lines changed

src/main/java/net/javadiscord/javabot/Bot.java

Lines changed: 63 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -6,90 +6,65 @@
66
import com.dynxsty.dih4jda.interactions.commands.ContextCommand;
77
import com.dynxsty.dih4jda.interactions.commands.RegistrationType;
88
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
9+
import com.dynxsty.dih4jda.interactions.components.ButtonHandler;
10+
import com.dynxsty.dih4jda.interactions.components.ModalHandler;
11+
import com.dynxsty.dih4jda.interactions.components.SelectMenuHandler;
912
import com.zaxxer.hikari.HikariDataSource;
1013
import io.sentry.Sentry;
1114
import lombok.Getter;
12-
import lombok.extern.slf4j.Slf4j;
1315
import net.dv8tion.jda.api.JDA;
1416
import net.dv8tion.jda.api.JDABuilder;
1517
import net.dv8tion.jda.api.OnlineStatus;
1618
import net.dv8tion.jda.api.entities.Message;
19+
import net.dv8tion.jda.api.hooks.ListenerAdapter;
1720
import net.dv8tion.jda.api.requests.GatewayIntent;
1821
import net.dv8tion.jda.api.utils.AllowedMentions;
1922
import net.dv8tion.jda.api.utils.ChunkingFilter;
2023
import net.dv8tion.jda.api.utils.MemberCachePolicy;
2124
import net.dv8tion.jda.api.utils.cache.CacheFlag;
2225
import net.javadiscord.javabot.data.config.BotConfig;
2326
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.moderation.AutoMod;
31-
import net.javadiscord.javabot.systems.moderation.report.ReportManager;
32-
import net.javadiscord.javabot.systems.moderation.server_lock.ServerLockManager;
33-
import net.javadiscord.javabot.systems.qotw.commands.questions_queue.AddQuestionSubcommand;
34-
import net.javadiscord.javabot.systems.qotw.commands.view.QOTWQuerySubcommand;
35-
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionInteractionManager;
36-
import net.javadiscord.javabot.systems.staff_commands.embeds.AddEmbedFieldSubcommand;
37-
import net.javadiscord.javabot.systems.staff_commands.embeds.CreateEmbedSubcommand;
38-
import net.javadiscord.javabot.systems.staff_commands.embeds.EditEmbedSubcommand;
39-
import net.javadiscord.javabot.systems.staff_commands.self_roles.SelfRoleInteractionManager;
40-
import net.javadiscord.javabot.systems.staff_commands.tags.CustomTagManager;
41-
import net.javadiscord.javabot.systems.staff_commands.tags.commands.CreateCustomTagSubcommand;
42-
import net.javadiscord.javabot.systems.staff_commands.tags.commands.EditCustomTagSubcommand;
43-
import net.javadiscord.javabot.systems.starboard.StarboardManager;
44-
import net.javadiscord.javabot.systems.user_commands.leaderboard.ExperienceLeaderboardSubcommand;
45-
import net.javadiscord.javabot.tasks.MetricsUpdater;
27+
import net.javadiscord.javabot.systems.AutoDetectableComponentHandler;
4628
import net.javadiscord.javabot.tasks.PresenceUpdater;
47-
import net.javadiscord.javabot.tasks.ScheduledTasks;
4829
import net.javadiscord.javabot.util.ExceptionLogger;
49-
import net.javadiscord.javabot.util.InteractionUtils;
50-
import org.jetbrains.annotations.NotNull;
51-
import org.quartz.SchedulerException;
5230
import org.springframework.beans.factory.annotation.Autowired;
5331
import org.springframework.boot.SpringApplication;
5432
import org.springframework.boot.autoconfigure.SpringBootApplication;
33+
import org.springframework.context.ConfigurableApplicationContext;
5534
import org.springframework.context.annotation.ComponentScan;
5635
import org.springframework.context.annotation.FilterType;
36+
import org.springframework.scheduling.annotation.EnableScheduling;
5737

5838
import java.nio.file.Path;
5939
import java.time.ZoneOffset;
40+
import java.util.Arrays;
6041
import java.util.EnumSet;
42+
import java.util.HashMap;
6143
import java.util.List;
6244
import java.util.Map;
6345
import java.util.TimeZone;
6446
import java.util.concurrent.Executors;
6547
import java.util.concurrent.ScheduledExecutorService;
6648

49+
import javax.annotation.PostConstruct;
50+
6751
/**
6852
* The main class where the bot is initialized.
6953
*/
70-
@Slf4j
7154
@SpringBootApplication
72-
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SlashCommand.class, ContextCommand.class}))
55+
@ComponentScan(
56+
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { SlashCommand.class, ContextCommand.class, ListenerAdapter.class }),
57+
excludeFilters = @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, classes = PresenceUpdater.class)
58+
)
59+
@EnableScheduling
7360
public class Bot {
7461

7562
@Getter
7663
private static BotConfig config;
7764

78-
@Getter
79-
private static AutoMod autoMod;
80-
8165
@Getter
8266
private static DIH4JDA dih4jda;
8367

84-
@Getter
85-
private static MessageCache messageCache;
86-
87-
@Getter
88-
private static ServerLockManager serverLockManager;
89-
90-
@Getter
91-
private static CustomTagManager customTagManager;
92-
9368
@Getter
9469
private static HikariDataSource dataSource;
9570

@@ -102,9 +77,10 @@ public class Bot {
10277
*
10378
* @param commands The {@link Autowired} list of {@link SlashCommand}s.
10479
* @param contexts The {@link Autowired} list of {@link ContextCommand}s.
80+
* @param listeners The {@link Autowired} list of {@link ListenerAdapter}s.
10581
*/
10682
@Autowired
107-
public Bot(final List<SlashCommand> commands, final List<ContextCommand> contexts) {
83+
public Bot(final List<SlashCommand> commands, final List<ContextCommand> contexts, final List<ListenerAdapter> listeners) {
10884
if (!commands.isEmpty()) {
10985
getDih4jda().addSlashCommands(commands.toArray(SlashCommand[]::new));
11086
}
@@ -116,6 +92,26 @@ public Bot(final List<SlashCommand> commands, final List<ContextCommand> context
11692
} catch (ReflectiveOperationException e) {
11793
ExceptionLogger.capture(e, getClass().getSimpleName());
11894
}
95+
addEventListeners(listeners);
96+
}
97+
98+
private void addEventListeners(final List<ListenerAdapter> listeners) {
99+
for (ListenerAdapter listener : listeners) {
100+
dih4jda.getJDA().addEventListener(listener);
101+
}
102+
dih4jda.getJDA().addEventListener(dih4jda);
103+
}
104+
105+
/**
106+
* Initialize Sentry.
107+
*/
108+
@PostConstruct
109+
public void init() {
110+
Sentry.init(options -> {
111+
options.setDsn(config.getSystems().getSentryDsn());
112+
options.setTracesSampleRate(1.0);
113+
options.setDebug(false);
114+
});
119115
}
120116

121117
/**
@@ -136,95 +132,48 @@ public static void main(String[] args) throws Exception {
136132
config = new BotConfig(Path.of("config"));
137133
dataSource = DbHelper.initDataSource(config);
138134
asyncPool = Executors.newScheduledThreadPool(config.getSystems().getAsyncPoolSize());
139-
autoMod = new AutoMod();
140135
JDA jda = JDABuilder.createDefault(config.getSystems().getJdaBotToken())
141136
.setStatus(OnlineStatus.DO_NOT_DISTURB)
142137
.setChunkingFilter(ChunkingFilter.ALL)
143138
.setMemberCachePolicy(MemberCachePolicy.ALL)
144139
.enableCache(CacheFlag.ACTIVITY)
145140
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES, GatewayIntent.MESSAGE_CONTENT)
146-
.addEventListeners(autoMod, new StateListener())
147141
.build();
148142
AllowedMentions.setDefaultMentions(EnumSet.of(Message.MentionType.ROLE, Message.MentionType.CHANNEL, Message.MentionType.USER, Message.MentionType.EMOJI));
149143
dih4jda = DIH4JDABuilder.setJDA(jda)
150144
.setDefaultCommandType(RegistrationType.GLOBAL)
151145
.disableLogging(DIH4JDALogger.Type.SMART_QUEUE_IGNORED)
152146
.disableAutomaticCommandRegistration()
153147
.build();
154-
customTagManager = new CustomTagManager(jda, dataSource);
155-
messageCache = new MessageCache();
156-
serverLockManager = new ServerLockManager(jda);
157-
addEventListeners(jda, dih4jda);
158-
addComponentHandler(dih4jda);
159-
// initialize Sentry
160-
Sentry.init(options -> {
161-
options.setDsn(config.getSystems().getSentryDsn());
162-
options.setTracesSampleRate(1.0);
163-
options.setDebug(false);
164-
});
165-
try {
166-
ScheduledTasks.init(jda);
167-
log.info("Initialized scheduled tasks.");
168-
} catch (SchedulerException e) {
169-
ExceptionLogger.capture(e, Bot.class.getSimpleName());
170-
log.error("Could not initialize all scheduled tasks.", e);
171-
jda.shutdown();
172-
}
173-
SpringApplication.run(Bot.class, args);
148+
ConfigurableApplicationContext ctx = SpringApplication.run(Bot.class, args);
149+
registerComponentHandlers(ctx);
150+
174151
}
175152

176-
/**
177-
* Adds all the bots' event listeners to the JDA instance, except for
178-
* the {@link AutoMod} instance.
179-
*
180-
* @param jda The JDA bot instance to add listeners to.
181-
* @param dih4jda The {@link DIH4JDA} instance.
182-
*/
183-
private static void addEventListeners(@NotNull JDA jda, @NotNull DIH4JDA dih4jda) {
184-
jda.addEventListener(
185-
serverLockManager,
186-
PresenceUpdater.standardActivities(),
187-
new MessageCacheListener(),
188-
new GitHubLinkListener(),
189-
new MessageLinkListener(),
190-
new GuildJoinListener(),
191-
new UserLeaveListener(),
192-
new MetricsUpdater(),
193-
new SuggestionListener(),
194-
new StarboardManager(),
195-
new HelpChannelListener(),
196-
new ShareKnowledgeVoteListener(),
197-
new JobChannelVoteListener(),
198-
new PingableNameListener(),
199-
new HugListener()
200-
);
201-
dih4jda.addEventListener(new DIH4JDAListener());
153+
private static void registerComponentHandlers(ConfigurableApplicationContext ctx) {
154+
Map<String, Object> interactionHandlers = ctx.getBeansWithAnnotation(AutoDetectableComponentHandler.class);
155+
Map<List<String>, ButtonHandler> buttonHandlers = new HashMap<>();
156+
Map<List<String>, ModalHandler> modalHandlers = new HashMap<>();
157+
Map<List<String>, SelectMenuHandler> selectMenuHandlers = new HashMap<>();
158+
for (Object handler : interactionHandlers.values()) {
159+
AutoDetectableComponentHandler annotation = handler.getClass().getAnnotation(AutoDetectableComponentHandler.class);
160+
List<String> keys = Arrays.asList(annotation.value());
161+
addHandler(buttonHandlers, keys, handler, ButtonHandler.class);
162+
addHandler(modalHandlers, keys, handler, ModalHandler.class);
163+
addHandler(selectMenuHandlers, keys, handler, SelectMenuHandler.class);
164+
}
165+
dih4jda.addButtonHandlers(buttonHandlers);
166+
dih4jda.addModalHandlers(modalHandlers);
167+
dih4jda.addSelectMenuHandlers(selectMenuHandlers);
202168
}
203169

204-
private static void addComponentHandler(@NotNull DIH4JDA dih4jda) {
205-
dih4jda.addButtonHandlers(Map.of(
206-
List.of("experience-leaderboard"), new ExperienceLeaderboardSubcommand(),
207-
List.of("utils"), new InteractionUtils(),
208-
List.of("resolve-report"), new ReportManager(),
209-
List.of("self-role"), new SelfRoleInteractionManager(),
210-
List.of("qotw-submission"), new SubmissionInteractionManager(),
211-
List.of("help-channel", "help-thank"), new HelpChannelInteractionManager(),
212-
List.of("qotw-list-questions"), new QOTWQuerySubcommand()
213-
));
214-
dih4jda.addModalHandlers(Map.of(
215-
List.of("qotw-add-question"), new AddQuestionSubcommand(),
216-
List.of("embed-create"), new CreateEmbedSubcommand(),
217-
List.of(EditEmbedSubcommand.EDIT_EMBED_ID), new EditEmbedSubcommand(),
218-
List.of("embed-addfield"), new AddEmbedFieldSubcommand(),
219-
List.of("quick-migrate"), new QuickMigrateSubcommand(),
220-
List.of("report"), new ReportManager(),
221-
List.of("self-role"), new SelfRoleInteractionManager(),
222-
List.of("tag-create"), new CreateCustomTagSubcommand(),
223-
List.of("tag-edit"), new EditCustomTagSubcommand()
224-
));
225-
dih4jda.addSelectMenuHandlers(Map.of(
226-
List.of("qotw-submission-select"), new SubmissionInteractionManager()
227-
));
170+
private static <T> void addHandler(Map<List<String>, T> handlers, List<String> keys, Object handler, Class<T> type) {
171+
if (type.isInstance(handler)) {
172+
T old = handlers.putIfAbsent(keys, type.cast(handler));
173+
if(old!=null) {
174+
throw new IllegalStateException("The same interaction name was registered multiple times");
175+
}
176+
}
228177
}
229178
}
230179

src/main/java/net/javadiscord/javabot/api/SpringConfig.java renamed to src/main/java/net/javadiscord/javabot/SpringConfig.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
package net.javadiscord.javabot.api;
1+
package net.javadiscord.javabot;
22

33
import net.dv8tion.jda.api.JDA;
4-
import net.javadiscord.javabot.Bot;
4+
import net.javadiscord.javabot.tasks.PresenceUpdater;
5+
6+
import javax.sql.DataSource;
7+
58
import org.springframework.context.annotation.Bean;
69
import org.springframework.context.annotation.Configuration;
710

@@ -14,4 +17,14 @@ public class SpringConfig {
1417
public JDA getJDA() {
1518
return Bot.getDih4jda().getJDA();
1619
}
20+
21+
@Bean
22+
public PresenceUpdater standardActivityPresenceUpdater() {
23+
return PresenceUpdater.standardActivities();
24+
}
25+
26+
@Bean
27+
public DataSource getDataSource() {
28+
return Bot.getDataSource();
29+
}
1730
}

src/main/java/net/javadiscord/javabot/api/routes/leaderboard/qotw/QOTWLeaderboardController.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.github.benmanes.caffeine.cache.Caffeine;
44
import net.dv8tion.jda.api.JDA;
55
import net.dv8tion.jda.api.entities.Guild;
6-
import net.javadiscord.javabot.Bot;
76
import net.javadiscord.javabot.api.exception.InvalidEntityIdException;
87
import net.javadiscord.javabot.api.routes.CaffeineCache;
98
import net.javadiscord.javabot.api.routes.leaderboard.qotw.model.QOTWUserData;
@@ -27,19 +26,22 @@
2726
public class QOTWLeaderboardController extends CaffeineCache<Pair<Long, Integer>, List<QOTWUserData>> {
2827
private static final int PAGE_AMOUNT = 8;
2928
private final JDA jda;
29+
private final QOTWPointsService pointsService;
3030

3131
/**
3232
* The constructor of this class which initializes the {@link Caffeine} cache.
3333
*
3434
* @param jda The {@link JDA} instance to use.
35+
* @param pointsService The {@link QOTWPointsService}
3536
*/
3637
@Autowired
37-
public QOTWLeaderboardController(final JDA jda) {
38+
public QOTWLeaderboardController(final JDA jda, QOTWPointsService pointsService) {
3839
super(Caffeine.newBuilder()
3940
.expireAfterWrite(10, TimeUnit.MINUTES)
4041
.build()
4142
);
4243
this.jda = jda;
44+
this.pointsService = pointsService;
4345
}
4446

4547
/**
@@ -59,10 +61,9 @@ public ResponseEntity<List<QOTWUserData>> getQOTWLeaderboard(
5961
if (guild == null) {
6062
throw new InvalidEntityIdException(Guild.class, "You've provided an invalid guild id!");
6163
}
62-
QOTWPointsService service = new QOTWPointsService(Bot.getDataSource());
6364
List<QOTWUserData> members = getCache().getIfPresent(new Pair<>(guild.getIdLong(), page));
6465
if (members == null || members.isEmpty()) {
65-
members = service.getTopAccounts(PAGE_AMOUNT, page).stream()
66+
members = pointsService.getTopAccounts(PAGE_AMOUNT, page).stream()
6667
.map(p -> QOTWUserData.of(p, jda.retrieveUserById(p.getUserId()).complete()))
6768
.toList();
6869
getCache().put(new Pair<>(guild.getIdLong(), page), members);

src/main/java/net/javadiscord/javabot/api/routes/user_profile/UserProfileController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,22 @@
3939
@RestController
4040
public class UserProfileController extends CaffeineCache<Pair<Long, Long>, UserProfileData> {
4141
private final JDA jda;
42+
private final QOTWPointsService qotwPointsService;
4243

4344
/**
4445
* The constructor of this class which initializes the {@link Caffeine} cache.
4546
*
4647
* @param jda The {@link Autowired} {@link JDA} instance to use.
48+
* @param qotwPointsService The {@link QOTWPointsService}
4749
*/
4850
@Autowired
49-
public UserProfileController(final JDA jda) {
51+
public UserProfileController(final JDA jda, QOTWPointsService qotwPointsService) {
5052
super(Caffeine.newBuilder()
5153
.expireAfterWrite(10, TimeUnit.MINUTES)
5254
.build()
5355
);
5456
this.jda = jda;
57+
this.qotwPointsService = qotwPointsService;
5558
}
5659

5760
/**
@@ -84,8 +87,7 @@ public ResponseEntity<UserProfileData> getUserProfile(
8487
data.setDiscriminator(user.getDiscriminator());
8588
data.setEffectiveAvatarUrl(user.getEffectiveAvatarUrl());
8689
// Question of the Week Account
87-
QOTWPointsService qotwService = new QOTWPointsService(Bot.getDataSource());
88-
QOTWAccount qotwAccount = qotwService.getOrCreateAccount(user.getIdLong());
90+
QOTWAccount qotwAccount = qotwPointsService.getOrCreateAccount(user.getIdLong());
8991
data.setQotwAccount(qotwAccount);
9092
// Help Account
9193
HelpExperienceService helpService = new HelpExperienceService(Bot.getDataSource());

0 commit comments

Comments
 (0)