Skip to content

Commit b6826a1

Browse files
committed
preserve last_reward_at on birthday clear
1 parent 2023d98 commit b6826a1

File tree

9 files changed

+34
-38
lines changed

9 files changed

+34
-38
lines changed

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Commands/BirthdayAgeSlashCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public ValueTask<Command> GetCommandAsync(RunContext context, Options options)
2424
var user = options.user.User;
2525
var birthday = await birthdayRepository.GetBirthdayAsync(user);
2626

27-
if (birthday != null)
27+
if (birthday != null && birthday.IsSet)
2828
{
29-
if (birthday.Date.Year != IBirthdayRepository.Birthday.NoYearValue)
29+
if (birthday.Date.Year != UserBirthday.NoYearValue)
3030
{
3131
var age = AgeCalculator.GetCurrentAge(context.CreatedAt, birthday.Date);
3232
ageCalculator.TryAddAgeRolesInBackground(context, user, age);

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Commands/BirthdaySetSlashCommand.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public ValueTask<Command> GetCommandAsync(RunContext context, Options options)
3535
DateOnly birthday;
3636
try
3737
{
38-
birthday = new(options.year.Value ?? IBirthdayRepository.Birthday.NoYearValue, options.month.Value, options.day.Value);
38+
birthday = new(options.year.Value ?? UserBirthday.NoYearValue, options.month.Value, options.day.Value);
3939
}
4040
catch (ArgumentOutOfRangeException)
4141
{
@@ -47,9 +47,7 @@ The date you chose is not valid 🤔
4747
}
4848

4949
var setBirthday = await birthdayRepository.GetBirthdayAsync(context.User);
50-
51-
if (setBirthday != null &&
52-
(setBirthday.Date.Month != birthday.Month || setBirthday.Date.Day != birthday.Day))
50+
if (setBirthday != null && setBirthday.IsSet && (setBirthday.Date.Month != birthday.Month || setBirthday.Date.Day != birthday.Day))
5351
{
5452
List<CustomIdDataEntry> data = [
5553
new("date", $"{birthday.Year:D4}{birthday.Month:D2}{birthday.Day:D2}"),
@@ -85,7 +83,7 @@ Are you sure you want to change your birthday?
8583

8684
public async ValueTask<Embed> SetBirthdayAsync(RunContext context, bool isPrivate, DateOnly birthday)
8785
{
88-
if (birthday.Year != IBirthdayRepository.Birthday.NoYearValue)
86+
if (birthday.Year != UserBirthday.NoYearValue)
8987
{
9088
int age = AgeCalculator.GetCurrentAge(context.CreatedAt, birthday);
9189

@@ -109,7 +107,7 @@ public async ValueTask<Embed> SetBirthdayAsync(RunContext context, bool isPrivat
109107
.WithDescription(
110108
$"""
111109
Your birthday has been set **{birthday.ToString("MMMM d", TaylorBotCulture.Culture)}** ✅
112-
{(birthday.Year == IBirthdayRepository.Birthday.NoYearValue
110+
{(birthday.Year == UserBirthday.NoYearValue
113111
? $"Consider setting your birthday with the **year** option for {mention.SlashCommand("birthday age", context)} to work ❓"
114112
: $"You can now use {mention.SlashCommand("birthday age", context)} to display your age 🔢")}
115113
{(isPrivate

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Commands/BirthdayShowSlashCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public record Options(ParsedUserOrAuthor user);
2424
{
2525
var birthday = await birthdayRepository.GetBirthdayAsync(user);
2626

27-
if (birthday != null)
27+
if (birthday != null && birthday.IsSet)
2828
{
29-
if (birthday.Date.Year != IBirthdayRepository.Birthday.NoYearValue)
29+
if (birthday.Date.Year != UserBirthday.NoYearValue)
3030
{
3131
var age = AgeCalculator.GetCurrentAge(createdAt, birthday.Date);
3232
ageCalculator.TryAddAgeRolesInBackground(context, user, age);

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Domain/IBirthdayRepository.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33

44
namespace TaylorBot.Net.Commands.Discord.Program.Modules.Birthday.Domain;
55

6-
public interface IBirthdayRepository
6+
public record UserBirthday(DateOnly Date, bool IsPrivate)
77
{
8-
record Birthday(DateOnly Date, bool IsPrivate)
9-
{
10-
public const int NoYearValue = 1804;
11-
}
8+
public const int NoYearValue = 1804;
9+
10+
public static readonly DateOnly ClearedDate = new(1, 1, 1);
11+
12+
public bool IsSet => Date != ClearedDate;
13+
}
1214

13-
record BirthdayCalendarEntry(SnowflakeId UserId, string Username, DateOnly NextBirthday);
15+
public record BirthdayCalendarEntry(SnowflakeId UserId, string Username, DateOnly NextBirthday);
1416

15-
record AgeRole(SnowflakeId RoleId, int MinimumAge);
17+
public record AgeRole(SnowflakeId RoleId, int MinimumAge);
1618

17-
ValueTask<Birthday?> GetBirthdayAsync(DiscordUser user);
19+
public interface IBirthdayRepository
20+
{
21+
ValueTask<UserBirthday?> GetBirthdayAsync(DiscordUser user);
1822
ValueTask ClearBirthdayAsync(DiscordUser user);
19-
ValueTask SetBirthdayAsync(DiscordUser user, Birthday birthday);
23+
ValueTask SetBirthdayAsync(DiscordUser user, UserBirthday birthday);
2024
ValueTask<IList<BirthdayCalendarEntry>> GetBirthdayCalendarAsync(CommandGuild guild);
2125
ValueTask<IList<AgeRole>> GetAgeRolesAsync(SnowflakeId guildId);
2226
}

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Infrastructure/BirthdayPostgresRepository.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Dapper;
1+
using Dapper;
22
using TaylorBot.Net.Commands.Discord.Program.Modules.Birthday.Domain;
33
using TaylorBot.Net.Core.Infrastructure;
44
using TaylorBot.Net.Core.Snowflake;
@@ -10,7 +10,7 @@ public class BirthdayPostgresRepository(PostgresConnectionFactory postgresConnec
1010
{
1111
private sealed record BirthdayDto(DateOnly birthday, bool is_private);
1212

13-
public async ValueTask<IBirthdayRepository.Birthday?> GetBirthdayAsync(DiscordUser user)
13+
public async ValueTask<UserBirthday?> GetBirthdayAsync(DiscordUser user)
1414
{
1515
await using var connection = postgresConnectionFactory.CreateConnection();
1616

@@ -34,18 +34,11 @@ FROM attributes.birthdays
3434

3535
public async ValueTask ClearBirthdayAsync(DiscordUser user)
3636
{
37-
await using var connection = postgresConnectionFactory.CreateConnection();
38-
39-
await connection.ExecuteAsync(
40-
"DELETE FROM attributes.birthdays WHERE user_id = @UserId;",
41-
new
42-
{
43-
UserId = $"{user.Id}",
44-
}
45-
);
37+
// Set birthday to 0001-01-01 to preserve last_reward_at (avoids clear as an exploit to get more points)
38+
await SetBirthdayAsync(user, new(UserBirthday.ClearedDate, IsPrivate: true));
4639
}
4740

48-
public async ValueTask SetBirthdayAsync(DiscordUser user, IBirthdayRepository.Birthday birthday)
41+
public async ValueTask SetBirthdayAsync(DiscordUser user, UserBirthday birthday)
4942
{
5043
await using var connection = postgresConnectionFactory.CreateConnection();
5144

@@ -68,7 +61,7 @@ ON CONFLICT (user_id) DO UPDATE SET
6861

6962
private sealed record CalendarEntryDto(string user_id, string username, DateOnly next_birthday);
7063

71-
public async ValueTask<IList<IBirthdayRepository.BirthdayCalendarEntry>> GetBirthdayCalendarAsync(CommandGuild guild)
64+
public async ValueTask<IList<BirthdayCalendarEntry>> GetBirthdayCalendarAsync(CommandGuild guild)
7265
{
7366
await using var connection = postgresConnectionFactory.CreateConnection();
7467

@@ -86,7 +79,7 @@ ORDER BY next_birthday
8679
}
8780
);
8881

89-
return [.. entries.Select(e => new IBirthdayRepository.BirthdayCalendarEntry(
82+
return [.. entries.Select(e => new BirthdayCalendarEntry(
9083
new(e.user_id),
9184
e.username,
9285
e.next_birthday
@@ -95,7 +88,7 @@ ORDER BY next_birthday
9588

9689
private sealed record AgeRoleDto(string age_role_id, int minimum_age);
9790

98-
public async ValueTask<IList<IBirthdayRepository.AgeRole>> GetAgeRolesAsync(SnowflakeId guildId)
91+
public async ValueTask<IList<AgeRole>> GetAgeRolesAsync(SnowflakeId guildId)
9992
{
10093
await using var connection = postgresConnectionFactory.CreateConnection();
10194

@@ -114,7 +107,7 @@ INNER JOIN plus.plus_guilds
114107
}
115108
);
116109

117-
return [.. ageRoles.Select(a => new IBirthdayRepository.AgeRole(
110+
return [.. ageRoles.Select(a => new AgeRole(
118111
new(a.age_role_id),
119112
a.minimum_age
120113
))];

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Birthday/Infrastructure/ZodiacSignPostgresRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class ZodiacSignPostgresRepository(PostgresConnectionFactory postgresConn
1212
await using var connection = postgresConnectionFactory.CreateConnection();
1313

1414
return await connection.QuerySingleOrDefaultAsync<string?>(
15-
"SELECT zodiac(birthday) FROM attributes.birthdays WHERE user_id = @UserId;",
15+
"SELECT zodiac(birthday) FROM attributes.birthdays WHERE user_id = @UserId AND birthday != '0001-01-01';",
1616
new
1717
{
1818
UserId = $"{user.Id}",

src/TaylorBot.Net/Program.Commands.Discord/src/TaylorBot.Net.Commands.Discord.Program/Modules/Stats/Infrastructure/ServerStatsRepositoryPostgresRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ SELECT ROUND(AVG(human_age), 2) AS age_average, ROUND(MEDIAN(human_age), 2) AS a
2222
FROM (
2323
SELECT date_part('year', age(birthday))::int AS human_age
2424
FROM attributes.birthdays
25-
WHERE date_part('year', birthday)::int != 1804 AND user_id IN (
25+
WHERE date_part('year', birthday)::int != 1804 AND birthday != '0001-01-01' AND user_id IN (
2626
SELECT user_id
2727
FROM guilds.guild_members
2828
WHERE guild_id = @GuildId AND alive = TRUE

src/TaylorBot.Net/Program.UserNotifier/src/TaylorBot.Net.BirthdayReward.Infrastructure/BirthdayPostgresRepository.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public async ValueTask<List<RewardedUser>> RewardEligibleUsersAsync(long rewardA
2626
(birthday + (INTERVAL '1 YEAR' * (date_part('year', CURRENT_DATE) - date_part('year', birthday))))
2727
BETWEEN CURRENT_DATE - 2 AND CURRENT_DATE
2828
)
29+
AND birthday != '0001-01-01'
2930
RETURNING user_id;
3031
"""
3132
);

src/TaylorBot.Net/Program.UserNotifier/src/TaylorBot.Net.BirthdayReward.Infrastructure/BirthdayRolePostgresRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task<List<BirthdayUser>> GetBirthdayUsersAsync()
2121
FROM (
2222
SELECT user_id, (birthday + (INTERVAL '1 YEAR' * (date_part('year', CURRENT_DATE) - date_part('year', birthday))))::date as birthday_date
2323
FROM attributes.birthdays
24-
WHERE is_private IS FALSE
24+
WHERE is_private IS FALSE AND birthday != '0001-01-01'
2525
) AS birthdays
2626
) AS birthday_windows
2727
WHERE CURRENT_TIMESTAMP AT TIME ZONE 'UTC' BETWEEN birthday_start AND birthday_end;

0 commit comments

Comments
 (0)