generated from zobweyt/Discord.Net.Template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using Discord.Interactions; | ||
|
||
namespace Giveaways; | ||
|
||
[method: ComplexParameterCtor] | ||
public class GiveawayStartArgs( | ||
[MinLength(2)] | ||
[MaxLength(128)] | ||
[Summary(description: "The prize of the giveaway.")] | ||
string prize, | ||
|
||
[MinValue(1)] | ||
[MaxValue(25)] | ||
[Summary(description: "The maximum number of winners for the giveaway.")] | ||
int winners, | ||
|
||
[Choice("1 hour", "1h")] | ||
[Choice("4 hours", "4h")] | ||
[Choice("8 hours", "8h")] | ||
[Choice("1 day", "1d")] | ||
[Choice("3 days", "3d")] | ||
[Choice("1 week", "7d")] | ||
[Summary(description: "The duration of the giveaway.")] | ||
TimeSpan duration) | ||
{ | ||
public string Prize => prize; | ||
public int MaxWinners => winners; | ||
public DateTime ExpiresAt => DateTime.Now + duration; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Discord; | ||
using Discord.Interactions; | ||
using Giveaways.Data; | ||
using Giveaways.Services; | ||
using Hangfire.States; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace Giveaways.Modules; | ||
|
||
[RequireContext(ContextType.Guild)] | ||
public class GiveawayModule : ModuleBase | ||
{ | ||
private readonly AppDbContext _db; | ||
private readonly GiveawayFormatter _formatter; | ||
private readonly GiveawayScheduler _scheduler; | ||
private readonly GiveawayService _service; | ||
|
||
public GiveawayModule(AppDbContext db, GiveawayFormatter formatter, GiveawayScheduler scheduler, GiveawayService service) | ||
{ | ||
_db = db; | ||
_formatter = formatter; | ||
_scheduler = scheduler; | ||
_service = service; | ||
} | ||
|
||
[DefaultMemberPermissions(GuildPermission.ManageEvents)] | ||
[SlashCommand("start", "Starts a new giveaway in the current channel.")] | ||
public async Task Start([ComplexParameter] GiveawayStartArgs args) | ||
{ | ||
await DeferAsync(); | ||
var response = await GetOriginalResponseAsync(); | ||
|
||
var giveaway = new Giveaway() | ||
{ | ||
MessageId = response.Id, | ||
ChannelId = Context.Channel.Id, | ||
GuildId = Context.Guild.Id, | ||
Prize = args.Prize, | ||
MaxWinners = args.MaxWinners, | ||
ExpiresAt = args.ExpiresAt, | ||
}; | ||
|
||
await _db.AddAsync(giveaway); | ||
await _db.SaveChangesAsync(); | ||
|
||
_scheduler.Schedule(giveaway.MessageId, giveaway.ExpiresAt); | ||
|
||
var props = _formatter.GetActiveMessageProperties(giveaway); | ||
await FollowupAsync(embed: props.Embed.Value, components: props.Components.Value); | ||
} | ||
|
||
[ComponentInteraction("join:*")] | ||
public async Task<RuntimeResult> Join(ulong messageId) | ||
{ | ||
await DeferAsync(true); | ||
|
||
var giveaway = await _db.Giveaways | ||
.Include(g => g.Participants) | ||
.FirstOrDefaultAsync(g => g.MessageId == messageId && g.Status == GiveawayStatus.Active); | ||
|
||
if (giveaway == null) | ||
return InteractionResult.FromError("There's no active giveaway associated with this message."); | ||
|
||
if (await _service.GetGiveawayMessage(giveaway) is not IUserMessage message) | ||
return InteractionResult.FromError("Unknown message!"); | ||
|
||
await _service.AddOrRemoveParticipantAsync(giveaway, Context.User.Id); | ||
|
||
var modifiedProps = _formatter.GetActiveMessageProperties(giveaway); | ||
await message.ModifyAsync(props => | ||
{ | ||
props.Embed = modifiedProps.Embed; | ||
props.Components = modifiedProps.Components; | ||
}); | ||
|
||
return InteractionResult.FromSuccess(); | ||
} | ||
|
||
[ComponentInteraction("info:*")] | ||
public async Task<RuntimeResult> Info(ulong messageId) | ||
{ | ||
await DeferAsync(true); | ||
|
||
var giveaway = await _db.Giveaways | ||
.Include(g => g.Participants) | ||
.FirstOrDefaultAsync(g => g.MessageId == messageId && g.Status != GiveawayStatus.Ended); | ||
|
||
if (giveaway == null) | ||
return InteractionResult.FromError("There's no active giveaway associated with this message."); | ||
|
||
var props = _formatter.GetInfoMessageProperties(giveaway); | ||
await FollowupAsync(embed: props.Embed.Value, ephemeral: true); | ||
|
||
return InteractionResult.FromSuccess(); | ||
} | ||
|
||
[DefaultMemberPermissions(GuildPermission.ManageEvents)] | ||
[MessageCommand("End Giveaway")] | ||
public async Task<RuntimeResult> End(IMessage message) | ||
{ | ||
await DeferAsync(true); | ||
|
||
if (await _db.Giveaways.AnyAsync(g => g.MessageId == message.Id && g.Status == GiveawayStatus.Ended)) | ||
return InteractionResult.FromError("There's no active giveaway associated with this message."); | ||
|
||
await _service.ExpireAsync(message.Id); | ||
_scheduler.ChangeState(message.Id, new SucceededState(0, 0, 0)); | ||
|
||
return InteractionResult.FromSuccess("This giveaway has just been ended! Winners have been notified via DMs."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
using System.Linq; | ||
using Discord; | ||
using Discord.WebSocket; | ||
using Giveaways.Data; | ||
|
||
namespace Giveaways.Services; | ||
|
||
public class GiveawayFormatter | ||
{ | ||
private readonly DiscordSocketClient _client; | ||
|
||
public GiveawayFormatter(DiscordSocketClient client) | ||
{ | ||
_client = client; | ||
} | ||
|
||
public MessageProperties GetActiveMessageProperties(Giveaway giveaway) | ||
{ | ||
var relative = TimestampTag.FormatFromDateTime(giveaway.ExpiresAt, TimestampTagStyles.Relative); | ||
var longDateTime = TimestampTag.FormatFromDateTime(giveaway.ExpiresAt, TimestampTagStyles.LongDateTime); | ||
|
||
var embed = new EmbedBuilder() | ||
.WithAuthor($"x{giveaway.MaxWinners} Giveaway Prizes", Icons.Gift) | ||
.WithTitle(giveaway.Prize) | ||
.AddField("Winners Selection Date", $"{relative} ({longDateTime})") | ||
.WithFooter($"{giveaway.Participants.Count} participants") | ||
.WithColor(Colors.Fuchsia) | ||
.Build(); | ||
|
||
var components = new ComponentBuilder() | ||
.WithButton("Join", $"join:{giveaway.MessageId}", ButtonStyle.Primary, new Emoji("🪅")) | ||
.WithButton("Learn more", $"info:{giveaway.MessageId}", ButtonStyle.Secondary, new Emoji("❓")) | ||
.Build(); | ||
|
||
return new MessageProperties | ||
{ | ||
Embed = embed, | ||
Components = components | ||
}; | ||
} | ||
|
||
public MessageProperties GetEndedMessageProperties(Giveaway giveaway) | ||
{ | ||
var ids = giveaway.Winners.Select(w => MentionUtils.MentionUser(w.UserId)); | ||
var mentions = string.Join(", ", ids); | ||
|
||
var embed = new EmbedBuilder() | ||
.WithAuthor($"x{giveaway.MaxWinners} Giveaway Prizes", Icons.Confetti) | ||
.WithTitle(giveaway.Prize) | ||
.AddField("Winners", string.IsNullOrEmpty(mentions) ? "None" : mentions) | ||
.WithFooter($"{giveaway.Participants.Count} participants") | ||
.WithColor(Colors.Secondary) | ||
.WithCurrentTimestamp() | ||
.Build(); | ||
|
||
return new MessageProperties() | ||
{ | ||
Embed = embed, | ||
Components = null | ||
}; | ||
} | ||
|
||
public MessageProperties GetInfoMessageProperties(Giveaway giveaway) | ||
{ | ||
var guild = _client.GetGuild(giveaway.GuildId); | ||
|
||
var longDate = TimestampTag.FormatFromDateTime(giveaway.ExpiresAt, TimestampTagStyles.LongDate); | ||
var longDateTime = TimestampTag.FormatFromDateTime(giveaway.ExpiresAt, TimestampTagStyles.LongDateTime); | ||
|
||
var embed = new EmbedBuilder() | ||
.WithTitle("About this event") | ||
.AddField("How it works?", $"On {longDate} the app is going to choose **{giveaway.MaxWinners}** random winners.") | ||
.AddField("Who can join?", $"{guild.EveryoneRole.Mention} can push the join button before {longDateTime} in order to enter.") | ||
.WithFooter($"The event organizers of {guild.Name} are responsible for awarding prizes.") | ||
.WithColor(Colors.Primary) | ||
.Build(); | ||
|
||
return new MessageProperties | ||
{ | ||
Embed = embed | ||
}; | ||
} | ||
|
||
public MessageProperties GetCongratsMessageProperties(GiveawayParticipant winner) | ||
{ | ||
var guild = _client.GetGuild(winner.Giveaway.GuildId); | ||
|
||
var embed = new EmbedBuilder() | ||
.WithAuthor($"Congrats!", Icons.Confetti) | ||
.WithTitle($"You won the {winner.Giveaway.Prize}!") | ||
.WithDescription($"To get your prize, visit **{guild.Name}** and contact their event organizers.") | ||
.WithFooter("The app is not responsible for awarding prizes.") | ||
.WithColor(Colors.Fuchsia) | ||
.Build(); | ||
|
||
var url = MessageUtils.FormatJumpUrl(winner.Giveaway.GuildId, winner.Giveaway.ChannelId, winner.Giveaway.MessageId); | ||
|
||
var components = new ComponentBuilder() | ||
.WithLink("Jump to Message", url, new Emoji("🎁")) | ||
.Build(); | ||
|
||
return new MessageProperties | ||
{ | ||
Embed = embed, | ||
Components = components | ||
}; | ||
} | ||
} |
Oops, something went wrong.