Skip to content

Feature: Cake Day #1042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from

Conversation

christolis
Copy link
Member

@christolis christolis commented Mar 3, 2024

I know that this is not a certain feature to be added, but I still wanted to experiment and see what I could come up with :)

Description

This pull request introduces the addition of the cake day feature to the server, allowing members to commemorate their annual membership anniversary with a unique "Cake day" role, with which they can celebrate within the community.

This feature uses the database and makes a cake_days table in order to store each member's joined date, as well the guild in which they joined (in case the bot ever gets to handle multiple guilds). The date stored in the database is split into two columns: the month and the day in one (joined_month_day), and the year in an other (joined_year). The reason the join date is split into these two columns is so that an index can be added for the joined_month_day column in order to make reading from the database faster. Therefore, it will be easier for the bot to find all cake days by the month and day, a search that would occur daily by this feature's routine.

If the bot finds that the cake_days table has no records, then it attempts to populate the table from all members of all guilds in a separate thread with batched insert queries in order to optimize the table population task.

Configuration changes

Property Description Type Default
cakeDayConfig.rolePattern A pattern of the cake day
role to give to members.
String "Cake Day"

Diagram

image

TODO

  • Write JavaDocs for all new code.
  • Implement cake-day role re-assignment daily. First it should revoke the role from all members, then based on the day's month and day, it should find all matching cake days and for the matches where the year difference is bigger than 0, assign them the role and whatever cosmetic stuff necessary.
  • Talk with the team about whether changing the member cache policy as well as the chunking filter to all is a valid approach for fetching all members of the server (is it worth storing all member references in memory just for one bulk database insert that will not even happen more than once?)

Closes #1035.

@christolis christolis self-assigned this Mar 3, 2024
@christolis christolis marked this pull request as ready for review March 5, 2024 12:44
@christolis christolis requested review from a team as code owners March 5, 2024 12:44
@christolis christolis marked this pull request as draft March 5, 2024 21:05
@christolis christolis marked this pull request as ready for review March 6, 2024 21:33
@christolis christolis requested a review from SquidXTV March 6, 2024 23:42
SquidXTV
SquidXTV previously approved these changes Mar 6, 2024
@ankitsmt211
Copy link
Member

It would be nice if one of the @Together-Java/moderators can review some of the critical parts of this feature when you can.

@ankitsmt211 ankitsmt211 added the config-changes if your PR contains any changes related to config file label May 18, 2024
This commit aims to fix a bug where the
CakeDayService#addTodayMembersCakeDayRole() method would add the cake day
role to all members who have been at least one year into the server,
disregarding the month and date in which they joined.

The documentation has also been made more clean and concise, while the
CakeDayService#addCakeDayRole() which required a UserSnowflake as one of
its inputs has been removed and now the other version of this function
is used which only requires a Member instance. Passing the Guild would
be unnecessary as it could be easily acquired from the Member instance,
and additionally it helps make sure that the right Member and Guild are
used to call this method.

Finally, this commit adds an extra condition in the select-from query
found in CakeDayService#findCakeDaysTodayFromDatabase() which makes sure
that we get all the cake days for the right guild, instead of getting
them all unconditionally.
/**
* Configuration constructor for the Cake Day feature.
*/
public CakeDayConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment really needed? It would maybe make sense if it links to the corresponding classes. Though even that should be done as some @see in the records comment instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible that we require javadoc on this one (public class). But yeah, we can add some details or a @see.

* @param database the database for storing cake day information
*/
public CakeDayService(Config config, Database database) {
this.config = config.getCakeDayConfig();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really dont like the naming here. Both CakeDayConfig and Config variables are called config.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id rename the parameter then though. dunno, i think for the pov of the class and where its being used in the methods it makes sense that config is the config for this feature. so perhaps the parameter Config can then be called fullConfig or something instead here.

return;
}

refreshMembersCakeDayRoles(cakeDayRole.get(), guild);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use Optional#ifPresent here

boolean isAnniversaryDay = hasMemberCakeDayToday(member);
int yearsSinceJoin = OffsetDateTime.now().getYear() - cakeDayRecord.getJoinedYear();

if (yearsSinceJoin > 0 && isAnniversaryDay) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm so this is confusing me a bit. hasMemberCakeDayToday checks if today is the cake day using the Member member which includes checking if the anniversary year is > 0. This looks like checking the same thing twice (yearsSinceJoin calculation uses the db record though). Also is the member query needed for this check? Doesn't the database also store the join date (make sure it is up2date though)? You would only need the member instance fo adding the role later on, so you could move that getMemberById later in the chain and thus making it more efficient.

return;
}

guild.addRoleToMember(snowflake, cakeDayRole.get()).complete();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A member is a UserSnowflake, so it makes no sense that you get the snowflake from the member.

.where(CAKE_DAYS.JOINED_MONTH_DAY.eq(todayMonthDay))
.and(CAKE_DAYS.GUILD_ID.eq(guild.getIdLong()))
.fetch())
.collect(Collectors.toList());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't database.read already return a list?

.fetch())
.collect(Collectors.toList())
.stream()
.findFirst();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You sure the .collect(...).stream().findFirst() is really needed? Is there no other method to use here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if all u need is the first hit (or any hit, bc there is no sorting), best is to modify the query to only return that result on DB level and not everything. im pretty sure jooq has a method for that and i recall us using it somewhere already

*
* @param cakeDayService an instance of the cake day service
*/
public CakeDayRoutine(CakeDayService cakeDayService) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems pretty useless.

*
* @param cakeDayService the {@link CakeDayService} to be used by this listener
*/
public CakeDayListener(CakeDayService cakeDayService) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems pretty useless.

return;
}

cakeDayService.insertMemberCakeDayToDatabase(member, guildId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use Optional#ifPresent here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
config-changes if your PR contains any changes related to config file priority: normal
Projects
Status: In Review
Development

Successfully merging this pull request may close these issues.

cake day for TJ server
5 participants