Skip to content
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

Hotfix/fetch user #3

Merged
merged 3 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions Present/Commands/GiveawayCommand.Redraw.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,26 @@ public async Task RedrawAsync(InteractionContext context,
{
foreach (string keepId in keepIds.Split((char[]?) null, StringSplitOptions.RemoveEmptyEntries))
{
if (ulong.TryParse(keepId, out ulong userId) &&
_giveawayService.ValidateUser(userId, guild, out DiscordMember? member))
keep.Add(member);
else
if (!ulong.TryParse(keepId, out ulong userId))
{
invalidKeepIds.Add(keepId);
continue;
}

DiscordMember? member = await _giveawayService.ValidateUserAsync(userId, giveaway).ConfigureAwait(false);
if (member is null) invalidKeepIds.Add(keepId);
else keep.Add(member);
}
}

IReadOnlyList<DiscordMember> winners = _giveawayService.SelectWinners(giveaway, keep);
IReadOnlyList<DiscordMember> winners = await _giveawayService.SelectWinnersAsync(giveaway, keep).ConfigureAwait(false);
await _giveawayService.UpdateWinnersAsync(giveaway, winners).ConfigureAwait(false);

await _giveawayService.EditGiveawayAsync(giveaway).ConfigureAwait(false);
await _giveawayService.UpdateGiveawayLogMessageAsync(giveaway).ConfigureAwait(false);
await _giveawayService.UpdateGiveawayPublicMessageAsync(giveaway).ConfigureAwait(false);

embed = _giveawayService.CreateGiveawayInformationEmbed(giveaway);
embed = await _giveawayService.CreateGiveawayInformationEmbedAsync(giveaway).ConfigureAwait(false);
embed.WithColor(DiscordColor.Green);
embed.WithTitle(EmbedStrings.GiveawayEdited_Title);

Expand Down
2 changes: 1 addition & 1 deletion Present/Commands/GiveawayCommand.SetWinners.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public async Task SetWinnersAsync(InteractionContext context,
await _giveawayService.UpdateGiveawayLogMessageAsync(giveaway).ConfigureAwait(false);
await _giveawayService.UpdateGiveawayPublicMessageAsync(giveaway).ConfigureAwait(false);

embed = _giveawayService.CreateGiveawayInformationEmbed(giveaway);
embed = await _giveawayService.CreateGiveawayInformationEmbedAsync(giveaway).ConfigureAwait(false);
embed.WithColor(DiscordColor.Green);
embed.WithTitle(EmbedStrings.GiveawayEdited_Title);
await context.CreateResponseAsync(embed).ConfigureAwait(false);
Expand Down
2 changes: 1 addition & 1 deletion Present/Commands/GiveawayCommand.View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task ViewAsync(InteractionContext context,
return;
}

embed = _giveawayService.CreateGiveawayInformationEmbed(giveaway);
embed = await _giveawayService.CreateGiveawayInformationEmbedAsync(giveaway).ConfigureAwait(false);
embed.WithTitle(EmbedStrings.GiveawayInformation);

await context.CreateResponseAsync(embed).ConfigureAwait(false);
Expand Down
2 changes: 2 additions & 0 deletions Present/Data/EntityConfigurations/GiveawayConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public void Configure(EntityTypeBuilder<Giveaway> builder)
builder.Property(e => e.Description);
builder.Property(e => e.ImageUri).HasConversion<UriToStringConverter>();
builder.Property(e => e.Entrants).HasConversion<UInt64ListToBytesConverter>();
builder.Property(e => e.ExcludedRoles).HasConversion<UInt64ListToBytesConverter>();
builder.Property(e => e.ExcludedUsers).HasConversion<UInt64ListToBytesConverter>();
builder.Property(e => e.WinnerCount);
builder.Property(e => e.WinnerIds).HasConversion<UInt64ListToBytesConverter>();
builder.Property(e => e.MessageId);
Expand Down
12 changes: 12 additions & 0 deletions Present/Data/Giveaway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ internal sealed class Giveaway : IEquatable<Giveaway>
/// <value>The end date and time.</value>
public DateTimeOffset EndTime { get; set; }

/// <summary>
/// Gets or sets the list of excluded role IDs for this giveaway.
/// </summary>
/// <value>A <see cref="List{T}" /> of <see cref="ulong" /> representing the excluded role IDs.</value>
public List<ulong> ExcludedRoles { get; set; } = new();

/// <summary>
/// Gets or sets the list of excluded user IDs for this giveaway.
/// </summary>
/// <value>A <see cref="List{T}" /> of <see cref="ulong" /> representing the excluded user IDs.</value>
public List<ulong> ExcludedUsers { get; set; } = new();

/// <summary>
/// Gets or sets the guild ID of the giveaway.
/// </summary>
Expand Down
16 changes: 8 additions & 8 deletions Present/Present.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@
<PackageReference Include="DSharpPlus" Version="4.3.0"/>
<PackageReference Include="DSharpPlus.SlashCommands" Version="4.3.0"/>
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.1"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/>
<PackageReference Include="NLog" Version="5.1.0"/>
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.3"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"/>
<PackageReference Include="NLog" Version="5.1.2"/>
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.2"/>
<PackageReference Include="SmartFormat.NET" Version="3.2.0"/>
<PackageReference Include="X10D" Version="3.2.0-nightly.149"/>
<PackageReference Include="X10D.DSharpPlus" Version="3.2.0-nightly.149"/>
<PackageReference Include="X10D.Hosting" Version="3.2.0-nightly.149"/>
<PackageReference Include="X10D" Version="3.2.0-nightly.152"/>
<PackageReference Include="X10D.DSharpPlus" Version="3.2.0-nightly.152"/>
<PackageReference Include="X10D.Hosting" Version="3.2.0-nightly.152"/>
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Present/Services/ActiveGiveawayService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private async Task HandleExpiredGiveawayAsync(Giveaway giveaway)
giveaway.EndHandled = true;
RemoveActiveGiveaway(giveaway);

IReadOnlyList<DiscordMember> winners = _giveawayService.SelectWinners(giveaway);
IReadOnlyList<DiscordMember> winners = await _giveawayService.SelectWinnersAsync(giveaway).ConfigureAwait(false);
Logger.Info($"Selected {"winner".ToQuantity(winners.Count)} for giveaway {giveaway.Id} ({giveaway.Title})");
foreach (DiscordMember winner in winners)
Logger.Info(winner);
Expand Down
67 changes: 43 additions & 24 deletions Present/Services/GiveawayService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.Extensions.Hosting;
using NLog;
using SmartFormat;
using X10D.Collections;
using X10D.Core;
using X10D.DSharpPlus;

Expand Down Expand Up @@ -169,7 +168,7 @@ public async Task<Giveaway> CreateGiveawayAsync(GiveawayCreationOptions options)
/// <param name="giveaway">The giveaway whose information to fetch.</param>
/// <returns>A <see cref="DiscordEmbedBuilder" />, populated with information from <paramref name="giveaway" />.</returns>
/// <exception cref="ArgumentNullException"><paramref name="giveaway" /> is <see langword="null" />.</exception>
public DiscordEmbedBuilder CreateGiveawayInformationEmbed(Giveaway giveaway)
public async Task<DiscordEmbedBuilder> CreateGiveawayInformationEmbedAsync(Giveaway giveaway)
{
ArgumentNullException.ThrowIfNull(giveaway);

Expand All @@ -188,7 +187,14 @@ public DiscordEmbedBuilder CreateGiveawayInformationEmbed(Giveaway giveaway)
var jumpLink = new Uri($"https://discord.com/channels/{giveaway.GuildId}/{giveaway.ChannelId}/{giveaway.MessageId}");
string conjugatedEnd = giveaway.EndTime < DateTimeOffset.UtcNow ? EmbedStrings.Ended : EmbedStrings.Ends;
var entrantsCount = giveaway.Entrants.Count.ToString("N0");
var excludedEntrantsCount = giveaway.Entrants.CountWhereNot(e => ValidateUser(e, guild, out _)).ToString("N0");
var excludedEntrantsCount = 0;
foreach (ulong entrantId in giveaway.Entrants)
{
DiscordMember? member = await ValidateUserAsync(entrantId, giveaway).ConfigureAwait(false);
if (member is null) excludedEntrantsCount++;
}

var excludedEntrants = excludedEntrantsCount.ToString("N0");

embed.AddField(EmbedStrings.Title, giveaway.Title);
embed.AddField(EmbedStrings.Description, description);
Expand All @@ -199,7 +205,7 @@ public DiscordEmbedBuilder CreateGiveawayInformationEmbed(Giveaway giveaway)
embed.AddField(conjugatedEnd, Formatter.Timestamp(giveaway.EndTime), true);
embed.AddField(EmbedStrings.Creator, MentionUtility.MentionUser(giveaway.CreatorId), true);
embed.AddField(EmbedStrings.Entrants, entrantsCount, true);
embed.AddField(EmbedStrings.ExcludedEntrants, excludedEntrantsCount, true);
embed.AddField(EmbedStrings.ExcludedEntrants, excludedEntrants, true);
embed.AddFieldIf(giveaway.ImageUri is not null, "Image", () => Formatter.MaskedUrl("View", giveaway.ImageUri), true);

List<ulong> winnerIds = giveaway.WinnerIds;
Expand All @@ -218,11 +224,11 @@ public DiscordEmbedBuilder CreateGiveawayInformationEmbed(Giveaway giveaway)
/// <param name="giveaway">The giveaway whose log embed to return.</param>
/// <returns>The log embed.</returns>
/// <exception cref="ArgumentNullException"><paramref name="giveaway" /> is <see langword="null" />.</exception>
public DiscordEmbed CreateGiveawayLogEmbed(Giveaway giveaway)
public async Task<DiscordEmbed> CreateGiveawayLogEmbedAsync(Giveaway giveaway)
{
ArgumentNullException.ThrowIfNull(giveaway);

DiscordEmbedBuilder embed = CreateGiveawayInformationEmbed(giveaway);
DiscordEmbedBuilder embed = await CreateGiveawayInformationEmbedAsync(giveaway).ConfigureAwait(false);
embed.WithTitle(EmbedStrings.GiveawayCreated_Title_NoEmoji);
embed.WithColor(DiscordColor.Green);
return embed;
Expand Down Expand Up @@ -425,7 +431,7 @@ public async Task LogGiveawayCreationAsync(Giveaway giveaway)
return;
}

DiscordEmbedBuilder embed = CreateGiveawayInformationEmbed(giveaway);
DiscordEmbedBuilder embed = await CreateGiveawayInformationEmbedAsync(giveaway).ConfigureAwait(false);
embed.WithTitle(EmbedStrings.GiveawayCreated_Title_NoEmoji);
embed.WithColor(DiscordColor.Green);

Expand Down Expand Up @@ -461,15 +467,22 @@ public async Task LogGiveawayExpirationAsync(Giveaway giveaway)
description = $"{description[..1021]}...";

var entrantsCount = giveaway.Entrants.Count.ToString("N0");
var excludedEntrantsCount = giveaway.Entrants.CountWhereNot(e => ValidateUser(e, guild, out _)).ToString("N0");
var excludedEntrantsCount = 0;
foreach (ulong entrantId in giveaway.Entrants)
{
DiscordMember? member = await ValidateUserAsync(entrantId, giveaway).ConfigureAwait(false);
if (member is null) excludedEntrantsCount++;
}

var excludedEntrants = excludedEntrantsCount.ToString("N0");

embed.AddField(EmbedStrings.Title, giveaway.Title);
embed.AddField(EmbedStrings.Description, description);
embed.AddField(EmbedStrings.Id, giveaway.Id, true);
embed.AddField(EmbedStrings.NumberOfWinners, giveaway.WinnerCount, true);
embed.AddField(EmbedStrings.Duration, (giveaway.EndTime - giveaway.StartTime).Humanize(), true);
embed.AddField(EmbedStrings.Entrants, entrantsCount, true);
embed.AddField(EmbedStrings.ExcludedEntrants, excludedEntrantsCount, true);
embed.AddField(EmbedStrings.ExcludedEntrants, excludedEntrants, true);

var winners = new List<DiscordMember>();
foreach (ulong winnerId in giveaway.WinnerIds)
Expand Down Expand Up @@ -518,7 +531,8 @@ public async Task LogGiveawayExpirationAsync(Giveaway giveaway)
/// The selected winners of the giveaway. The returned list does not include invalid users, users which are no longer in
/// the guild, or users with at least one of the excluded role IDs.
/// </returns>
public IReadOnlyList<DiscordMember> SelectWinners(Giveaway giveaway, IEnumerable<DiscordMember>? winnersToKeep = null)
public async Task<IReadOnlyList<DiscordMember>> SelectWinnersAsync(Giveaway giveaway,
IEnumerable<DiscordMember>? winnersToKeep = null)
{
ArgumentNullException.ThrowIfNull(giveaway);

Expand All @@ -536,7 +550,8 @@ public IReadOnlyList<DiscordMember> SelectWinners(Giveaway giveaway, IEnumerable
// in the event that there are zero valid winners
entrants.Remove(randomUserId);

if (ValidateUser(randomUserId, guild, out DiscordMember? member))
DiscordMember? member = await ValidateUserAsync(randomUserId, giveaway).ConfigureAwait(false);
if (member is not null)
winners.Add(member);
}

Expand Down Expand Up @@ -595,7 +610,7 @@ public async Task UpdateGiveawayLogMessageAsync(Giveaway giveaway)
return;
}

DiscordEmbed embed = CreateGiveawayLogEmbed(giveaway);
DiscordEmbed embed = await CreateGiveawayLogEmbedAsync(giveaway).ConfigureAwait(false);
await message.ModifyAsync(embed).ConfigureAwait(false);
}

Expand Down Expand Up @@ -667,21 +682,25 @@ public bool ValidateMember(DiscordMember member, DiscordGuild guild)
/// Validates that the user with the specified ID is a valid participant of giveaways in the specified guild.
/// </summary>
/// <param name="userId">The ID of the user to validate.</param>
/// <param name="guild">The guild in which to perform the validation check.</param>
/// <param name="member">
/// When this method returns, contains the <see cref="DiscordMember" /> with the ID <paramref name="userId" /> that is a
/// member of <paramref name="guild" />.
/// </param>
/// <param name="giveaway">The giveaway used for validation.</param>
/// <returns>
/// <see langword="true" /> if the user with the ID <paramref name="userId" /> is a valid a participant of giveaways in
/// <paramref name="guild" />; otherwise, <see langword="false" />.
/// A <see cref="DiscordMember" /> object encapsulating the member as part of the guild in <paramref name="giveaway" />,
/// if this user satisfies the giveaway requirements; otherwise, <see langword="null" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="guild" /> is <see langword="null" />.</exception>
public bool ValidateUser(ulong userId, DiscordGuild guild, [NotNullWhen(true)] out DiscordMember? member)
/// <exception cref="ArgumentNullException"><paramref name="giveaway" /> is <see langword="null" />.</exception>
public async Task<DiscordMember?> ValidateUserAsync(ulong userId, Giveaway giveaway)
{
ArgumentNullException.ThrowIfNull(guild);
member = null;
return userId != 0 && guild.Members.TryGetValue(userId, out member) && ValidateMember(member, guild);
ArgumentNullException.ThrowIfNull(giveaway);

DiscordGuild guild = await _discordClient.GetGuildAsync(giveaway.GuildId).ConfigureAwait(false);
DiscordMember? member = await guild.GetMemberOrNullAsync(userId).ConfigureAwait(false);

if (member is null) return null;
if (!ValidateMember(member, guild)) return null;
if (giveaway.ExcludedUsers.Contains(userId)) return null;
if (member.Roles.Any(r => giveaway.ExcludedRoles.Contains(r.Id))) return null;

return member;
}

private async Task UpdateFromDatabaseAsync(CancellationToken cancellationToken)
Expand Down