11package 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+
320import com .dynxsty .dih4jda .DIH4JDA ;
4- import com .dynxsty .dih4jda .DIH4JDABuilder ;
5- import com .dynxsty .dih4jda .DIH4JDALogger ;
621import com .dynxsty .dih4jda .interactions .commands .ContextCommand ;
7- import com .dynxsty .dih4jda .interactions .commands .RegistrationType ;
822import 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+
1028import 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 ;
1630import 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 ;
2132import net .dv8tion .jda .api .utils .messages .MessageRequest ;
2233import 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 ;
4835import net .javadiscord .javabot .tasks .PresenceUpdater ;
49- import net .javadiscord .javabot .tasks .ScheduledTasks ;
5036import 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
7548public 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