44import net .dv8tion .jda .api .entities .Guild ;
55import net .dv8tion .jda .api .entities .Message ;
66import net .dv8tion .jda .api .entities .MessageReaction ;
7+ import net .dv8tion .jda .api .entities .channel .concrete .ForumChannel ;
78import net .dv8tion .jda .api .entities .channel .concrete .ThreadChannel ;
89import net .dv8tion .jda .api .entities .emoji .Emoji ;
910import net .dv8tion .jda .api .entities .emoji .RichCustomEmoji ;
1718import org .togetherjava .tjbot .config .Config ;
1819import org .togetherjava .tjbot .config .NumericScoreConfig ;
1920import org .togetherjava .tjbot .features .EventReceiver ;
21+ import org .togetherjava .tjbot .features .Routine ;
2022
2123import java .util .List ;
2224import java .util .Map ;
2325import java .util .Optional ;
2426import java .util .Set ;
27+ import java .util .concurrent .TimeUnit ;
2528import java .util .function .Function ;
2629import java .util .stream .Collectors ;
2730import java .util .stream .Stream ;
4346 * <p>
4447 * Score formula: {@code 1 (base for OP) + upvotes - downvotes}, where the OP's own votes are
4548 * excluded. The displayed emoji is determined by the configured score-to-emoji mapping.
49+ *
50+ * <p>
51+ * On startup, a routine scan corrects any missing or stale score emojis on all active posts,
52+ * recovering from bot downtime.
4653 */
47- public final class ProjectsNumericScoreListener extends ListenerAdapter implements EventReceiver {
54+ public final class ProjectsNumericScoreListener extends ListenerAdapter
55+ implements EventReceiver , Routine {
4856
4957 private static final Logger logger =
5058 LoggerFactory .getLogger (ProjectsNumericScoreListener .class );
@@ -69,6 +77,40 @@ public ProjectsNumericScoreListener(Config config) {
6977 ProjectsNumericScoreListener ::buildScoreEmojiSet ));
7078 }
7179
80+ @ Override
81+ public Schedule createSchedule () {
82+ // Run immediately on startup, then daily as a maintenance check
83+ return new Schedule (ScheduleMode .FIXED_RATE , 0 , 24 , TimeUnit .HOURS );
84+ }
85+
86+ @ Override
87+ public void runRoutine (JDA jda ) {
88+ forumIdToConfig .forEach ((forumId , cfg ) -> syncForum (forumId , cfg , jda ));
89+ }
90+
91+ private void syncForum (long forumId , NumericScoreConfig cfg , JDA jda ) {
92+ ForumChannel forum = jda .getChannelById (ForumChannel .class , forumId );
93+ if (forum == null ) {
94+ logger .warn ("Numeric score: forum channel {} not found or not cached, skipping sync" ,
95+ forumId );
96+ return ;
97+ }
98+
99+ List <ThreadChannel > activeThreads = forum .getThreadChannels ()
100+ .stream ()
101+ .filter (t -> !t .isArchived ())
102+ .toList ();
103+
104+ logger .debug ("Syncing score emojis for {} active threads in forum {}" , activeThreads .size (),
105+ forum .getName ());
106+
107+ Guild guild = forum .getGuild ();
108+ activeThreads .forEach (thread -> thread .retrieveMessageById (thread .getIdLong ())
109+ .queue (post -> refreshScore (post , thread , guild , jda ),
110+ e -> logger .warn ("Failed to retrieve post for thread {} during sync" ,
111+ thread .getId (), e )));
112+ }
113+
72114 @ Override
73115 public void onMessageReceived (MessageReceivedEvent event ) {
74116 if (!event .isFromThread ()) {
@@ -93,8 +135,8 @@ public void onMessageReceived(MessageReceivedEvent event) {
93135 addVoteEmoji (cfg .downVoteEmoteName (), guild , post );
94136
95137 Emoji initialEmoji = Emoji .fromUnicode (scoreToEmojiStr (BASE_SCORE , cfg ));
96- post .addReaction (initialEmoji ).queue (_ -> {
97- }, e -> logger .warn ("Failed to add initial score emoji to post {}" , post .getId (), e ));
138+ post .addReaction (initialEmoji ).queue (_ -> {}, e -> logger
139+ .warn ("Failed to add initial score emoji to post {}" , post .getId (), e ));
98140 }
99141
100142 @ Override
@@ -123,8 +165,8 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) {
123165 if (isScoreEmoji (reacted , forumId )) {
124166 event .retrieveUser ()
125167 .flatMap (user -> event .getReaction ().removeReaction (user ))
126- .queue (_ -> {
127- }, e -> logger . warn ( "Failed to remove score emoji added by user {} on post {}" ,
168+ .queue (_ -> {}, e -> logger . warn (
169+ "Failed to remove score emoji added by user {} on post {}" ,
128170 event .getUserId (), event .getMessageId (), e ));
129171 return ;
130172 }
0 commit comments