mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-01-31 09:09:00 +03:00
Force maximum Cognitive Complexity via InspectCode (#60)
This PR uses a new feature of the InspectCode action: extensions. By adding the `Cognitive Complexity` extension, InspectCode can provide warnings and force the contributors to follow standards regarding complex methods. This functionality was previously provided by CodeFactor, but it is no longer used. Here's the full changelog of this PR: - Allowed Actions to run on push for any branch, not just `master`; - Added `Cognitive Complexity` plugin for InspectCode; - Significantly reduced complexity of KickCommandGroup, MuteCommandGroup and GuildUpdateService --------- Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
8c72dc663e
commit
27b8f15e3b
11 changed files with 476 additions and 421 deletions
5
.github/workflows/resharper.yml
vendored
5
.github/workflows/resharper.yml
vendored
|
@ -4,12 +4,12 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
inspect-code:
|
||||
|
@ -32,4 +32,5 @@ jobs:
|
|||
with:
|
||||
solutionPath: ./Boyfriend.sln
|
||||
ignoreIssueType: InvertIf, ConvertIfStatementToSwitchStatement
|
||||
extensions: ReSharperPlugin.CognitiveComplexity
|
||||
solutionWideAnalysis: true
|
||||
|
|
|
@ -5,6 +5,7 @@ using Boyfriend.Services;
|
|||
using JetBrains.Annotations;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Conditions;
|
||||
|
@ -47,7 +48,7 @@ public class AboutCommandGroup : CommandGroup {
|
|||
[RequireContext(ChannelContext.Guild)]
|
||||
[Description("Shows Boyfriend's developers")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> SendAboutBotAsync() {
|
||||
public async Task<Result> ExecuteAboutAsync() {
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
||||
return Result.FromError(
|
||||
new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context"));
|
||||
|
@ -59,6 +60,10 @@ public class AboutCommandGroup : CommandGroup {
|
|||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
return await SendAboutBotAsync(currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> SendAboutBotAsync(IUser currentUser, CancellationToken ct = default) {
|
||||
var builder = new StringBuilder().AppendLine(Markdown.Bold(Messages.AboutTitleDevelopers));
|
||||
foreach (var dev in Developers)
|
||||
builder.AppendLine($"@{dev} — {$"AboutDeveloper@{dev}".Localized()}");
|
||||
|
@ -73,6 +78,6 @@ public class AboutCommandGroup : CommandGroup {
|
|||
.WithImageUrl("https://cdn.upload.systems/uploads/JFAaX5vr.png")
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class BanCommandGroup : CommandGroup {
|
|||
[RequireBotDiscordPermissions(DiscordPermission.BanMembers)]
|
||||
[Description("Ban user")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ExecuteBan(
|
||||
public async Task<Result> ExecuteBanAsync(
|
||||
[Description("User to ban")] IUser target,
|
||||
[Description("Ban reason")] string reason,
|
||||
[Description("Ban duration")] TimeSpan? duration = null) {
|
||||
|
@ -84,26 +84,26 @@ public class BanCommandGroup : CommandGroup {
|
|||
if (!guildResult.IsDefined(out var guild))
|
||||
return Result.FromError(guildResult);
|
||||
|
||||
return await BanUserAsync(target, reason, duration, guild, channelId.Value, user, currentUser);
|
||||
var data = await _dataService.GetData(guild.ID, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await BanUserAsync(
|
||||
target, reason, duration, guild, data, channelId.Value, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> BanUserAsync(
|
||||
IUser target, string reason, TimeSpan? duration, IGuild guild, Snowflake channelId,
|
||||
IUser user, IUser currentUser) {
|
||||
var data = await _dataService.GetData(guild.ID, CancellationToken);
|
||||
var cfg = data.Settings;
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
var existingBanResult = await _guildApi.GetGuildBanAsync(guild.ID, target.ID, CancellationToken);
|
||||
IUser target, string reason, TimeSpan? duration, IGuild guild, GuildData data, Snowflake channelId,
|
||||
IUser user, IUser currentUser, CancellationToken ct = default) {
|
||||
var existingBanResult = await _guildApi.GetGuildBanAsync(guild.ID, target.ID, ct);
|
||||
if (existingBanResult.IsDefined()) {
|
||||
var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserAlreadyBanned, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, ct);
|
||||
}
|
||||
|
||||
var interactionResult
|
||||
= await _utility.CheckInteractionsAsync(guild.ID, user.ID, target.ID, "Ban", CancellationToken);
|
||||
= await _utility.CheckInteractionsAsync(guild.ID, user.ID, target.ID, "Ban", ct);
|
||||
if (!interactionResult.IsSuccess)
|
||||
return Result.FromError(interactionResult);
|
||||
|
||||
|
@ -111,7 +111,7 @@ public class BanCommandGroup : CommandGroup {
|
|||
var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, ct);
|
||||
}
|
||||
|
||||
var builder = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason));
|
||||
|
@ -123,7 +123,7 @@ public class BanCommandGroup : CommandGroup {
|
|||
var title = string.Format(Messages.UserBanned, target.GetTag());
|
||||
var description = builder.ToString();
|
||||
|
||||
var dmChannelResult = await _userApi.CreateDMAsync(target.ID, CancellationToken);
|
||||
var dmChannelResult = await _userApi.CreateDMAsync(target.ID, ct);
|
||||
if (dmChannelResult.IsDefined(out var dmChannel)) {
|
||||
var dmEmbed = new EmbedBuilder().WithGuildTitle(guild)
|
||||
.WithTitle(Messages.YouWereBanned)
|
||||
|
@ -135,12 +135,12 @@ public class BanCommandGroup : CommandGroup {
|
|||
|
||||
if (!dmEmbed.IsDefined(out var dmBuilt))
|
||||
return Result.FromError(dmEmbed);
|
||||
await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: CancellationToken);
|
||||
await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: ct);
|
||||
}
|
||||
|
||||
var banResult = await _guildApi.CreateGuildBanAsync(
|
||||
guild.ID, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
ct: CancellationToken);
|
||||
ct: ct);
|
||||
if (!banResult.IsSuccess)
|
||||
return Result.FromError(banResult.Error);
|
||||
var memberData = data.GetMemberData(target.ID);
|
||||
|
@ -152,11 +152,12 @@ public class BanCommandGroup : CommandGroup {
|
|||
title, target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
var logResult = _utility.LogActionAsync(cfg, channelId, user, title, description, target, CancellationToken);
|
||||
var logResult = _utility.LogActionAsync(
|
||||
data.Settings, channelId, user, title, description, target, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -171,7 +172,7 @@ public class BanCommandGroup : CommandGroup {
|
|||
/// A feedback sending result which may or may not have succeeded. A successful result does not mean that the user
|
||||
/// was unbanned and vice-versa.
|
||||
/// </returns>
|
||||
/// <seealso cref="ExecuteBan" />
|
||||
/// <seealso cref="ExecuteBanAsync" />
|
||||
/// <seealso cref="GuildUpdateService.TickGuildAsync"/>
|
||||
[Command("unban")]
|
||||
[DiscordDefaultMemberPermissions(DiscordPermission.BanMembers)]
|
||||
|
@ -196,25 +197,27 @@ public class BanCommandGroup : CommandGroup {
|
|||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
return await UnbanUserAsync(target, reason, guildId.Value, channelId.Value, user, currentUser);
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await UnbanUserAsync(
|
||||
target, reason, guildId.Value, data, channelId.Value, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> UnbanUserAsync(
|
||||
IUser target, string reason, Snowflake guildId, Snowflake channelId, IUser user, IUser currentUser) {
|
||||
var cfg = await _dataService.GetSettings(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, target.ID, CancellationToken);
|
||||
IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user,
|
||||
IUser currentUser, CancellationToken ct = default) {
|
||||
var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, target.ID, ct);
|
||||
if (!existingBanResult.IsDefined()) {
|
||||
var errorEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserNotBanned, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, ct);
|
||||
}
|
||||
|
||||
var unbanResult = await _guildApi.RemoveGuildBanAsync(
|
||||
guildId, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
ct: CancellationToken);
|
||||
ct);
|
||||
if (!unbanResult.IsSuccess)
|
||||
return Result.FromError(unbanResult.Error);
|
||||
|
||||
|
@ -224,10 +227,10 @@ public class BanCommandGroup : CommandGroup {
|
|||
|
||||
var title = string.Format(Messages.UserUnbanned, target.GetTag());
|
||||
var description = string.Format(Messages.DescriptionActionReason, reason);
|
||||
var logResult = _utility.LogActionAsync(cfg, channelId, user, title, description, target, CancellationToken);
|
||||
var logResult = _utility.LogActionAsync(data.Settings, channelId, user, title, description, target, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,15 +76,15 @@ public class ClearCommandGroup : CommandGroup {
|
|||
if (!currentUserResult.IsDefined(out var currentUser))
|
||||
return Result.FromError(currentUserResult);
|
||||
|
||||
return await ClearMessagesAsync(amount, guildId.Value, channelId.Value, messages, user, currentUser);
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await ClearMessagesAsync(amount, data, channelId.Value, messages, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> ClearMessagesAsync(
|
||||
int amount, Snowflake guildId, Snowflake channelId, IReadOnlyList<IMessage> messages,
|
||||
IUser user, IUser currentUser) {
|
||||
var cfg = await _dataService.GetSettings(guildId, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
int amount, GuildData data, Snowflake channelId, IReadOnlyList<IMessage> messages,
|
||||
IUser user, IUser currentUser, CancellationToken ct = default) {
|
||||
var idList = new List<Snowflake>(messages.Count);
|
||||
var builder = new StringBuilder().AppendLine(Mention.Channel(channelId)).AppendLine();
|
||||
for (var i = messages.Count - 1; i >= 1; i--) { // '>= 1' to skip last message ('Boyfriend is thinking...')
|
||||
|
@ -98,18 +98,18 @@ public class ClearCommandGroup : CommandGroup {
|
|||
var description = builder.ToString();
|
||||
|
||||
var deleteResult = await _channelApi.BulkDeleteMessagesAsync(
|
||||
channelId, idList, user.GetTag().EncodeHeader(), CancellationToken);
|
||||
channelId, idList, user.GetTag().EncodeHeader(), ct);
|
||||
if (!deleteResult.IsSuccess)
|
||||
return Result.FromError(deleteResult.Error);
|
||||
|
||||
var logResult = _utility.LogActionAsync(
|
||||
cfg, channelId, user, title, description, currentUser, CancellationToken);
|
||||
data.Settings, channelId, user, title, description, currentUser, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(title, currentUser)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ using Remora.Commands.Attributes;
|
|||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Conditions;
|
||||
using Remora.Discord.Commands.Contexts;
|
||||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -62,21 +62,25 @@ public class KickCommandGroup : CommandGroup {
|
|||
[RequireBotDiscordPermissions(DiscordPermission.KickMembers)]
|
||||
[Description("Kick member")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> KickUserAsync(
|
||||
public async Task<Result> ExecuteKick(
|
||||
[Description("Member to kick")] IUser target,
|
||||
[Description("Kick reason")] string reason) {
|
||||
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId))
|
||||
return Result.FromError(
|
||||
new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context"));
|
||||
|
||||
// The current user's avatar is used when sending error messages
|
||||
var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||
if (!currentUserResult.IsDefined(out var currentUser))
|
||||
return Result.FromError(currentUserResult);
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
var guildResult = await _guildApi.GetGuildAsync(guildId.Value, ct: CancellationToken);
|
||||
if (!guildResult.IsDefined(out var guild))
|
||||
return Result.FromError(guildResult);
|
||||
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
var cfg = data.Settings;
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
var memberResult = await _guildApi.GetGuildMemberAsync(guildId.Value, target.ID, CancellationToken);
|
||||
if (!memberResult.IsSuccess) {
|
||||
|
@ -86,79 +90,57 @@ public class KickCommandGroup : CommandGroup {
|
|||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
}
|
||||
|
||||
return await KickUserAsync(target, reason, guild, channelId.Value, data, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> KickUserAsync(
|
||||
IUser target, string reason, IGuild guild, Snowflake channelId, GuildData data, IUser user, IUser currentUser,
|
||||
CancellationToken ct = default) {
|
||||
var interactionResult
|
||||
= await _utility.CheckInteractionsAsync(guildId.Value, userId.Value, target.ID, "Kick", CancellationToken);
|
||||
= await _utility.CheckInteractionsAsync(guild.ID, user.ID, target.ID, "Kick", ct);
|
||||
if (!interactionResult.IsSuccess)
|
||||
return Result.FromError(interactionResult);
|
||||
|
||||
Result<Embed> responseEmbed;
|
||||
if (interactionResult.Entity is not null) {
|
||||
responseEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
} else {
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
var dmChannelResult = await _userApi.CreateDMAsync(target.ID, CancellationToken);
|
||||
if (dmChannelResult.IsDefined(out var dmChannel)) {
|
||||
var guildResult = await _guildApi.GetGuildAsync(guildId.Value, ct: CancellationToken);
|
||||
if (!guildResult.IsDefined(out var guild))
|
||||
return Result.FromError(guildResult);
|
||||
|
||||
var dmEmbed = new EmbedBuilder().WithGuildTitle(guild)
|
||||
.WithTitle(Messages.YouWereKicked)
|
||||
.WithDescription(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.WithActionFooter(user)
|
||||
.WithCurrentTimestamp()
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
if (!dmEmbed.IsDefined(out var dmBuilt))
|
||||
return Result.FromError(dmEmbed);
|
||||
await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: CancellationToken);
|
||||
}
|
||||
|
||||
var kickResult = await _guildApi.RemoveGuildMemberAsync(
|
||||
guildId.Value, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
ct: CancellationToken);
|
||||
if (!kickResult.IsSuccess)
|
||||
return Result.FromError(kickResult.Error);
|
||||
data.GetMemberData(target.ID).Roles.Clear();
|
||||
|
||||
responseEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserKicked, target.GetTag()), target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
if ((!GuildSettings.PublicFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
|| (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)) {
|
||||
var logEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserKicked, target.GetTag()), target)
|
||||
.WithDescription(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.WithActionFooter(user)
|
||||
.WithCurrentTimestamp()
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
if (!logEmbed.IsDefined(out var logBuilt))
|
||||
return Result.FromError(logEmbed);
|
||||
|
||||
var builtArray = new[] { logBuilt };
|
||||
// Not awaiting to reduce response time
|
||||
if (GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PublicFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != GuildSettings.PublicFeedbackChannel.Get(cfg)
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
}
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, ct);
|
||||
}
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken);
|
||||
var dmChannelResult = await _userApi.CreateDMAsync(target.ID, ct);
|
||||
if (dmChannelResult.IsDefined(out var dmChannel)) {
|
||||
var dmEmbed = new EmbedBuilder().WithGuildTitle(guild)
|
||||
.WithTitle(Messages.YouWereKicked)
|
||||
.WithDescription(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.WithActionFooter(user)
|
||||
.WithCurrentTimestamp()
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
if (!dmEmbed.IsDefined(out var dmBuilt))
|
||||
return Result.FromError(dmEmbed);
|
||||
await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: ct);
|
||||
}
|
||||
|
||||
var kickResult = await _guildApi.RemoveGuildMemberAsync(
|
||||
guild.ID, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
ct);
|
||||
if (!kickResult.IsSuccess)
|
||||
return Result.FromError(kickResult.Error);
|
||||
data.GetMemberData(target.ID).Roles.Clear();
|
||||
|
||||
var title = string.Format(Messages.UserKicked, target.GetTag());
|
||||
var description = string.Format(Messages.DescriptionActionReason, reason);
|
||||
var logResult = _utility.LogActionAsync(
|
||||
data.Settings, channelId, user, title, description, target, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserKicked, target.GetTag()), target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ using Remora.Commands.Attributes;
|
|||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.API.Objects;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Conditions;
|
||||
using Remora.Discord.Commands.Contexts;
|
||||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -23,20 +23,17 @@ namespace Boyfriend.Commands;
|
|||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MuteCommandGroup : CommandGroup {
|
||||
private readonly IDiscordRestChannelAPI _channelApi;
|
||||
private readonly ICommandContext _context;
|
||||
private readonly GuildDataService _dataService;
|
||||
private readonly FeedbackService _feedbackService;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
private readonly UtilityService _utility;
|
||||
private readonly ICommandContext _context;
|
||||
private readonly GuildDataService _dataService;
|
||||
private readonly FeedbackService _feedbackService;
|
||||
private readonly IDiscordRestGuildAPI _guildApi;
|
||||
private readonly IDiscordRestUserAPI _userApi;
|
||||
private readonly UtilityService _utility;
|
||||
|
||||
public MuteCommandGroup(
|
||||
ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService dataService,
|
||||
FeedbackService feedbackService, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi,
|
||||
UtilityService utility) {
|
||||
ICommandContext context, GuildDataService dataService, FeedbackService feedbackService,
|
||||
IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, UtilityService utility) {
|
||||
_context = context;
|
||||
_channelApi = channelApi;
|
||||
_dataService = dataService;
|
||||
_feedbackService = feedbackService;
|
||||
_guildApi = guildApi;
|
||||
|
@ -57,7 +54,7 @@ public class MuteCommandGroup : CommandGroup {
|
|||
/// A feedback sending result which may or may not have succeeded. A successful result does not mean that the member
|
||||
/// was muted and vice-versa.
|
||||
/// </returns>
|
||||
/// <seealso cref="UnmuteUserAsync" />
|
||||
/// <seealso cref="ExecuteUnmute" />
|
||||
[Command("mute", "мут")]
|
||||
[DiscordDefaultMemberPermissions(DiscordPermission.ModerateMembers)]
|
||||
[DiscordDefaultDMPermission(false)]
|
||||
|
@ -66,7 +63,7 @@ public class MuteCommandGroup : CommandGroup {
|
|||
[RequireBotDiscordPermissions(DiscordPermission.ModerateMembers)]
|
||||
[Description("Mute member")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> MuteUserAsync(
|
||||
public async Task<Result> ExecuteMute(
|
||||
[Description("Member to mute")] IUser target,
|
||||
[Description("Mute reason")] string reason,
|
||||
[Description("Mute duration")] TimeSpan duration) {
|
||||
|
@ -79,6 +76,13 @@ public class MuteCommandGroup : CommandGroup {
|
|||
if (!currentUserResult.IsDefined(out var currentUser))
|
||||
return Result.FromError(currentUserResult);
|
||||
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
var memberResult = await _guildApi.GetGuildMemberAsync(guildId.Value, target.ID, CancellationToken);
|
||||
if (!memberResult.IsSuccess) {
|
||||
var embed = new EmbedBuilder().WithSmallTitle(Messages.UserNotFoundShort, currentUser)
|
||||
|
@ -87,71 +91,49 @@ public class MuteCommandGroup : CommandGroup {
|
|||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
}
|
||||
|
||||
return await MuteUserAsync(
|
||||
target, reason, duration, guildId.Value, data, channelId.Value, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> MuteUserAsync(
|
||||
IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId,
|
||||
IUser user, IUser currentUser, CancellationToken ct = default) {
|
||||
var interactionResult
|
||||
= await _utility.CheckInteractionsAsync(
|
||||
guildId.Value, userId.Value, target.ID, "Mute", CancellationToken);
|
||||
guildId, user.ID, target.ID, "Mute", ct);
|
||||
if (!interactionResult.IsSuccess)
|
||||
return Result.FromError(interactionResult);
|
||||
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
var cfg = data.Settings;
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
Result<Embed> responseEmbed;
|
||||
if (interactionResult.Entity is not null) {
|
||||
responseEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
} else {
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
var until = DateTimeOffset.UtcNow.Add(duration); // >:)
|
||||
var muteResult = await _guildApi.ModifyGuildMemberAsync(
|
||||
guildId.Value, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
communicationDisabledUntil: until, ct: CancellationToken);
|
||||
if (!muteResult.IsSuccess)
|
||||
return Result.FromError(muteResult.Error);
|
||||
|
||||
responseEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserMuted, target.GetTag()), target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
if ((!GuildSettings.PublicFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
|| (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)) {
|
||||
var builder = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.Append(
|
||||
string.Format(
|
||||
Messages.DescriptionActionExpiresAt, Markdown.Timestamp(until)));
|
||||
|
||||
var logEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserMuted, target.GetTag()), target)
|
||||
.WithDescription(builder.ToString())
|
||||
.WithActionFooter(user)
|
||||
.WithCurrentTimestamp()
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
if (!logEmbed.IsDefined(out var logBuilt))
|
||||
return Result.FromError(logEmbed);
|
||||
|
||||
var builtArray = new[] { logBuilt };
|
||||
// Not awaiting to reduce response time
|
||||
if (GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PublicFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != GuildSettings.PublicFeedbackChannel.Get(cfg)
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
}
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, ct);
|
||||
}
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken);
|
||||
var until = DateTimeOffset.UtcNow.Add(duration); // >:)
|
||||
var muteResult = await _guildApi.ModifyGuildMemberAsync(
|
||||
guildId, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
communicationDisabledUntil: until, ct: ct);
|
||||
if (!muteResult.IsSuccess)
|
||||
return Result.FromError(muteResult.Error);
|
||||
|
||||
var title = string.Format(Messages.UserMuted, target.GetTag());
|
||||
var description = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.Append(
|
||||
string.Format(
|
||||
Messages.DescriptionActionExpiresAt, Markdown.Timestamp(until))).ToString();
|
||||
|
||||
var logResult = _utility.LogActionAsync(
|
||||
data.Settings, channelId, user, title, description, target, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserMuted, target.GetTag()), target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -166,7 +148,7 @@ public class MuteCommandGroup : CommandGroup {
|
|||
/// A feedback sending result which may or may not have succeeded. A successful result does not mean that the member
|
||||
/// was unmuted and vice-versa.
|
||||
/// </returns>
|
||||
/// <seealso cref="MuteUserAsync" />
|
||||
/// <seealso cref="ExecuteMute" />
|
||||
/// <seealso cref="GuildUpdateService.TickGuildAsync"/>
|
||||
[Command("unmute", "размут")]
|
||||
[DiscordDefaultMemberPermissions(DiscordPermission.ModerateMembers)]
|
||||
|
@ -176,7 +158,7 @@ public class MuteCommandGroup : CommandGroup {
|
|||
[RequireBotDiscordPermissions(DiscordPermission.ModerateMembers)]
|
||||
[Description("Unmute member")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> UnmuteUserAsync(
|
||||
public async Task<Result> ExecuteUnmute(
|
||||
[Description("Member to unmute")] IUser target,
|
||||
[Description("Unmute reason")] string reason) {
|
||||
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId))
|
||||
|
@ -188,8 +170,13 @@ public class MuteCommandGroup : CommandGroup {
|
|||
if (!currentUserResult.IsDefined(out var currentUser))
|
||||
return Result.FromError(currentUserResult);
|
||||
|
||||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
// Needed to get the tag and avatar
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
var memberResult = await _guildApi.GetGuildMemberAsync(guildId.Value, target.ID, CancellationToken);
|
||||
if (!memberResult.IsSuccess) {
|
||||
|
@ -199,56 +186,43 @@ public class MuteCommandGroup : CommandGroup {
|
|||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
}
|
||||
|
||||
return await UnmuteUserAsync(
|
||||
target, reason, guildId.Value, data, channelId.Value, user, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> UnmuteUserAsync(
|
||||
IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user,
|
||||
IUser currentUser, CancellationToken ct = default) {
|
||||
var interactionResult
|
||||
= await _utility.CheckInteractionsAsync(
|
||||
guildId.Value, userId.Value, target.ID, "Unmute", CancellationToken);
|
||||
guildId, user.ID, target.ID, "Unmute", ct);
|
||||
if (!interactionResult.IsSuccess)
|
||||
return Result.FromError(interactionResult);
|
||||
|
||||
// Needed to get the tag and avatar
|
||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
if (interactionResult.Entity is not null) {
|
||||
var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||
.WithColour(ColorsList.Red).Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, ct);
|
||||
}
|
||||
|
||||
var unmuteResult = await _guildApi.ModifyGuildMemberAsync(
|
||||
guildId.Value, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
communicationDisabledUntil: null, ct: CancellationToken);
|
||||
guildId, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(),
|
||||
communicationDisabledUntil: null, ct: ct);
|
||||
if (!unmuteResult.IsSuccess)
|
||||
return Result.FromError(unmuteResult.Error);
|
||||
|
||||
var responseEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
var title = string.Format(Messages.UserUnmuted, target.GetTag());
|
||||
var description = string.Format(Messages.DescriptionActionReason, reason);
|
||||
var logResult = _utility.LogActionAsync(
|
||||
data.Settings, channelId, user, title, description, target, ct);
|
||||
if (!logResult.IsSuccess)
|
||||
return Result.FromError(logResult.Error);
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserUnmuted, target.GetTag()), target)
|
||||
.WithColour(ColorsList.Green).Build();
|
||||
|
||||
if ((!GuildSettings.PublicFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
|| (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty()
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)) {
|
||||
var logEmbed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.UserUnmuted, target.GetTag()), target)
|
||||
.WithDescription(string.Format(Messages.DescriptionActionReason, reason))
|
||||
.WithActionFooter(user)
|
||||
.WithCurrentTimestamp()
|
||||
.WithColour(ColorsList.Green)
|
||||
.Build();
|
||||
|
||||
if (!logEmbed.IsDefined(out var logBuilt))
|
||||
return Result.FromError(logEmbed);
|
||||
|
||||
var builtArray = new[] { logBuilt };
|
||||
|
||||
// Not awaiting to reduce response time
|
||||
if (GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PublicFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != GuildSettings.PublicFeedbackChannel.Get(cfg)
|
||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)
|
||||
_ = _channelApi.CreateMessageAsync(
|
||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: builtArray,
|
||||
ct: CancellationToken);
|
||||
}
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using Boyfriend.Services;
|
|||
using JetBrains.Annotations;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Conditions;
|
||||
|
@ -11,6 +12,7 @@ using Remora.Discord.Commands.Contexts;
|
|||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -49,7 +51,7 @@ public class PingCommandGroup : CommandGroup {
|
|||
[DiscordDefaultDMPermission(false)]
|
||||
[RequireContext(ChannelContext.Guild)]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> SendPingAsync() {
|
||||
public async Task<Result> ExecutePingAsync() {
|
||||
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out _))
|
||||
return Result.FromError(
|
||||
new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context"));
|
||||
|
@ -61,11 +63,16 @@ public class PingCommandGroup : CommandGroup {
|
|||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
return await SendLatencyAsync(channelId.Value, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> SendLatencyAsync(
|
||||
Snowflake channelId, IUser currentUser, CancellationToken ct = default) {
|
||||
var latency = _client.Latency.TotalMilliseconds;
|
||||
if (latency is 0) {
|
||||
// No heartbeat has occurred, estimate latency from local time and "Boyfriend is thinking..." message
|
||||
var lastMessageResult = await _channelApi.GetChannelMessagesAsync(
|
||||
channelId.Value, limit: 1, ct: CancellationToken);
|
||||
channelId, limit: 1, ct: ct);
|
||||
if (!lastMessageResult.IsDefined(out var lastMessage))
|
||||
return Result.FromError(lastMessageResult);
|
||||
latency = DateTimeOffset.UtcNow.Subtract(lastMessage.Single().Timestamp).TotalMilliseconds;
|
||||
|
@ -78,6 +85,6 @@ public class PingCommandGroup : CommandGroup {
|
|||
.WithCurrentTimestamp()
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using Boyfriend.Services;
|
|||
using JetBrains.Annotations;
|
||||
using Remora.Commands.Attributes;
|
||||
using Remora.Commands.Groups;
|
||||
using Remora.Discord.API.Abstractions.Objects;
|
||||
using Remora.Discord.API.Abstractions.Rest;
|
||||
using Remora.Discord.Commands.Attributes;
|
||||
using Remora.Discord.Commands.Conditions;
|
||||
|
@ -11,6 +12,7 @@ using Remora.Discord.Commands.Contexts;
|
|||
using Remora.Discord.Commands.Feedback.Services;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Extensions.Formatting;
|
||||
using Remora.Rest.Core;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -45,7 +47,7 @@ public class RemindCommandGroup : CommandGroup {
|
|||
[DiscordDefaultDMPermission(false)]
|
||||
[RequireContext(ChannelContext.Guild)]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> AddReminderAsync(
|
||||
public async Task<Result> ExecuteReminderAsync(
|
||||
[Description("After what period of time mention the reminder")]
|
||||
TimeSpan @in,
|
||||
[Description("Reminder message")] string message) {
|
||||
|
@ -57,12 +59,21 @@ public class RemindCommandGroup : CommandGroup {
|
|||
if (!userResult.IsDefined(out var user))
|
||||
return Result.FromError(userResult);
|
||||
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await AddReminderAsync(@in, message, data, channelId.Value, user, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> AddReminderAsync(
|
||||
TimeSpan @in, string message, GuildData data,
|
||||
Snowflake channelId, IUser user, CancellationToken ct = default) {
|
||||
var remindAt = DateTimeOffset.UtcNow.Add(@in);
|
||||
|
||||
(await _dataService.GetMemberData(guildId.Value, userId.Value, CancellationToken)).Reminders.Add(
|
||||
data.GetMemberData(user.ID).Reminders.Add(
|
||||
new Reminder {
|
||||
At = remindAt,
|
||||
Channel = channelId.Value.Value,
|
||||
Channel = channelId.Value,
|
||||
Text = message
|
||||
});
|
||||
|
||||
|
@ -71,6 +82,6 @@ public class RemindCommandGroup : CommandGroup {
|
|||
.WithColour(ColorsList.Green)
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using Boyfriend.Data;
|
||||
using Boyfriend.Data.Options;
|
||||
using Boyfriend.Services;
|
||||
|
@ -66,7 +67,7 @@ public class SettingsCommandGroup : CommandGroup {
|
|||
[RequireDiscordPermission(DiscordPermission.ManageGuild)]
|
||||
[Description("Shows settings list for this server")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> ListSettingsAsync() {
|
||||
public async Task<Result> ExecuteSettingsListAsync() {
|
||||
if (!_context.TryGetContextIDs(out var guildId, out _, out _))
|
||||
return Result.FromError(
|
||||
new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context"));
|
||||
|
@ -78,6 +79,10 @@ public class SettingsCommandGroup : CommandGroup {
|
|||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
|
||||
return await SendSettingsListAsync(cfg, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> SendSettingsListAsync(JsonNode cfg, IUser currentUser, CancellationToken ct = default) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
foreach (var option in AllOptions) {
|
||||
|
@ -91,7 +96,7 @@ public class SettingsCommandGroup : CommandGroup {
|
|||
.WithColour(ColorsList.Default)
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -107,7 +112,7 @@ public class SettingsCommandGroup : CommandGroup {
|
|||
[RequireDiscordPermission(DiscordPermission.ManageGuild)]
|
||||
[Description("Change settings for this server")]
|
||||
[UsedImplicitly]
|
||||
public async Task<Result> EditSettingsAsync(
|
||||
public async Task<Result> ExecuteSettingsAsync(
|
||||
[Description("The setting whose value you want to change")]
|
||||
string setting,
|
||||
[Description("Setting value")] string value) {
|
||||
|
@ -119,33 +124,38 @@ public class SettingsCommandGroup : CommandGroup {
|
|||
if (!currentUserResult.IsDefined(out var currentUser))
|
||||
return Result.FromError(currentUserResult);
|
||||
|
||||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||
var data = await _dataService.GetData(guildId.Value, CancellationToken);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
return await EditSettingAsync(setting, value, data, currentUser, CancellationToken);
|
||||
}
|
||||
|
||||
private async Task<Result> EditSettingAsync(
|
||||
string setting, string value, GuildData data, IUser currentUser, CancellationToken ct = default) {
|
||||
var option = AllOptions.Single(
|
||||
o => string.Equals(setting, o.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
var setResult = option.Set(cfg, value);
|
||||
var setResult = option.Set(data.Settings, value);
|
||||
if (!setResult.IsSuccess) {
|
||||
var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.SettingNotChanged, currentUser)
|
||||
.WithDescription(setResult.Error.Message)
|
||||
.WithColour(ColorsList.Red)
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, ct);
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append(Markdown.InlineCode(option.Name))
|
||||
.Append($" {Messages.SettingIsNow} ")
|
||||
.Append(option.Display(cfg));
|
||||
.Append(option.Display(data.Settings));
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(Messages.SettingSuccessfullyChanged, currentUser)
|
||||
.WithDescription(builder.ToString())
|
||||
.WithColour(ColorsList.Green)
|
||||
.Build();
|
||||
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, ct);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,6 @@ public class InteractionResponders : InteractionGroup {
|
|||
var idArray = state.Split(':');
|
||||
return (Result)await _feedbackService.SendContextualAsync(
|
||||
$"https://discord.com/events/{idArray[0]}/{idArray[1]}",
|
||||
options: new FeedbackMessageOptions(MessageFlags: MessageFlags.Ephemeral));
|
||||
options: new FeedbackMessageOptions(MessageFlags: MessageFlags.Ephemeral), ct: CancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,106 +116,131 @@ public class GuildUpdateService : BackgroundService {
|
|||
private async Task TickGuildAsync(Snowflake guildId, CancellationToken ct = default) {
|
||||
var data = await _dataService.GetData(guildId, ct);
|
||||
Messages.Culture = GuildSettings.Language.Get(data.Settings);
|
||||
|
||||
var defaultRole = GuildSettings.DefaultRole.Get(data.Settings);
|
||||
|
||||
foreach (var memberData in data.MemberData.Values) {
|
||||
var userId = memberData.Id.ToSnowflake();
|
||||
var userResult = await _userApi.GetUserAsync(memberData.Id.ToSnowflake(), ct);
|
||||
if (!userResult.IsDefined(out var user)) return;
|
||||
|
||||
if (defaultRole.Value is not 0 && !memberData.Roles.Contains(defaultRole.Value))
|
||||
_ = _guildApi.AddGuildMemberRoleAsync(
|
||||
guildId, userId, defaultRole, ct: ct);
|
||||
|
||||
if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
|
||||
var unbanResult = await _guildApi.RemoveGuildBanAsync(
|
||||
guildId, userId, Messages.PunishmentExpired.EncodeHeader(), ct);
|
||||
if (unbanResult.IsSuccess)
|
||||
memberData.BannedUntil = null;
|
||||
else
|
||||
_logger.LogWarning(
|
||||
"Error in automatic user unban request.\n{ErrorMessage}", unbanResult.Error.Message);
|
||||
}
|
||||
|
||||
var userResult = await _userApi.GetUserAsync(userId, ct);
|
||||
if (!userResult.IsDefined(out var user)) continue;
|
||||
|
||||
for (var i = memberData.Reminders.Count - 1; i >= 0; i--) {
|
||||
var reminder = memberData.Reminders[i];
|
||||
if (DateTimeOffset.UtcNow < reminder.At) continue;
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.Reminder, user.GetTag()), user)
|
||||
.WithDescription(
|
||||
string.Format(Messages.DescriptionReminder, Markdown.InlineCode(reminder.Text)))
|
||||
.WithColour(ColorsList.Magenta)
|
||||
.Build();
|
||||
|
||||
if (!embed.IsDefined(out var built)) continue;
|
||||
|
||||
var messageResult = await _channelApi.CreateMessageAsync(
|
||||
reminder.Channel.ToSnowflake(), Mention.User(user), embeds: new[] { built }, ct: ct);
|
||||
if (!messageResult.IsSuccess)
|
||||
_logger.LogWarning(
|
||||
"Error in reminder send.\n{ErrorMessage}", messageResult.Error.Message);
|
||||
|
||||
memberData.Reminders.Remove(reminder);
|
||||
}
|
||||
await TickMemberAsync(guildId, user, memberData, defaultRole, ct);
|
||||
}
|
||||
|
||||
var eventsResult = await _eventApi.ListScheduledEventsForGuildAsync(guildId, ct: ct);
|
||||
if (!eventsResult.IsDefined(out var events)) return;
|
||||
|
||||
if (GuildSettings.EventNotificationChannel.Get(data.Settings).Empty()) return;
|
||||
if (!eventsResult.IsSuccess)
|
||||
_logger.LogWarning("Error retrieving scheduled events.\n{ErrorMessage}", eventsResult.Error.Message);
|
||||
else if (!GuildSettings.EventNotificationChannel.Get(data.Settings).Empty())
|
||||
await TickScheduledEventsAsync(guildId, data, eventsResult.Entity, ct);
|
||||
}
|
||||
|
||||
private async Task TickScheduledEventsAsync(
|
||||
Snowflake guildId, GuildData data, IEnumerable<IGuildScheduledEvent> events, CancellationToken ct) {
|
||||
foreach (var scheduledEvent in events) {
|
||||
if (!data.ScheduledEvents.ContainsKey(scheduledEvent.ID.Value)) {
|
||||
if (!data.ScheduledEvents.ContainsKey(scheduledEvent.ID.Value))
|
||||
data.ScheduledEvents.Add(scheduledEvent.ID.Value, new ScheduledEventData(scheduledEvent.Status));
|
||||
} else {
|
||||
var storedEvent = data.ScheduledEvents[scheduledEvent.ID.Value];
|
||||
if (storedEvent.Status == scheduledEvent.Status) {
|
||||
if (DateTimeOffset.UtcNow >= scheduledEvent.ScheduledStartTime) {
|
||||
if (GuildSettings.AutoStartEvents.Get(data.Settings)
|
||||
&& scheduledEvent.Status is not GuildScheduledEventStatus.Active) {
|
||||
var startResult = await _eventApi.ModifyGuildScheduledEventAsync(
|
||||
guildId, scheduledEvent.ID,
|
||||
status: GuildScheduledEventStatus.Active, ct: ct);
|
||||
if (!startResult.IsSuccess)
|
||||
_logger.LogWarning(
|
||||
"Error in automatic scheduled event start request.\n{ErrorMessage}",
|
||||
startResult.Error.Message);
|
||||
}
|
||||
} else if (GuildSettings.EventEarlyNotificationOffset.Get(data.Settings) != TimeSpan.Zero
|
||||
&& !storedEvent.EarlyNotificationSent
|
||||
&& DateTimeOffset.UtcNow
|
||||
>= scheduledEvent.ScheduledStartTime
|
||||
- GuildSettings.EventEarlyNotificationOffset.Get(data.Settings)) {
|
||||
var earlyResult = await SendScheduledEventUpdatedMessage(scheduledEvent, data, true, ct);
|
||||
if (earlyResult.IsSuccess)
|
||||
storedEvent.EarlyNotificationSent = true;
|
||||
else
|
||||
_logger.LogWarning(
|
||||
"Error in scheduled event early notification sender.\n{ErrorMessage}",
|
||||
earlyResult.Error.Message);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
storedEvent.Status = scheduledEvent.Status;
|
||||
var storedEvent = data.ScheduledEvents[scheduledEvent.ID.Value];
|
||||
if (storedEvent.Status == scheduledEvent.Status) {
|
||||
await TickScheduledEventAsync(guildId, data, scheduledEvent, storedEvent, ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = scheduledEvent.Status switch {
|
||||
storedEvent.Status = scheduledEvent.Status;
|
||||
|
||||
var statusChangedResponseResult = storedEvent.Status switch {
|
||||
GuildScheduledEventStatus.Scheduled =>
|
||||
await SendScheduledEventCreatedMessage(scheduledEvent, data.Settings, ct),
|
||||
GuildScheduledEventStatus.Active or GuildScheduledEventStatus.Completed =>
|
||||
await SendScheduledEventUpdatedMessage(scheduledEvent, data, false, ct),
|
||||
await SendScheduledEventUpdatedMessage(scheduledEvent, data, ct),
|
||||
_ => Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status)))
|
||||
};
|
||||
|
||||
if (!result.IsSuccess)
|
||||
_logger.LogWarning("Error in guild update.\n{ErrorMessage}", result.Error.Message);
|
||||
if (!statusChangedResponseResult.IsSuccess)
|
||||
_logger.LogWarning(
|
||||
"Error handling scheduled event status update.\n{ErrorMessage}",
|
||||
statusChangedResponseResult.Error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TickScheduledEventAsync(
|
||||
Snowflake guildId, GuildData data, IGuildScheduledEvent scheduledEvent, ScheduledEventData eventData,
|
||||
CancellationToken ct) {
|
||||
if (DateTimeOffset.UtcNow >= scheduledEvent.ScheduledStartTime) {
|
||||
await TryAutoStartEventAsync(guildId, data, scheduledEvent, ct);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GuildSettings.EventEarlyNotificationOffset.Get(data.Settings) == TimeSpan.Zero
|
||||
|| eventData.EarlyNotificationSent
|
||||
|| DateTimeOffset.UtcNow
|
||||
< scheduledEvent.ScheduledStartTime
|
||||
- GuildSettings.EventEarlyNotificationOffset.Get(data.Settings)) return;
|
||||
|
||||
var earlyResult = await SendEarlyEventNotificationAsync(scheduledEvent, data, ct);
|
||||
if (earlyResult.IsSuccess) {
|
||||
eventData.EarlyNotificationSent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogWarning(
|
||||
"Error in scheduled event early notification sender.\n{ErrorMessage}",
|
||||
earlyResult.Error.Message);
|
||||
}
|
||||
|
||||
private async Task TryAutoStartEventAsync(
|
||||
Snowflake guildId, GuildData data, IGuildScheduledEvent scheduledEvent, CancellationToken ct) {
|
||||
if (GuildSettings.AutoStartEvents.Get(data.Settings)
|
||||
&& scheduledEvent.Status is not GuildScheduledEventStatus.Active) {
|
||||
var startResult = await _eventApi.ModifyGuildScheduledEventAsync(
|
||||
guildId, scheduledEvent.ID,
|
||||
status: GuildScheduledEventStatus.Active, ct: ct);
|
||||
if (!startResult.IsSuccess)
|
||||
_logger.LogWarning(
|
||||
"Error in automatic scheduled event start request.\n{ErrorMessage}",
|
||||
startResult.Error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TickMemberAsync(
|
||||
Snowflake guildId, IUser user, MemberData memberData, Snowflake defaultRole, CancellationToken ct) {
|
||||
if (defaultRole.Value is not 0 && !memberData.Roles.Contains(defaultRole.Value))
|
||||
_ = _guildApi.AddGuildMemberRoleAsync(
|
||||
guildId, user.ID, defaultRole, ct: ct);
|
||||
|
||||
if (DateTimeOffset.UtcNow > memberData.BannedUntil) {
|
||||
var unbanResult = await _guildApi.RemoveGuildBanAsync(
|
||||
guildId, user.ID, Messages.PunishmentExpired.EncodeHeader(), ct);
|
||||
if (unbanResult.IsSuccess)
|
||||
memberData.BannedUntil = null;
|
||||
else
|
||||
_logger.LogWarning(
|
||||
"Error in automatic user unban request.\n{ErrorMessage}", unbanResult.Error.Message);
|
||||
}
|
||||
|
||||
for (var i = memberData.Reminders.Count - 1; i >= 0; i--)
|
||||
await TickReminderAsync(memberData.Reminders[i], user, memberData, ct);
|
||||
}
|
||||
|
||||
private async Task TickReminderAsync(Reminder reminder, IUser user, MemberData memberData, CancellationToken ct) {
|
||||
if (DateTimeOffset.UtcNow < reminder.At) return;
|
||||
|
||||
var embed = new EmbedBuilder().WithSmallTitle(
|
||||
string.Format(Messages.Reminder, user.GetTag()), user)
|
||||
.WithDescription(
|
||||
string.Format(Messages.DescriptionReminder, Markdown.InlineCode(reminder.Text)))
|
||||
.WithColour(ColorsList.Magenta)
|
||||
.Build();
|
||||
|
||||
if (!embed.IsDefined(out var built)) return;
|
||||
|
||||
var messageResult = await _channelApi.CreateMessageAsync(
|
||||
reminder.Channel.ToSnowflake(), Mention.User(user), embeds: new[] { built }, ct: ct);
|
||||
if (!messageResult.IsSuccess)
|
||||
_logger.LogWarning(
|
||||
"Error in reminder send.\n{ErrorMessage}", messageResult.Error.Message);
|
||||
|
||||
memberData.Reminders.Remove(reminder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending a notification, mentioning the <see cref="GuildSettings.EventNotificationRole" /> if one is
|
||||
/// set,
|
||||
|
@ -228,47 +253,23 @@ public class GuildUpdateService : BackgroundService {
|
|||
/// <returns>A notification sending result which may or may not have succeeded.</returns>
|
||||
private async Task<Result> SendScheduledEventCreatedMessage(
|
||||
IGuildScheduledEvent scheduledEvent, JsonNode settings, CancellationToken ct = default) {
|
||||
if (!scheduledEvent.Creator.IsDefined(out var creator))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.Creator)));
|
||||
|
||||
if (!scheduledEvent.CreatorID.IsDefined(out var creatorId))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.CreatorID)));
|
||||
var creatorResult = await _userApi.GetUserAsync(creatorId.Value, ct);
|
||||
if (!creatorResult.IsDefined(out var creator)) return Result.FromError(creatorResult);
|
||||
|
||||
string embedDescription;
|
||||
Result<string> embedDescriptionResult;
|
||||
var eventDescription = scheduledEvent.Description is { HasValue: true, Value: not null }
|
||||
? scheduledEvent.Description.Value
|
||||
: string.Empty;
|
||||
switch (scheduledEvent.EntityType) {
|
||||
case GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice:
|
||||
if (!scheduledEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.ChannelID)));
|
||||
embedDescriptionResult = scheduledEvent.EntityType switch {
|
||||
GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice =>
|
||||
GetLocalEventCreatedEmbedDescription(scheduledEvent, eventDescription),
|
||||
GuildScheduledEventEntityType.External => GetExternalScheduledEventCreatedEmbedDescription(
|
||||
scheduledEvent, eventDescription),
|
||||
_ => Result<string>.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.EntityType)))
|
||||
};
|
||||
|
||||
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||
string.Format(
|
||||
Messages.DescriptionLocalEventCreated,
|
||||
Markdown.Timestamp(scheduledEvent.ScheduledStartTime),
|
||||
Mention.Channel(channelId)
|
||||
))}";
|
||||
break;
|
||||
case GuildScheduledEventEntityType.External:
|
||||
if (!scheduledEvent.EntityMetadata.AsOptional().IsDefined(out var metadata))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.EntityMetadata)));
|
||||
if (!scheduledEvent.ScheduledEndTime.AsOptional().IsDefined(out var endTime))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.ScheduledEndTime)));
|
||||
if (!metadata.Location.IsDefined(out var location))
|
||||
return Result.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
||||
|
||||
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||
string.Format(
|
||||
Messages.DescriptionExternalEventCreated,
|
||||
Markdown.Timestamp(scheduledEvent.ScheduledStartTime),
|
||||
Markdown.Timestamp(endTime),
|
||||
Markdown.InlineCode(location)
|
||||
))}";
|
||||
break;
|
||||
default:
|
||||
return Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.EntityType)));
|
||||
}
|
||||
if (!embedDescriptionResult.IsDefined(out var embedDescription))
|
||||
return Result.FromError(embedDescriptionResult);
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithSmallTitle(string.Format(Messages.EventCreatedTitle, creator.GetTag()), creator)
|
||||
|
@ -297,92 +298,153 @@ public class GuildUpdateService : BackgroundService {
|
|||
components: new[] { new ActionRowComponent(new[] { button }) }, ct: ct);
|
||||
}
|
||||
|
||||
private static Result<string> GetExternalScheduledEventCreatedEmbedDescription(
|
||||
IGuildScheduledEvent scheduledEvent, string eventDescription) {
|
||||
Result<string> embedDescription;
|
||||
if (!scheduledEvent.EntityMetadata.AsOptional().IsDefined(out var metadata))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.EntityMetadata)));
|
||||
if (!scheduledEvent.ScheduledEndTime.AsOptional().IsDefined(out var endTime))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.ScheduledEndTime)));
|
||||
if (!metadata.Location.IsDefined(out var location))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
||||
|
||||
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||
string.Format(
|
||||
Messages.DescriptionExternalEventCreated,
|
||||
Markdown.Timestamp(scheduledEvent.ScheduledStartTime),
|
||||
Markdown.Timestamp(endTime),
|
||||
Markdown.InlineCode(location)
|
||||
))}";
|
||||
return embedDescription;
|
||||
}
|
||||
|
||||
private static Result<string> GetLocalEventCreatedEmbedDescription(
|
||||
IGuildScheduledEvent scheduledEvent, string eventDescription) {
|
||||
if (!scheduledEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.ChannelID)));
|
||||
|
||||
return $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||
string.Format(
|
||||
Messages.DescriptionLocalEventCreated,
|
||||
Markdown.Timestamp(scheduledEvent.ScheduledStartTime),
|
||||
Mention.Channel(channelId)
|
||||
))}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles sending a notification, mentioning the <see cref="GuildSettings.EventNotificationRole"/> and event subscribers,
|
||||
/// when a scheduled event is about to start, has started or completed
|
||||
/// when a scheduled event has started or completed
|
||||
/// in a guild's <see cref="GuildSettings.EventNotificationChannel" /> if one is set.
|
||||
/// </summary>
|
||||
/// <param name="scheduledEvent">The scheduled event that is about to start, has started or completed.</param>
|
||||
/// <param name="data">The data for the guild containing the scheduled event.</param>
|
||||
/// <param name="early">Controls whether or not a reminder for the scheduled event should be sent instead of the event started/completed notification</param>
|
||||
/// <param name="ct">The cancellation token for this operation</param>
|
||||
/// <returns>A reminder/notification sending result which may or may not have succeeded.</returns>
|
||||
private async Task<Result> SendScheduledEventUpdatedMessage(
|
||||
IGuildScheduledEvent scheduledEvent, GuildData data, bool early, CancellationToken ct = default) {
|
||||
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
||||
IGuildScheduledEvent scheduledEvent, GuildData data, CancellationToken ct = default) {
|
||||
if (scheduledEvent.Status == GuildScheduledEventStatus.Active) {
|
||||
data.ScheduledEvents[scheduledEvent.ID.Value].ActualStartTime = DateTimeOffset.UtcNow;
|
||||
|
||||
var embed = new EmbedBuilder();
|
||||
string? content = null;
|
||||
if (early)
|
||||
embed.WithSmallTitle(string.Format(Messages.EventEarlyNotification, scheduledEvent.Name), currentUser)
|
||||
.WithColour(ColorsList.Default);
|
||||
else
|
||||
switch (scheduledEvent.Status) {
|
||||
case GuildScheduledEventStatus.Active:
|
||||
data.ScheduledEvents[scheduledEvent.ID.Value].ActualStartTime = DateTimeOffset.UtcNow;
|
||||
var embedDescriptionResult = scheduledEvent.EntityType switch {
|
||||
GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice =>
|
||||
GetLocalEventStartedEmbedDescription(scheduledEvent),
|
||||
GuildScheduledEventEntityType.External => GetExternalEventStartedEmbedDescription(scheduledEvent),
|
||||
_ => Result<string>.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.EntityType)))
|
||||
};
|
||||
|
||||
string embedDescription;
|
||||
switch (scheduledEvent.EntityType) {
|
||||
case GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice:
|
||||
if (!scheduledEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.ChannelID)));
|
||||
var contentResult = await _utility.GetEventNotificationMentions(
|
||||
scheduledEvent, data.Settings, ct);
|
||||
if (!contentResult.IsDefined(out var content))
|
||||
return Result.FromError(contentResult);
|
||||
if (!embedDescriptionResult.IsDefined(out var embedDescription))
|
||||
return Result.FromError(embedDescriptionResult);
|
||||
|
||||
embedDescription = string.Format(
|
||||
Messages.DescriptionLocalEventStarted,
|
||||
Mention.Channel(channelId)
|
||||
);
|
||||
break;
|
||||
case GuildScheduledEventEntityType.External:
|
||||
if (!scheduledEvent.EntityMetadata.AsOptional().IsDefined(out var metadata))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.EntityMetadata)));
|
||||
if (!scheduledEvent.ScheduledEndTime.AsOptional().IsDefined(out var endTime))
|
||||
return Result.FromError(new ArgumentNullError(nameof(scheduledEvent.ScheduledEndTime)));
|
||||
if (!metadata.Location.IsDefined(out var location))
|
||||
return Result.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
||||
var startedEmbed = new EmbedBuilder().WithTitle(string.Format(Messages.EventStarted, scheduledEvent.Name))
|
||||
.WithDescription(embedDescription)
|
||||
.WithColour(ColorsList.Green)
|
||||
.WithCurrentTimestamp()
|
||||
.Build();
|
||||
|
||||
embedDescription = string.Format(
|
||||
Messages.DescriptionExternalEventStarted,
|
||||
Markdown.InlineCode(location),
|
||||
Markdown.Timestamp(endTime)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.EntityType)));
|
||||
}
|
||||
if (!startedEmbed.IsDefined(out var startedBuilt)) return Result.FromError(startedEmbed);
|
||||
|
||||
var contentResult = await _utility.GetEventNotificationMentions(
|
||||
scheduledEvent, data.Settings, ct);
|
||||
if (!contentResult.IsDefined(out content))
|
||||
return Result.FromError(contentResult);
|
||||
return (Result)await _channelApi.CreateMessageAsync(
|
||||
GuildSettings.EventNotificationChannel.Get(data.Settings),
|
||||
content, embeds: new[] { startedBuilt }, ct: ct);
|
||||
}
|
||||
|
||||
embed.WithTitle(string.Format(Messages.EventStarted, scheduledEvent.Name))
|
||||
.WithDescription(embedDescription)
|
||||
.WithColour(ColorsList.Green);
|
||||
break;
|
||||
case GuildScheduledEventStatus.Completed:
|
||||
embed.WithTitle(string.Format(Messages.EventCompleted, scheduledEvent.Name))
|
||||
.WithDescription(
|
||||
string.Format(
|
||||
Messages.EventDuration,
|
||||
DateTimeOffset.UtcNow.Subtract(
|
||||
data.ScheduledEvents[scheduledEvent.ID.Value].ActualStartTime
|
||||
?? scheduledEvent.ScheduledStartTime).ToString()))
|
||||
.WithColour(ColorsList.Black);
|
||||
if (scheduledEvent.Status != GuildScheduledEventStatus.Completed)
|
||||
return Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status)));
|
||||
data.ScheduledEvents.Remove(scheduledEvent.ID.Value);
|
||||
|
||||
data.ScheduledEvents.Remove(scheduledEvent.ID.Value);
|
||||
break;
|
||||
case GuildScheduledEventStatus.Canceled:
|
||||
case GuildScheduledEventStatus.Scheduled:
|
||||
default: return Result.FromError(new ArgumentOutOfRangeError(nameof(scheduledEvent.Status)));
|
||||
}
|
||||
var completedEmbed = new EmbedBuilder().WithTitle(string.Format(Messages.EventCompleted, scheduledEvent.Name))
|
||||
.WithDescription(
|
||||
string.Format(
|
||||
Messages.EventDuration,
|
||||
DateTimeOffset.UtcNow.Subtract(
|
||||
data.ScheduledEvents[scheduledEvent.ID.Value].ActualStartTime
|
||||
?? scheduledEvent.ScheduledStartTime).ToString()))
|
||||
.WithColour(ColorsList.Black)
|
||||
.WithCurrentTimestamp()
|
||||
.Build();
|
||||
|
||||
var result = embed.WithCurrentTimestamp().Build();
|
||||
|
||||
if (!result.IsDefined(out var built)) return Result.FromError(result);
|
||||
if (!completedEmbed.IsDefined(out var completedBuilt))
|
||||
return Result.FromError(completedEmbed);
|
||||
|
||||
return (Result)await _channelApi.CreateMessageAsync(
|
||||
GuildSettings.EventNotificationChannel.Get(data.Settings),
|
||||
content ?? default(Optional<string>), embeds: new[] { built }, ct: ct);
|
||||
embeds: new[] { completedBuilt }, ct: ct);
|
||||
}
|
||||
|
||||
private static Result<string> GetLocalEventStartedEmbedDescription(IGuildScheduledEvent scheduledEvent) {
|
||||
Result<string> embedDescription;
|
||||
if (!scheduledEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.ChannelID)));
|
||||
|
||||
embedDescription = string.Format(
|
||||
Messages.DescriptionLocalEventStarted,
|
||||
Mention.Channel(channelId)
|
||||
);
|
||||
return embedDescription;
|
||||
}
|
||||
|
||||
private static Result<string> GetExternalEventStartedEmbedDescription(IGuildScheduledEvent scheduledEvent) {
|
||||
Result<string> embedDescription;
|
||||
if (!scheduledEvent.EntityMetadata.AsOptional().IsDefined(out var metadata))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.EntityMetadata)));
|
||||
if (!scheduledEvent.ScheduledEndTime.AsOptional().IsDefined(out var endTime))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(scheduledEvent.ScheduledEndTime)));
|
||||
if (!metadata.Location.IsDefined(out var location))
|
||||
return Result<string>.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
||||
|
||||
embedDescription = string.Format(
|
||||
Messages.DescriptionExternalEventStarted,
|
||||
Markdown.InlineCode(location),
|
||||
Markdown.Timestamp(endTime)
|
||||
);
|
||||
return embedDescription;
|
||||
}
|
||||
|
||||
private async Task<Result> SendEarlyEventNotificationAsync(
|
||||
IGuildScheduledEvent scheduledEvent, GuildData data, CancellationToken ct) {
|
||||
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||
if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult);
|
||||
|
||||
var contentResult = await _utility.GetEventNotificationMentions(
|
||||
scheduledEvent, data.Settings, ct);
|
||||
if (!contentResult.IsDefined(out var content))
|
||||
return Result.FromError(contentResult);
|
||||
|
||||
var earlyResult = new EmbedBuilder()
|
||||
.WithSmallTitle(string.Format(Messages.EventEarlyNotification, scheduledEvent.Name), currentUser)
|
||||
.WithColour(ColorsList.Default)
|
||||
.WithCurrentTimestamp()
|
||||
.Build();
|
||||
|
||||
if (!earlyResult.IsDefined(out var earlyBuilt)) return Result.FromError(earlyResult);
|
||||
|
||||
return (Result)await _channelApi.CreateMessageAsync(
|
||||
GuildSettings.EventNotificationChannel.Get(data.Settings),
|
||||
content,
|
||||
embeds: new[] { earlyBuilt }, ct: ct);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue