/// <summary> /// Manually removes an event, automatically denying it and preventing it from running. It also deleted the associated proposal. /// </summary> /// <param name="EventID">The unique ID of the target Event.</param> /// <returns>A <c>Task</c> object, which can be awaited until the method completes successfully.</returns> public async Task RemoveEvent(int EventID) { CommunityEvent Event = GetEvent(EventID); if (Event.Status == EventStatus.Released) { await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("Event already released!") .WithDescription("You can't remove an event that has already been released!") .WithCurrentTimestamp() .SendEmbed(Context.Channel); } if (TimerService.TimerExists(Event.ReleaseTimer)) { TimerService.RemoveTimer(Event.ReleaseTimer); } Event.Status = EventStatus.Removed; await UpdateEventProposal(Event.ID); CommunityEventsDB.SaveChanges(); await BuildEmbed(EmojiEnum.Love) .WithTitle("Event was successfully removed!") .WithDescription($"The event \"{Event.Description}\" has been removed from the proposal system.") .WithCurrentTimestamp() .SendEmbed(Context.Channel); }
/// <summary> /// A callback method to be called when an event suggestion is declined. It sets the status to denied and handles timer cleanup. /// </summary> /// <param name="EventID">The unique numerical ID for the target event.</param> /// <param name="Reason">The optional reason why the event was declined.</param> /// <returns>A <c>Task</c> object, which can be awaited until this method completes successfully.</returns> public async Task DeclineEventCallback(int EventID, string Reason = "") { CommunityEvent Event = GetEvent(EventID); IUser Proposer = DiscordSocketClient.GetUser(Event.ProposerID); if (Event == null) { return; } Event.ResolveReason = Reason; await UpdateEventProposal(Event.ID); if (TimerService.TimerExists(Event.ReleaseTimer)) { TimerService.RemoveTimer(Event.ReleaseTimer); } await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("Event has been declined!") .WithDescription($"Event #{Event.ID} has been declined.") .WithCurrentTimestamp() .SendDMAttachedEmbed(Context.Channel, BotConfiguration, Proposer, BuildEmbed(EmojiEnum.Love) .WithTitle("Your Event has been Declined!") .WithDescription(Event.Description) .AddField(Reason.Length > 0, "Reason: ", Reason)); Event.Status = EventStatus.Denied; CommunityEventsDB.SaveChanges(); }
/// <summary> /// A function to be called when an event is released or updated after its release date. /// </summary> /// <param name="EventID">The unique numeric ID of the target event.</param> /// <returns>A <c>Task</c> object, which can be awaited until the method completes successfully.</returns> public async Task ReleaseEvent(int EventID) { CommunityEvent Event = GetEvent(EventID); if (Event == null) { return; } if (Event.Status is EventStatus.Denied or EventStatus.Removed or EventStatus.Released) { return; } if (Event.Status == EventStatus.Expired && CommunityConfiguration.FailOnOverdueApproval) { return; } IUser User = DiscordSocketClient.GetUser(Event.ProposerID); if (Event.Status == EventStatus.Pending) { Event.Status = EventStatus.Expired; try { await BuildEmbed(EmojiEnum.Sign) .WithTitle("Event Expired") .WithDescription($"One of your events expired without the admins giving their feedback in time! \n{Event.Description}") .WithCurrentTimestamp() .SendEmbed(await User.GetOrCreateDMChannelAsync()); } catch (HttpException) { } } else { Event.Status = EventStatus.Released; ulong ChannelID = Event.EventType == EventType.Official ? CommunityConfiguration.OfficialEventsChannel : CommunityConfiguration.CommunityEventsChannel; IMessageChannel Channel = DiscordSocketClient.GetChannel(ChannelID) as IMessageChannel; string RoleMention = $"<@&{(Event.EventType == EventType.Official ? CommunityConfiguration.OfficialEventsNotifiedRole : CommunityConfiguration.CommunityEventsNotifiedRole)}>"; string ProposalText = $"**New {(Event.EventType == EventType.Official ? "Official" : "Community")} Event >>>** {RoleMention} \n" + $"*Event by <@{Event.ProposerID}>:* \n" + $"{Event.Description}"; await Channel.SendMessageAsync(text : ProposalText); } await UpdateEventProposal(Event.ID); CommunityEventsDB.SaveChanges(); }
/// <summary> /// Edits the target event to change its description to something new. It also deletes the old proposal and creates a new one with the updated information. /// </summary> /// <param name="EventID">The unique ID of the target event.</param> /// <param name="NewDescription">The new Description for the target event.</param> /// <returns>A <c>Task</c> object, which can be awaited until the method completes successfully.</returns> public async Task EditEvent(int EventID, string NewDescription) { CommunityEvent Event = GetEvent(EventID); if (NewDescription.Length > 1000) { await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("Event description too long!") .WithDescription($"Try to keep the description under 1000 characters, the current description is {NewDescription.Length} characters long.") .SendEmbed(Context.Channel); return; } if (Event.EventType != EventType.Official) { if (Event.Status is EventStatus.Approved or EventStatus.Denied or EventStatus.Removed or EventStatus.Released) { await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("Event is not pending!") .WithDescription($"This event has already been {Event.Status}; and thus can't be edited. \nYou can remove this event and propose a new one if you wish to change it.") .WithCurrentTimestamp() .SendEmbed(Context.Channel); return; } } Event.Description = NewDescription; await UpdateEventProposal(Event.ID); CommunityEventsDB.SaveChanges(); await BuildEmbed(EmojiEnum.Love) .WithTitle("Event was successfully edited!") .WithDescription($"Event #{Event.ID}'s description has been changed to: \n" + $"{Event.Description}") .WithCurrentTimestamp() .SendEmbed(Context.Channel); }
/// <summary> /// Sends an event for admin approval (if <paramref name="EventType"/> is Official, it skips this step), and adds it to the database. /// </summary> /// <param name="EventType">The type of event hosting, either official or user-hosted.</param> /// <param name="Proposer">The User who proposed the event in the first place.</param> /// <param name="Release">The Time when the event is to be released to the public.</param> /// <param name="Description">The event description containing all relevant information as to partake in the event and what it consists on.</param> /// <returns>A <c>Task</c> object, which can be awaited until the method completes successfully.</returns> public async Task AddEvent(EventType EventType, IGuildUser Proposer, DateTimeOffset Release, string Description) { if (Description.Length > 1000) { await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("Event description too long!") .WithDescription($"Try to keep the description under 1000 characters, the current description is {Description.Length} characters long.") .SendEmbed(Context.Channel); return; } int ID = CommunityEventsDB.GenerateToken(); EventStatus Status = EventType == EventType.UserHosted ? EventStatus.Pending : EventStatus.Approved; string ReleaseTimer = await CreateEventTimer(ReleaseEventCallback, new Dictionary <string, string> { { "ID", ID.ToString() } }, (int)Release.Subtract(DateTimeOffset.Now).TotalSeconds, TimerType.Expire); string ProposalText = $"**New Event Suggestion >>>** {CommunityConfiguration.EventsNotificationMention}"; string Link = Description.GetHyperLinks().FirstOrDefault(); if (Link != null && Link.Length > 0) { ProposalText += $"\n{Link}"; } ulong ProposalMessage = 0; if (EventType == EventType.UserHosted) { IMessageChannel MessageChannel = DiscordSocketClient.GetChannel(CommunityConfiguration.EventsNotificationsChannel) as IMessageChannel; Embed EventInfo = CreateEventProposalEmbed(ID, Status, Proposer, Release.ToOffset(TimeSpan.FromHours(BotConfiguration.StandardTimeZone)), Description).Build(); IMessage Proposal = await MessageChannel.SendMessageAsync( text : ProposalText, embed : EventInfo); ProposalMessage = Proposal.Id; } CommunityEventsDB.Events.Add( new() { ID = ID, EventType = EventType, ProposerID = Proposer.Id, DateTimeProposed = DateTimeOffset.Now.ToUnixTimeSeconds(), DateTimeRelease = Release.ToUnixTimeSeconds(), Description = Description, Status = Status, ResolveReason = "", EventProposal = ProposalMessage, ReleaseTimer = ReleaseTimer }); CommunityEventsDB.SaveChanges(); await UpdateEventProposal(ID); if (EventType == EventType.Official) { await BuildEmbed(EmojiEnum.Love) .WithTitle($"Event #{ID} Successfully Programmed!") .WithDescription($"The official server event has been successfully added to the database and will be displayed in <#{CommunityConfiguration.OfficialEventsChannel}> when its release time comes. \n" + $"You can always check the information of this event with the command `{BotConfiguration.Prefix}events get id {ID}`") .AddField("Release Time: ", $"{Release:ddd', 'MMM d 'at' hh:mm tt 'UTC'z} ({Release.Humanize()})") .SendEmbed(Context.Channel); } else { await BuildEmbed(EmojiEnum.Love) .WithTitle($"Event #{ID} Successfully Suggested!") .WithDescription($"Your suggestion went through! You will be informed when it is approved or declined. You can always check the status with `{BotConfiguration.Prefix}event get id {ID}`") .AddField("Release Time: ", $"{Release:ddd', 'MMM d 'at' hh:mm tt 'UTC'z} ({Release.Humanize()})") .WithCurrentTimestamp() .SendEmbed(Context.Channel); } }
/// <summary> /// A callback method to be called when an event suggestion is approved. It sets the status to approved and handles expired events. /// </summary> /// <param name="EventID">The unique numerical ID for the target event.</param> /// <param name="Reason">The optional reason for approval of the event.</param> /// <returns>A <c>Task</c> object, which can be awaited until this method completes successfully.</returns> public async Task ApproveEventCallback(int EventID, string Reason = "") { CommunityEvent Event = GetEvent(EventID); IUser Proposer = DiscordSocketClient.GetUser(Event.ProposerID); if (Event == null) { return; } if (Event.Status == EventStatus.Expired) { if (CommunityConfiguration.FailOnOverdueApproval) { await BuildEmbed(EmojiEnum.Annoyed) .WithTitle("This Event has already expired!") .WithDescription("Expired events can't be approved (FailOnOverdueApproval configuration is activated)") .WithCurrentTimestamp() .SendEmbed(Context.Channel); return; } else { await BuildEmbed(EmojiEnum.Sign) .WithTitle("This Event is overdue!") .WithDescription("The event will be released immediately (FailOnOverDueApproval configuration is off)") .WithCurrentTimestamp() .SendEmbed(Context.Channel); Event.Status = EventStatus.Approved; await ReleaseEvent(EventID); return; } } DateTimeOffset Release = DateTimeOffset.FromUnixTimeSeconds(Event.DateTimeRelease).ToOffset(TimeSpan.FromHours(BotConfiguration.StandardTimeZone)); if (!TimerService.TimerExists(Event.ReleaseTimer)) { Event.ReleaseTimer = await CreateEventTimer(ReleaseEventCallback, new Dictionary <string, string> { { "ID", Event.ID.ToString() } }, (int)Release.Subtract(DateTimeOffset.Now).TotalSeconds, TimerType.Expire); } Event.ResolveReason = Reason; Event.Status = EventStatus.Approved; await UpdateEventProposal(Event.ID); await BuildEmbed(EmojiEnum.Love) .WithTitle("Event approved for release!") .WithDescription($"Event #{Event.ID} will be released at {Release:ddd', 'MMM d 'at' hh:mm tt 'UTC'z} ({Release.Humanize()}).") .WithCurrentTimestamp() .SendDMAttachedEmbed(Context.Channel, BotConfiguration, Proposer, BuildEmbed(EmojiEnum.Love) .WithTitle("Your Event has been Approved!") .WithDescription(Event.Description) .AddField(Reason.Length > 0, "Reason: ", Reason) .AddField("Release Time:", $"{Release:ddd', 'MMM d 'at' hh:mm tt 'UTC'z}")); CommunityEventsDB.SaveChanges(); }