33import net .dv8tion .jda .api .JDA ;
44import net .dv8tion .jda .api .entities .Guild ;
55import net .dv8tion .jda .api .entities .Member ;
6+ import net .dv8tion .jda .api .entities .Role ;
7+ import net .dv8tion .jda .api .entities .UserSnowflake ;
68import org .jooq .Query ;
79import org .jooq .impl .DSL ;
810import org .slf4j .Logger ;
2123import java .util .Optional ;
2224import java .util .concurrent .CompletableFuture ;
2325import java .util .concurrent .TimeUnit ;
26+ import java .util .function .Predicate ;
27+ import java .util .regex .Pattern ;
2428import java .util .stream .Collectors ;
2529
2630import static org .togetherjava .tjbot .db .generated .tables .CakeDays .CAKE_DAYS ;
@@ -31,12 +35,15 @@ public class CakeDayRoutine implements Routine {
3135 private static final DateTimeFormatter MONTH_DAY_FORMATTER =
3236 DateTimeFormatter .ofPattern ("MM-dd" );
3337 private static final int BULK_INSERT_SIZE = 500 ;
38+ private final Predicate <String > cakeDayRolePredicate ;
3439 private final CakeDayConfig config ;
3540 private final Database database ;
3641
3742 public CakeDayRoutine (Config config , Database database ) {
3843 this .config = config .getCakeDayConfig ();
3944 this .database = database ;
45+
46+ this .cakeDayRolePredicate = Pattern .compile (this .config .rolePattern ()).asPredicate ();
4047 }
4148
4249 /**
@@ -75,7 +82,49 @@ public void runRoutine(JDA jda) {
7582
7683 return result ;
7784 });
85+
86+ return ;
7887 }
88+
89+ jda .getGuilds ().forEach (this ::reassignCakeDayRole );
90+ }
91+
92+ private void reassignCakeDayRole (Guild guild ) {
93+ Role cakeDayRole = getCakeDayRoleFromGuild (guild ).orElse (null );
94+
95+ if (cakeDayRole == null ) {
96+ logger .warn ("Cake day role with pattern {} not found for guild: {}" ,
97+ config .rolePattern (), guild .getName ());
98+ return ;
99+ }
100+
101+ removeMembersCakeDayRole (cakeDayRole , guild )
102+ .thenCompose (result -> addTodayMembersCakeDayRole (cakeDayRole , guild ))
103+ .join ();
104+ }
105+
106+ private CompletableFuture <Void > addTodayMembersCakeDayRole (Role cakeDayRole , Guild guild ) {
107+ return CompletableFuture
108+ .runAsync (() -> findCakeDaysTodayFromDatabase ().forEach (cakeDayRecord -> {
109+ UserSnowflake snowflake = UserSnowflake .fromId (cakeDayRecord .getUserId ());
110+
111+ int anniversary = OffsetDateTime .now ().getYear () - cakeDayRecord .getJoinedYear ();
112+ if (anniversary > 0 ) {
113+ guild .addRoleToMember (snowflake , cakeDayRole ).complete ();
114+ }
115+ }));
116+ }
117+
118+ private CompletableFuture <Void > removeMembersCakeDayRole (Role cakeDayRole , Guild guild ) {
119+ return CompletableFuture .runAsync (() -> guild .findMembersWithRoles (cakeDayRole )
120+ .onSuccess (members -> removeRoleFromMembers (guild , cakeDayRole , members )));
121+ }
122+
123+ private void removeRoleFromMembers (Guild guild , Role role , List <Member > members ) {
124+ members .forEach (member -> {
125+ UserSnowflake snowflake = UserSnowflake .fromId (member .getIdLong ());
126+ guild .removeRoleFromMember (snowflake , role ).complete ();
127+ });
79128 }
80129
81130 private int getCakeDayCount (Database database ) {
@@ -121,6 +170,13 @@ private Optional<Query> createMemberCakeDayQuery(Member member, long guildId) {
121170 .set (CAKE_DAYS .USER_ID , member .getIdLong ()));
122171 }
123172
173+ private Optional <Role > getCakeDayRoleFromGuild (Guild guild ) {
174+ return guild .getRoles ()
175+ .stream ()
176+ .filter (role -> cakeDayRolePredicate .test (role .getName ()))
177+ .findFirst ();
178+ }
179+
124180 private List <CakeDaysRecord > findCakeDaysTodayFromDatabase () {
125181 String todayMonthDay = OffsetDateTime .now ().format (MONTH_DAY_FORMATTER );
126182
0 commit comments