mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Merge branch 'master' into 40-rename-users-who-attempt-to-hoist-themselves
This commit is contained in:
commit
306c1702ba
7 changed files with 67 additions and 66 deletions
2
.github/workflows/resharper.yml
vendored
2
.github/workflows/resharper.yml
vendored
|
@ -31,5 +31,5 @@ jobs:
|
||||||
uses: muno92/resharper_inspectcode@1.7.1
|
uses: muno92/resharper_inspectcode@1.7.1
|
||||||
with:
|
with:
|
||||||
solutionPath: ./Boyfriend.sln
|
solutionPath: ./Boyfriend.sln
|
||||||
ignoreIssueType: InvertIf, ConvertIfStatementToReturnStatement, ConvertIfStatementToSwitchStatement
|
ignoreIssueType: InvertIf, ConvertIfStatementToSwitchStatement
|
||||||
solutionWideAnalysis: true
|
solutionWideAnalysis: true
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
Beep! I'm a general-purpose bot for moderation written by [@Octol1ttle](https://github.com/Octol1ttle) in C# and
|
Beep! I'm a general-purpose bot for moderation written by [@Octol1ttle](https://github.com/Octol1ttle) in C# and
|
||||||
Remora.Discord
|
Remora.Discord
|
||||||
|
|
|
@ -152,7 +152,9 @@ public class BanCommandGroup : CommandGroup {
|
||||||
title, target)
|
title, target)
|
||||||
.WithColour(ColorsList.Green).Build();
|
.WithColour(ColorsList.Green).Build();
|
||||||
|
|
||||||
_utility.LogActionAsync(cfg, channelId, title, target, description, user, CancellationToken);
|
var logResult = _utility.LogActionAsync(cfg, channelId, user, title, description, target, CancellationToken);
|
||||||
|
if (!logResult.IsSuccess)
|
||||||
|
return Result.FromError(logResult.Error);
|
||||||
|
|
||||||
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -222,7 +224,7 @@ public class BanCommandGroup : CommandGroup {
|
||||||
|
|
||||||
var title = string.Format(Messages.UserUnbanned, target.GetTag());
|
var title = string.Format(Messages.UserUnbanned, target.GetTag());
|
||||||
var description = string.Format(Messages.DescriptionActionReason, reason);
|
var description = string.Format(Messages.DescriptionActionReason, reason);
|
||||||
var logResult = _utility.LogActionAsync(cfg, channelId, title, target, description, user, CancellationToken);
|
var logResult = _utility.LogActionAsync(cfg, channelId, user, title, description, target, CancellationToken);
|
||||||
if (!logResult.IsSuccess)
|
if (!logResult.IsSuccess)
|
||||||
return Result.FromError(logResult.Error);
|
return Result.FromError(logResult.Error);
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,17 @@ public class ClearCommandGroup : CommandGroup {
|
||||||
private readonly GuildDataService _dataService;
|
private readonly GuildDataService _dataService;
|
||||||
private readonly FeedbackService _feedbackService;
|
private readonly FeedbackService _feedbackService;
|
||||||
private readonly IDiscordRestUserAPI _userApi;
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
private readonly UtilityService _utility;
|
||||||
|
|
||||||
public ClearCommandGroup(
|
public ClearCommandGroup(
|
||||||
IDiscordRestChannelAPI channelApi, ICommandContext context, GuildDataService dataService,
|
IDiscordRestChannelAPI channelApi, ICommandContext context, GuildDataService dataService,
|
||||||
FeedbackService feedbackService, IDiscordRestUserAPI userApi) {
|
FeedbackService feedbackService, IDiscordRestUserAPI userApi, UtilityService utility) {
|
||||||
_channelApi = channelApi;
|
_channelApi = channelApi;
|
||||||
_context = context;
|
_context = context;
|
||||||
_dataService = dataService;
|
_dataService = dataService;
|
||||||
_feedbackService = feedbackService;
|
_feedbackService = feedbackService;
|
||||||
_userApi = userApi;
|
_userApi = userApi;
|
||||||
|
_utility = utility;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -55,7 +57,7 @@ public class ClearCommandGroup : CommandGroup {
|
||||||
[RequireBotDiscordPermissions(DiscordPermission.ManageMessages)]
|
[RequireBotDiscordPermissions(DiscordPermission.ManageMessages)]
|
||||||
[Description("Remove multiple messages")]
|
[Description("Remove multiple messages")]
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public async Task<Result> ClearMessagesAsync(
|
public async Task<Result> ExecuteClear(
|
||||||
[Description("Number of messages to remove (2-100)")] [MinValue(2)] [MaxValue(100)]
|
[Description("Number of messages to remove (2-100)")] [MinValue(2)] [MaxValue(100)]
|
||||||
int amount) {
|
int amount) {
|
||||||
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId))
|
if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId))
|
||||||
|
@ -66,12 +68,25 @@ public class ClearCommandGroup : CommandGroup {
|
||||||
channelId.Value, limit: amount + 1, ct: CancellationToken);
|
channelId.Value, limit: amount + 1, ct: CancellationToken);
|
||||||
if (!messagesResult.IsDefined(out var messages))
|
if (!messagesResult.IsDefined(out var messages))
|
||||||
return Result.FromError(messagesResult);
|
return Result.FromError(messagesResult);
|
||||||
|
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||||
|
if (!userResult.IsDefined(out var user))
|
||||||
|
return Result.FromError(userResult);
|
||||||
|
// The current user's avatar is used when sending messages
|
||||||
|
var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||||
|
if (!currentUserResult.IsDefined(out var currentUser))
|
||||||
|
return Result.FromError(currentUserResult);
|
||||||
|
|
||||||
var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken);
|
return await ClearMessagesAsync(amount, guildId.Value, channelId.Value, messages, user, currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||||
|
|
||||||
var idList = new List<Snowflake>(messages.Count);
|
var idList = new List<Snowflake>(messages.Count);
|
||||||
var builder = new StringBuilder().AppendLine(Mention.Channel(channelId.Value)).AppendLine();
|
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...')
|
for (var i = messages.Count - 1; i >= 1; i--) { // '>= 1' to skip last message ('Boyfriend is thinking...')
|
||||||
var message = messages[i];
|
var message = messages[i];
|
||||||
idList.Add(message.ID);
|
idList.Add(message.ID);
|
||||||
|
@ -79,41 +94,18 @@ public class ClearCommandGroup : CommandGroup {
|
||||||
builder.Append(message.Content.InBlockCode());
|
builder.Append(message.Content.InBlockCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var title = string.Format(Messages.MessagesCleared, amount.ToString());
|
||||||
var description = builder.ToString();
|
var description = builder.ToString();
|
||||||
|
|
||||||
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
|
||||||
if (!userResult.IsDefined(out var user))
|
|
||||||
return Result.FromError(userResult);
|
|
||||||
|
|
||||||
var deleteResult = await _channelApi.BulkDeleteMessagesAsync(
|
var deleteResult = await _channelApi.BulkDeleteMessagesAsync(
|
||||||
channelId.Value, idList, user.GetTag().EncodeHeader(), CancellationToken);
|
channelId, idList, user.GetTag().EncodeHeader(), CancellationToken);
|
||||||
if (!deleteResult.IsSuccess)
|
if (!deleteResult.IsSuccess)
|
||||||
return Result.FromError(deleteResult.Error);
|
return Result.FromError(deleteResult.Error);
|
||||||
|
|
||||||
// The current user's avatar is used when sending messages
|
var logResult = _utility.LogActionAsync(
|
||||||
var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
cfg, channelId, user, title, description, currentUser, CancellationToken);
|
||||||
if (!currentUserResult.IsDefined(out var currentUser))
|
if (!logResult.IsSuccess)
|
||||||
return Result.FromError(currentUserResult);
|
return Result.FromError(logResult.Error);
|
||||||
|
|
||||||
var title = string.Format(Messages.MessagesCleared, amount.ToString());
|
|
||||||
if (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty()
|
|
||||||
&& GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value) {
|
|
||||||
var logEmbed = new EmbedBuilder().WithSmallTitle(title, currentUser)
|
|
||||||
.WithDescription(description)
|
|
||||||
.WithActionFooter(user)
|
|
||||||
.WithCurrentTimestamp()
|
|
||||||
.WithColour(ColorsList.Red)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
if (!logEmbed.IsDefined(out var logBuilt))
|
|
||||||
return Result.FromError(logEmbed);
|
|
||||||
|
|
||||||
// Not awaiting to reduce response time
|
|
||||||
if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)
|
|
||||||
_ = _channelApi.CreateMessageAsync(
|
|
||||||
GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: new[] { logBuilt },
|
|
||||||
ct: CancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
var embed = new EmbedBuilder().WithSmallTitle(title, currentUser)
|
var embed = new EmbedBuilder().WithSmallTitle(title, currentUser)
|
||||||
.WithColour(ColorsList.Green).Build();
|
.WithColour(ColorsList.Green).Build();
|
||||||
|
|
|
@ -27,9 +27,8 @@ public class LanguageOption : Option<CultureInfo> {
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Result Set(JsonNode settings, string from) {
|
public override Result Set(JsonNode settings, string from) {
|
||||||
if (!CultureInfoCache.ContainsKey(from.ToLowerInvariant()))
|
return CultureInfoCache.ContainsKey(from.ToLowerInvariant())
|
||||||
return Result.FromError(new ArgumentInvalidError(nameof(from), Messages.LanguageNotSupported));
|
? base.Set(settings, from.ToLowerInvariant())
|
||||||
|
: Result.FromError(new ArgumentInvalidError(nameof(from), Messages.LanguageNotSupported));
|
||||||
return base.Set(settings, from.ToLowerInvariant());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,15 +46,13 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
if (!auditLogResult.IsDefined(out var auditLogPage)) return Result.FromError(auditLogResult);
|
if (!auditLogResult.IsDefined(out var auditLogPage)) return Result.FromError(auditLogResult);
|
||||||
|
|
||||||
var auditLog = auditLogPage.AuditLogEntries.Single();
|
var auditLog = auditLogPage.AuditLogEntries.Single();
|
||||||
if (!auditLog.Options.IsDefined(out var options))
|
|
||||||
return Result.FromError(new ArgumentNullError(nameof(auditLog.Options)));
|
|
||||||
|
|
||||||
var user = message.Author;
|
var userResult = Result<IUser>.FromSuccess(message.Author);
|
||||||
if (options.ChannelID == gatewayEvent.ChannelID
|
if (auditLog.Options.Value.ChannelID == gatewayEvent.ChannelID
|
||||||
&& DateTimeOffset.UtcNow.Subtract(auditLog.ID.Timestamp).TotalSeconds <= 2) {
|
&& DateTimeOffset.UtcNow.Subtract(auditLog.ID.Timestamp).TotalSeconds <= 2)
|
||||||
var userResult = await _userApi.GetUserAsync(auditLog.UserID!.Value, ct);
|
userResult = await _userApi.GetUserAsync(auditLog.UserID!.Value, ct);
|
||||||
if (!userResult.IsDefined(out user)) return Result.FromError(userResult);
|
|
||||||
}
|
if (!userResult.IsDefined(out var user)) return Result.FromError(userResult);
|
||||||
|
|
||||||
Messages.Culture = GuildSettings.Language.Get(cfg);
|
Messages.Culture = GuildSettings.Language.Get(cfg);
|
||||||
|
|
||||||
|
|
|
@ -64,13 +64,10 @@ public class UtilityService : IHostedService {
|
||||||
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||||
if (!currentUserResult.IsDefined(out var currentUser))
|
if (!currentUserResult.IsDefined(out var currentUser))
|
||||||
return Result<string?>.FromError(currentUserResult);
|
return Result<string?>.FromError(currentUserResult);
|
||||||
if (currentUser.ID == targetId)
|
|
||||||
return Result<string?>.FromSuccess($"UserCannot{action}Bot".Localized());
|
|
||||||
|
|
||||||
var guildResult = await _guildApi.GetGuildAsync(guildId, ct: ct);
|
var guildResult = await _guildApi.GetGuildAsync(guildId, ct: ct);
|
||||||
if (!guildResult.IsDefined(out var guild))
|
if (!guildResult.IsDefined(out var guild))
|
||||||
return Result<string?>.FromError(guildResult);
|
return Result<string?>.FromError(guildResult);
|
||||||
if (targetId == guild.OwnerID) return Result<string?>.FromSuccess($"UserCannot{action}Owner".Localized());
|
|
||||||
|
|
||||||
var targetMemberResult = await _guildApi.GetGuildMemberAsync(guildId, targetId, ct);
|
var targetMemberResult = await _guildApi.GetGuildMemberAsync(guildId, targetId, ct);
|
||||||
if (!targetMemberResult.IsDefined(out var targetMember))
|
if (!targetMemberResult.IsDefined(out var targetMember))
|
||||||
|
@ -84,6 +81,25 @@ public class UtilityService : IHostedService {
|
||||||
if (!rolesResult.IsDefined(out var roles))
|
if (!rolesResult.IsDefined(out var roles))
|
||||||
return Result<string?>.FromError(rolesResult);
|
return Result<string?>.FromError(rolesResult);
|
||||||
|
|
||||||
|
var interacterResult = await _guildApi.GetGuildMemberAsync(guildId, interacterId, ct);
|
||||||
|
return interacterResult.IsDefined(out var interacter)
|
||||||
|
? CheckInteractions(action, guild, roles, targetMember, currentMember, interacter)
|
||||||
|
: Result<string?>.FromError(interacterResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Result<string?> CheckInteractions(
|
||||||
|
string action, IGuild guild, IReadOnlyList<IRole> roles, IGuildMember targetMember, IGuildMember currentMember,
|
||||||
|
IGuildMember interacter) {
|
||||||
|
if (!targetMember.User.IsDefined(out var targetUser))
|
||||||
|
return Result<string?>.FromError(new ArgumentNullError(nameof(targetMember.User)));
|
||||||
|
if (!interacter.User.IsDefined(out var interacterUser))
|
||||||
|
return Result<string?>.FromError(new ArgumentNullError(nameof(interacter.User)));
|
||||||
|
|
||||||
|
if (currentMember.User == targetMember.User)
|
||||||
|
return Result<string?>.FromSuccess($"UserCannot{action}Bot".Localized());
|
||||||
|
|
||||||
|
if (targetUser.ID == guild.OwnerID) return Result<string?>.FromSuccess($"UserCannot{action}Owner".Localized());
|
||||||
|
|
||||||
var targetRoles = roles.Where(r => targetMember.Roles.Contains(r.ID)).ToList();
|
var targetRoles = roles.Where(r => targetMember.Roles.Contains(r.ID)).ToList();
|
||||||
var botRoles = roles.Where(r => currentMember.Roles.Contains(r.ID));
|
var botRoles = roles.Where(r => currentMember.Roles.Contains(r.ID));
|
||||||
|
|
||||||
|
@ -91,20 +107,15 @@ public class UtilityService : IHostedService {
|
||||||
if (targetBotRoleDiff >= 0)
|
if (targetBotRoleDiff >= 0)
|
||||||
return Result<string?>.FromSuccess($"BotCannot{action}Target".Localized());
|
return Result<string?>.FromSuccess($"BotCannot{action}Target".Localized());
|
||||||
|
|
||||||
if (interacterId == guild.OwnerID)
|
if (interacterUser.ID == guild.OwnerID)
|
||||||
return Result<string?>.FromSuccess(null);
|
return Result<string?>.FromSuccess(null);
|
||||||
|
|
||||||
var interacterResult = await _guildApi.GetGuildMemberAsync(guildId, interacterId, ct);
|
|
||||||
if (!interacterResult.IsDefined(out var interacter))
|
|
||||||
return Result<string?>.FromError(interacterResult);
|
|
||||||
|
|
||||||
var interacterRoles = roles.Where(r => interacter.Roles.Contains(r.ID));
|
var interacterRoles = roles.Where(r => interacter.Roles.Contains(r.ID));
|
||||||
var targetInteracterRoleDiff
|
var targetInteracterRoleDiff
|
||||||
= targetRoles.MaxOrDefault(r => r.Position) - interacterRoles.MaxOrDefault(r => r.Position);
|
= targetRoles.MaxOrDefault(r => r.Position) - interacterRoles.MaxOrDefault(r => r.Position);
|
||||||
if (targetInteracterRoleDiff >= 0)
|
return targetInteracterRoleDiff < 0
|
||||||
return Result<string?>.FromSuccess($"UserCannot{action}Target".Localized());
|
? Result<string?>.FromSuccess(null)
|
||||||
|
: Result<string?>.FromSuccess($"UserCannot{action}Target".Localized());
|
||||||
return Result<string?>.FromSuccess(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -143,15 +154,15 @@ public class UtilityService : IHostedService {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cfg">The guild configuration.</param>
|
/// <param name="cfg">The guild configuration.</param>
|
||||||
/// <param name="channelId">The ID of the channel where the action was executed.</param>
|
/// <param name="channelId">The ID of the channel where the action was executed.</param>
|
||||||
/// <param name="title">The title for the embed.</param>
|
|
||||||
/// <param name="avatar">The user whose avatar will be displayed next to the <paramref name="title" /> of the embed.</param>
|
|
||||||
/// <param name="description">The description of the embed.</param>
|
|
||||||
/// <param name="user">The user who performed the action.</param>
|
/// <param name="user">The user who performed the action.</param>
|
||||||
|
/// <param name="title">The title for the embed.</param>
|
||||||
|
/// <param name="description">The description of the embed.</param>
|
||||||
|
/// <param name="avatar">The user whose avatar will be displayed next to the <paramref name="title" /> of the embed.</param>
|
||||||
/// <param name="ct">The cancellation token for this operation.</param>
|
/// <param name="ct">The cancellation token for this operation.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Result LogActionAsync(
|
public Result LogActionAsync(
|
||||||
JsonNode cfg, Snowflake channelId, string title, IUser avatar, string description,
|
JsonNode cfg, Snowflake channelId, IUser user, string title, string description, IUser avatar,
|
||||||
IUser user, CancellationToken ct = default) {
|
CancellationToken ct = default) {
|
||||||
var publicChannel = GuildSettings.PublicFeedbackChannel.Get(cfg);
|
var publicChannel = GuildSettings.PublicFeedbackChannel.Get(cfg);
|
||||||
var privateChannel = GuildSettings.PrivateFeedbackChannel.Get(cfg);
|
var privateChannel = GuildSettings.PrivateFeedbackChannel.Get(cfg);
|
||||||
if (GuildSettings.PublicFeedbackChannel.Get(cfg).EmptyOrEqualTo(channelId)
|
if (GuildSettings.PublicFeedbackChannel.Get(cfg).EmptyOrEqualTo(channelId)
|
||||||
|
|
Loading…
Add table
Reference in a new issue