mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Add /ban command v1
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This commit is contained in:
parent
a83aa03cf0
commit
806746925e
10 changed files with 275 additions and 81 deletions
26
Boyfriend.cs
26
Boyfriend.cs
|
@ -1,8 +1,11 @@
|
||||||
using Boyfriend.Data.Services;
|
using Boyfriend.Commands;
|
||||||
|
using Boyfriend.Services;
|
||||||
|
using Boyfriend.Services.Data;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Remora.Commands.Extensions;
|
||||||
using Remora.Discord.API.Abstractions.Gateway.Commands;
|
using Remora.Discord.API.Abstractions.Gateway.Commands;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Gateway.Commands;
|
using Remora.Discord.API.Gateway.Commands;
|
||||||
|
@ -26,16 +29,10 @@ public class Boyfriend {
|
||||||
public static async Task Main(string[] args) {
|
public static async Task Main(string[] args) {
|
||||||
var host = CreateHostBuilder(args).UseConsoleLifetime().Build();
|
var host = CreateHostBuilder(args).UseConsoleLifetime().Build();
|
||||||
var services = host.Services;
|
var services = host.Services;
|
||||||
var configuration = services.GetRequiredService<IConfiguration>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var slashService = services.GetRequiredService<SlashService>();
|
var slashService = services.GetRequiredService<SlashService>();
|
||||||
var updateSlash = await slashService.UpdateSlashCommandsAsync(new Snowflake(1115043975573811250));
|
_ = await slashService.UpdateSlashCommandsAsync();
|
||||||
if (!updateSlash.IsSuccess) {
|
|
||||||
Console.WriteLine("Failed to update slash commands: {Reason}", updateSlash.Error.Message);
|
|
||||||
}
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +68,13 @@ public class Boyfriend {
|
||||||
|
|
||||||
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>()
|
services.AddTransient<IConfigurationBuilder, ConfigurationBuilder>()
|
||||||
.AddDiscordCaching()
|
.AddDiscordCaching()
|
||||||
.AddDiscordCommands()
|
.AddDiscordCommands(true)
|
||||||
.AddInteractivity()
|
.AddInteractivity()
|
||||||
.AddInteractionGroup<InteractionResponders>()
|
.AddInteractionGroup<InteractionResponders>()
|
||||||
.AddSingleton<GuildDataService>();
|
.AddSingleton<GuildDataService>()
|
||||||
|
.AddSingleton<UtilityService>()
|
||||||
|
.AddCommandTree()
|
||||||
|
.WithCommandGroup<BanCommand>();
|
||||||
var responderTypes = typeof(Boyfriend).Assembly
|
var responderTypes = typeof(Boyfriend).Assembly
|
||||||
.GetExportedTypes()
|
.GetExportedTypes()
|
||||||
.Where(t => t.IsResponder());
|
.Where(t => t.IsResponder());
|
||||||
|
@ -86,8 +86,4 @@ public class Boyfriend {
|
||||||
.AddFilter("System.Net.Http.HttpClient.*.ClientHandler", LogLevel.Warning)
|
.AddFilter("System.Net.Http.HttpClient.*.ClientHandler", LogLevel.Warning)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetLocalized(string key) {
|
|
||||||
return Messages.ResourceManager.GetString(key, Messages.Culture) ?? key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,123 @@
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Drawing;
|
||||||
|
using Boyfriend.Services;
|
||||||
|
using Boyfriend.Services.Data;
|
||||||
using Remora.Commands.Attributes;
|
using Remora.Commands.Attributes;
|
||||||
using Remora.Commands.Groups;
|
using Remora.Commands.Groups;
|
||||||
using Remora.Discord.API.Abstractions.Objects;
|
using Remora.Discord.API.Abstractions.Objects;
|
||||||
using Remora.Discord.API.Gateway.Events;
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
using Remora.Discord.API.Objects;
|
using Remora.Discord.API.Objects;
|
||||||
using Remora.Discord.Commands.Attributes;
|
using Remora.Discord.Commands.Conditions;
|
||||||
|
using Remora.Discord.Commands.Contexts;
|
||||||
|
using Remora.Discord.Commands.Extensions;
|
||||||
using Remora.Discord.Commands.Feedback.Services;
|
using Remora.Discord.Commands.Feedback.Services;
|
||||||
using Remora.Rest.Core;
|
using Remora.Discord.Extensions.Embeds;
|
||||||
using Remora.Results;
|
using Remora.Results;
|
||||||
|
|
||||||
namespace Boyfriend.Commands;
|
namespace Boyfriend.Commands;
|
||||||
|
|
||||||
public class BanCommand : CommandGroup{
|
public class BanCommand : CommandGroup {
|
||||||
|
private readonly IDiscordRestChannelAPI _channelApi;
|
||||||
|
private readonly ICommandContext _context;
|
||||||
|
private readonly GuildDataService _dataService;
|
||||||
private readonly FeedbackService _feedbackService;
|
private readonly FeedbackService _feedbackService;
|
||||||
|
private readonly IDiscordRestGuildAPI _guildApi;
|
||||||
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
private readonly UtilityService _utility;
|
||||||
|
|
||||||
/// <summary>
|
public BanCommand(
|
||||||
/// Initializes a new instance of the <see cref="HttpCatCommands"/> class.
|
ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService dataService,
|
||||||
/// </summary>
|
FeedbackService feedbackService, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi,
|
||||||
/// <param name="feedbackService">The feedback service.</param>
|
UtilityService utility) {
|
||||||
public BanCommand(FeedbackService feedbackService)
|
_context = context;
|
||||||
{
|
_channelApi = channelApi;
|
||||||
|
_dataService = dataService;
|
||||||
_feedbackService = feedbackService;
|
_feedbackService = feedbackService;
|
||||||
|
_guildApi = guildApi;
|
||||||
|
_userApi = userApi;
|
||||||
|
_utility = utility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Command("ban")]
|
[Command("ban")]
|
||||||
|
[RequireContext(ChannelContext.Guild)]
|
||||||
|
[RequireDiscordPermission(DiscordPermission.BanMembers)]
|
||||||
|
[RequireBotDiscordPermissions(DiscordPermission.BanMembers)]
|
||||||
[Description("банит пидора")]
|
[Description("банит пидора")]
|
||||||
public async Task<IResult> BanAsync([Description("Юзер, кого банить")] IUser user, string reason) {
|
public async Task<Result> BanUserAsync([Description("Юзер, кого банить")] IUser target, string reason) {
|
||||||
var banan = new Ban(reason, user);
|
if (!_context.TryGetGuildID(out var guildId))
|
||||||
var embed = new Embed(Colour: _feedbackService.Theme.Secondary, Description: "забанен нахуй");
|
return Result.FromError(new ArgumentNullError(nameof(guildId)));
|
||||||
|
if (!_context.TryGetUserID(out var userId))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(userId)));
|
||||||
|
if (!_context.TryGetChannelID(out var channelId))
|
||||||
|
return Result.FromError(new ArgumentNullError(nameof(channelId)));
|
||||||
|
|
||||||
return (Result)await _feedbackService.SendContextualEmbedAsync(embed, ct: this.CancellationToken);
|
var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken);
|
||||||
|
if (!currentUserResult.IsDefined(out var currentUser))
|
||||||
|
return Result.FromError(currentUserResult);
|
||||||
|
|
||||||
|
var existingBanResult = await _guildApi.GetGuildBanAsync(guildId.Value, target.ID, CancellationToken);
|
||||||
|
if (existingBanResult.IsDefined(out _)) {
|
||||||
|
var embed = new EmbedBuilder().WithSmallTitle(Messages.UserAlreadyBanned, currentUser)
|
||||||
|
.WithColour(Color.Firebrick).Build();
|
||||||
|
|
||||||
|
if (!embed.IsDefined(out var alreadyBuilt))
|
||||||
|
return Result.FromError(embed);
|
||||||
|
|
||||||
|
return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
var interactionResult
|
||||||
|
= await _utility.CheckInteractionsAsync(guildId.Value, userId.Value, target.ID, "Ban", CancellationToken);
|
||||||
|
if (!interactionResult.IsSuccess)
|
||||||
|
return Result.FromError(interactionResult);
|
||||||
|
|
||||||
|
Result<Embed> responseEmbed;
|
||||||
|
if (interactionResult.Entity is not null) {
|
||||||
|
responseEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser)
|
||||||
|
.WithColour(Color.Firebrick).Build();
|
||||||
|
} else {
|
||||||
|
var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken);
|
||||||
|
if (!userResult.IsDefined(out var user))
|
||||||
|
return Result.FromError(userResult);
|
||||||
|
|
||||||
|
var banResult = await _guildApi.CreateGuildBanAsync(
|
||||||
|
guildId.Value, target.ID, reason: $"({user.GetTag()}) {reason}", ct: CancellationToken);
|
||||||
|
if (!banResult.IsSuccess)
|
||||||
|
return Result.FromError(banResult.Error);
|
||||||
|
|
||||||
|
responseEmbed = new EmbedBuilder().WithSmallTitle(
|
||||||
|
string.Format(Messages.UserBanned, target.GetTag()), target)
|
||||||
|
.WithColour(Color.LawnGreen).Build();
|
||||||
|
|
||||||
|
var cfg = await _dataService.GetConfiguration(guildId.Value, CancellationToken);
|
||||||
|
if ((cfg.PublicFeedbackChannel is not 0 && cfg.PublicFeedbackChannel != channelId.Value)
|
||||||
|
|| (cfg.PrivateFeedbackChannel is not 0 && cfg.PrivateFeedbackChannel != channelId.Value)) {
|
||||||
|
var logEmbed = new EmbedBuilder().WithSmallTitle(
|
||||||
|
string.Format(Messages.UserBanned, target.GetTag()), target)
|
||||||
|
.WithDescription(string.Format(Messages.DescriptionUserBanned, reason))
|
||||||
|
.WithActionFooter(user)
|
||||||
|
.WithCurrentTimestamp()
|
||||||
|
.WithColour(Color.Firebrick)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
if (!logEmbed.IsDefined(out var logBuilt))
|
||||||
|
return Result.FromError(logEmbed);
|
||||||
|
|
||||||
|
var builtArray = new[] { logBuilt };
|
||||||
|
if (cfg.PrivateFeedbackChannel != channelId.Value)
|
||||||
|
_ = _channelApi.CreateMessageAsync(
|
||||||
|
cfg.PrivateFeedbackChannel.ToDiscordSnowflake(), embeds: builtArray,
|
||||||
|
ct: CancellationToken);
|
||||||
|
if (cfg.PublicFeedbackChannel != channelId.Value)
|
||||||
|
_ = _channelApi.CreateMessageAsync(
|
||||||
|
cfg.PublicFeedbackChannel.ToDiscordSnowflake(), embeds: builtArray,
|
||||||
|
ct: CancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responseEmbed.IsDefined(out var built))
|
||||||
|
return Result.FromError(responseEmbed);
|
||||||
|
|
||||||
|
return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Boyfriend.Data;
|
using Boyfriend.Data;
|
||||||
using Boyfriend.Data.Services;
|
using Boyfriend.Services.Data;
|
||||||
using DiffPlex;
|
using DiffPlex;
|
||||||
using DiffPlex.DiffBuilder;
|
using DiffPlex.DiffBuilder;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -56,7 +56,7 @@ public class GuildCreateResponder : IResponder<IGuildCreate> {
|
||||||
var i = Random.Shared.Next(1, 4);
|
var i = Random.Shared.Next(1, 4);
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithTitle(Boyfriend.GetLocalized($"Beep{i}"))
|
.WithTitle($"Beep{i}".Localized())
|
||||||
.WithDescription(Messages.Ready)
|
.WithDescription(Messages.Ready)
|
||||||
.WithUserFooter(currentUser)
|
.WithUserFooter(currentUser)
|
||||||
.WithCurrentTimestamp()
|
.WithCurrentTimestamp()
|
||||||
|
@ -120,7 +120,7 @@ public class MessageDeletedResponder : IResponder<IMessageDelete> {
|
||||||
$"{Mention.Channel(gatewayEvent.ChannelID)}\n{Markdown.BlockCode(message.Content.SanitizeForBlockCode())}")
|
$"{Mention.Channel(gatewayEvent.ChannelID)}\n{Markdown.BlockCode(message.Content.SanitizeForBlockCode())}")
|
||||||
.WithActionFooter(user)
|
.WithActionFooter(user)
|
||||||
.WithTimestamp(message.Timestamp)
|
.WithTimestamp(message.Timestamp)
|
||||||
.WithColour(Color.Crimson)
|
.WithColour(Color.Firebrick)
|
||||||
.Build();
|
.Build();
|
||||||
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
if (!embed.IsDefined(out var built)) return Result.FromError(embed);
|
||||||
|
|
||||||
|
@ -153,13 +153,13 @@ public class MessageEditedResponder : IResponder<IMessageUpdate> {
|
||||||
return Result.FromSuccess();
|
return Result.FromSuccess();
|
||||||
if (!gatewayEvent.Content.IsDefined(out var newContent))
|
if (!gatewayEvent.Content.IsDefined(out var newContent))
|
||||||
return Result.FromSuccess();
|
return Result.FromSuccess();
|
||||||
|
if (!gatewayEvent.EditedTimestamp.IsDefined(out var timestamp))
|
||||||
|
return Result.FromSuccess();
|
||||||
|
|
||||||
if (!gatewayEvent.ChannelID.IsDefined(out var channelId))
|
if (!gatewayEvent.ChannelID.IsDefined(out var channelId))
|
||||||
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ChannelID)));
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ChannelID)));
|
||||||
if (!gatewayEvent.ID.IsDefined(out var messageId))
|
if (!gatewayEvent.ID.IsDefined(out var messageId))
|
||||||
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ID)));
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ID)));
|
||||||
if (!gatewayEvent.EditedTimestamp.IsDefined(out var timestamp))
|
|
||||||
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.EditedTimestamp)));
|
|
||||||
|
|
||||||
var cacheKey = new KeyHelpers.MessageCacheKey(channelId, messageId);
|
var cacheKey = new KeyHelpers.MessageCacheKey(channelId, messageId);
|
||||||
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
var messageResult = await _cacheService.TryGetValueAsync<IMessage>(
|
||||||
|
@ -286,7 +286,7 @@ public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEven
|
||||||
|
|
||||||
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||||
string.Format(
|
string.Format(
|
||||||
Messages.LocalEventCreatedDescription,
|
Messages.DescriptionLocalEventCreated,
|
||||||
Markdown.Timestamp(gatewayEvent.ScheduledStartTime),
|
Markdown.Timestamp(gatewayEvent.ScheduledStartTime),
|
||||||
Mention.Channel(channelId)
|
Mention.Channel(channelId)
|
||||||
))}";
|
))}";
|
||||||
|
@ -301,7 +301,7 @@ public class GuildScheduledEventCreateResponder : IResponder<IGuildScheduledEven
|
||||||
|
|
||||||
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
embedDescription = $"{eventDescription}\n\n{Markdown.BlockQuote(
|
||||||
string.Format(
|
string.Format(
|
||||||
Messages.ExternalEventCreatedDescription,
|
Messages.DescriptionExternalEventCreated,
|
||||||
Markdown.Timestamp(gatewayEvent.ScheduledStartTime),
|
Markdown.Timestamp(gatewayEvent.ScheduledStartTime),
|
||||||
Markdown.Timestamp(endTime),
|
Markdown.Timestamp(endTime),
|
||||||
Markdown.InlineCode(location)
|
Markdown.InlineCode(location)
|
||||||
|
@ -365,14 +365,14 @@ public class GuildScheduledEventUpdateResponder : IResponder<IGuildScheduledEven
|
||||||
case GuildScheduledEventStatus.Active:
|
case GuildScheduledEventStatus.Active:
|
||||||
guildData.ScheduledEvents[gatewayEvent.ID.Value].ActualStartTime = DateTimeOffset.UtcNow;
|
guildData.ScheduledEvents[gatewayEvent.ID.Value].ActualStartTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
string embedDescription; // what the fuck is this
|
string embedDescription;
|
||||||
switch (gatewayEvent.EntityType) {
|
switch (gatewayEvent.EntityType) {
|
||||||
case GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice:
|
case GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice:
|
||||||
if (!gatewayEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
if (!gatewayEvent.ChannelID.AsOptional().IsDefined(out var channelId))
|
||||||
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ChannelID)));
|
return Result.FromError(new ArgumentNullError(nameof(gatewayEvent.ChannelID)));
|
||||||
|
|
||||||
embedDescription = string.Format(
|
embedDescription = string.Format(
|
||||||
Messages.LocalEventStartedDescription,
|
Messages.DescriptionLocalEventStarted,
|
||||||
Mention.Channel(channelId)
|
Mention.Channel(channelId)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -385,7 +385,7 @@ public class GuildScheduledEventUpdateResponder : IResponder<IGuildScheduledEven
|
||||||
return Result.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
return Result.FromError(new ArgumentNullError(nameof(metadata.Location)));
|
||||||
|
|
||||||
embedDescription = string.Format(
|
embedDescription = string.Format(
|
||||||
Messages.ExternalEventStartedDescription,
|
Messages.DescriptionExternalEventStarted,
|
||||||
Markdown.InlineCode(location),
|
Markdown.InlineCode(location),
|
||||||
Markdown.Timestamp(endTime)
|
Markdown.Timestamp(endTime)
|
||||||
);
|
);
|
||||||
|
@ -462,7 +462,7 @@ public class GuildScheduledEventResponder : IResponder<IGuildScheduledEventDelet
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithSmallTitle(string.Format(Messages.EventCancelled, gatewayEvent.Name))
|
.WithSmallTitle(string.Format(Messages.EventCancelled, gatewayEvent.Name))
|
||||||
.WithDescription(":(")
|
.WithDescription(":(")
|
||||||
.WithColour(Color.DarkRed)
|
.WithColour(Color.Firebrick)
|
||||||
.WithCurrentTimestamp()
|
.WithCurrentTimestamp()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,10 @@ public static class Extensions {
|
||||||
return s.Replace("```", "```");
|
return s.Replace("```", "```");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string Localized(this string key) {
|
||||||
|
return Messages.ResourceManager.GetString(key, Messages.Culture) ?? key;
|
||||||
|
}
|
||||||
|
|
||||||
public static string AsMarkdown(this SideBySideDiffModel model) {
|
public static string AsMarkdown(this SideBySideDiffModel model) {
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
foreach (var line in model.OldText.Lines.Where(piece => !string.IsNullOrWhiteSpace(piece.Text)))
|
foreach (var line in model.OldText.Lines.Where(piece => !string.IsNullOrWhiteSpace(piece.Text)))
|
||||||
|
@ -78,13 +82,13 @@ public static class Extensions {
|
||||||
return $"{user.Username}#{user.Discriminator:0000}";
|
return $"{user.Username}#{user.Discriminator:0000}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Snowflake ToDiscordSnowflake(this ulong id) {
|
public static Snowflake ToDiscordSnowflake(this ulong id) {
|
||||||
return DiscordSnowflake.New(id);
|
return DiscordSnowflake.New(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public static string AsDuration(this TimeSpan span) {
|
public static TResult? MaxOrDefault<TSource, TResult>(
|
||||||
return span.Humanize(
|
this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
|
||||||
2, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Month,
|
return source.Any() ? source.Max(selector) : default;
|
||||||
culture: Messages.Culture.Name.Contains("RU") ? GuildConfiguration.CultureInfoCache["ru"] : Messages.Culture);
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
32
Messages.Designer.cs
generated
32
Messages.Designer.cs
generated
|
@ -243,9 +243,9 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string FeedbackUserBanned {
|
internal static string UserBanned {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("FeedbackUserBanned", resourceCulture);
|
return ResourceManager.GetString("UserBanned", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,15 +771,15 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string LocalEventCreatedDescription {
|
internal static string DescriptionLocalEventCreated {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("LocalEventCreatedDescription", resourceCulture);
|
return ResourceManager.GetString("DescriptionLocalEventCreated", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string ExternalEventCreatedDescription {
|
internal static string DescriptionExternalEventCreated {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ExternalEventCreatedDescription", resourceCulture);
|
return ResourceManager.GetString("DescriptionExternalEventCreated", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,15 +795,27 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string LocalEventStartedDescription {
|
internal static string DescriptionLocalEventStarted {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("LocalEventStartedDescription", resourceCulture);
|
return ResourceManager.GetString("DescriptionLocalEventStarted", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string ExternalEventStartedDescription {
|
internal static string DescriptionExternalEventStarted {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ExternalEventStartedDescription", resourceCulture);
|
return ResourceManager.GetString("DescriptionExternalEventStarted", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string DescriptionUserBanned {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DescriptionUserBanned", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string UserAlreadyBanned {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("UserAlreadyBanned", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,9 +204,9 @@
|
||||||
<data name="ClearAmountInvalid" xml:space="preserve">
|
<data name="ClearAmountInvalid" xml:space="preserve">
|
||||||
<value>You need to specify an integer from {0} to {1} instead of {2}!</value>
|
<value>You need to specify an integer from {0} to {1} instead of {2}!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackUserBanned" xml:space="preserve">
|
<data name="UserBanned" xml:space="preserve">
|
||||||
<value>Banned {0} for{1}: {2}</value>
|
<value>{0} was banned</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingDoesntExist" xml:space="preserve">
|
<data name="SettingDoesntExist" xml:space="preserve">
|
||||||
<value>That setting doesn't exist!</value>
|
<value>That setting doesn't exist!</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -468,22 +468,28 @@
|
||||||
<data name="EventCreatedTitle" xml:space="preserve">
|
<data name="EventCreatedTitle" xml:space="preserve">
|
||||||
<value>{0} has created a new event:</value>
|
<value>{0} has created a new event:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventCreated" xml:space="preserve">
|
||||||
<value>The event will start at {0} in {1}</value>
|
<value>The event will start at {0} in {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventCreated" xml:space="preserve">
|
||||||
<value>The event will start at {0} until {1} in {2}</value>
|
<value>The event will start at {0} until {1} in {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDetailsButton" xml:space="preserve">
|
<data name="EventDetailsButton" xml:space="preserve">
|
||||||
<value>Event details</value>
|
<value>Event details</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDuration" xml:space="preserve">
|
<data name="EventDuration" xml:space="preserve">
|
||||||
<value>The event has lasted for `{0}`</value>
|
<value>The event has lasted for `{0}`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventStarted" xml:space="preserve">
|
||||||
<value>The event is happening at {0}</value>
|
<value>The event is happening at {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventStarted" xml:space="preserve">
|
||||||
<value>The event is happening at {0} until {1}</value>
|
<value>The event is happening at {0} until {1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DescriptionUserBanned" xml:space="preserve">
|
||||||
|
<value>Reason: {0}</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserAlreadyBanned" xml:space="preserve">
|
||||||
|
<value>This user is already banned!</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -204,9 +204,9 @@
|
||||||
<data name="ClearAmountInvalid" xml:space="preserve">
|
<data name="ClearAmountInvalid" xml:space="preserve">
|
||||||
<value>Надо указать целое число от {0} до {1} вместо {2}!</value>
|
<value>Надо указать целое число от {0} до {1} вместо {2}!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackUserBanned" xml:space="preserve">
|
<data name="UserBanned" xml:space="preserve">
|
||||||
<value>Забанен {0} на{1}: {2}</value>
|
<value>{0} был(-а) забанен(-а)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingDoesntExist" xml:space="preserve">
|
<data name="SettingDoesntExist" xml:space="preserve">
|
||||||
<value>Такая настройка не существует!</value>
|
<value>Такая настройка не существует!</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -468,22 +468,28 @@
|
||||||
<data name="EventCreatedTitle" xml:space="preserve">
|
<data name="EventCreatedTitle" xml:space="preserve">
|
||||||
<value>{0} создаёт новое событие:</value>
|
<value>{0} создаёт новое событие:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventCreated" xml:space="preserve">
|
||||||
<value>Событие пройдёт {0} в канале {1}</value>
|
<value>Событие пройдёт {0} в канале {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventCreated" xml:space="preserve">
|
||||||
<value>Событие пройдёт с {0} до {1} в {2}</value>
|
<value>Событие пройдёт с {0} до {1} в {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDetailsButton" xml:space="preserve">
|
<data name="EventDetailsButton" xml:space="preserve">
|
||||||
<value>Подробнее о событии</value>
|
<value>Подробнее о событии</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDuration" xml:space="preserve">
|
<data name="EventDuration" xml:space="preserve">
|
||||||
<value>Событие длилось `{0}`</value>
|
<value>Событие длилось `{0}`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventStarted" xml:space="preserve">
|
||||||
<value>Событие происходит в {0}</value>
|
<value>Событие происходит в {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventStarted" xml:space="preserve">
|
||||||
<value>Событие происходит в {0} до {1}</value>
|
<value>Событие происходит в {0} до {1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DescriptionUserBanned" xml:space="preserve">
|
||||||
|
<value>Причина: {0}</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserAlreadyBanned" xml:space="preserve">
|
||||||
|
<value>Этот пользователь уже забанен!</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -204,9 +204,9 @@
|
||||||
<data name="ClearAmountInvalid" xml:space="preserve">
|
<data name="ClearAmountInvalid" xml:space="preserve">
|
||||||
<value>выбери число от {0} до {1} вместо {2}!</value>
|
<value>выбери число от {0} до {1} вместо {2}!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FeedbackUserBanned" xml:space="preserve">
|
<data name="UserBanned" xml:space="preserve">
|
||||||
<value>забанен {0} на{1}: {2}</value>
|
<value>{0} забанен</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingDoesntExist" xml:space="preserve">
|
<data name="SettingDoesntExist" xml:space="preserve">
|
||||||
<value>такой прикол не существует</value>
|
<value>такой прикол не существует</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -468,22 +468,28 @@
|
||||||
<data name="EventCreatedTitle" xml:space="preserve">
|
<data name="EventCreatedTitle" xml:space="preserve">
|
||||||
<value>{0} создает новое событие:</value>
|
<value>{0} создает новое событие:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventCreated" xml:space="preserve">
|
||||||
<value>движуха произойдет {0} в канале {1}</value>
|
<value>движуха произойдет {0} в канале {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventCreatedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventCreated" xml:space="preserve">
|
||||||
<value>движуха будет происходить с {0} до {1} в {2}</value>
|
<value>движуха будет происходить с {0} до {1} в {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDetailsButton" xml:space="preserve">
|
<data name="EventDetailsButton" xml:space="preserve">
|
||||||
<value>побольше о движухе</value>
|
<value>побольше о движухе</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EventDuration" xml:space="preserve">
|
<data name="EventDuration" xml:space="preserve">
|
||||||
<value>все это длилось `{0}`</value>
|
<value>все это длилось `{0}`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LocalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionLocalEventStarted" xml:space="preserve">
|
||||||
<value>движуха происходит в {0}</value>
|
<value>движуха происходит в {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalEventStartedDescription" xml:space="preserve">
|
<data name="DescriptionExternalEventStarted" xml:space="preserve">
|
||||||
<value>движуха происходит в {0} до {1}</value>
|
<value>движуха происходит в {0} до {1}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DescriptionUserBanned" xml:space="preserve">
|
||||||
|
<value>причина: {0}</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserAlreadyBanned" xml:space="preserve">
|
||||||
|
<value>этот шизоид уже лежит в бане</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Boyfriend.Data;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Remora.Rest.Core;
|
using Remora.Rest.Core;
|
||||||
|
|
||||||
namespace Boyfriend.Data.Services;
|
namespace Boyfriend.Services.Data;
|
||||||
|
|
||||||
public class GuildDataService : IHostedService {
|
public class GuildDataService : IHostedService {
|
||||||
private readonly Dictionary<Snowflake, GuildData> _datas = new();
|
private readonly Dictionary<Snowflake, GuildData> _datas = new();
|
76
Services/UtilityService.cs
Normal file
76
Services/UtilityService.cs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Remora.Discord.API.Abstractions.Rest;
|
||||||
|
using Remora.Rest.Core;
|
||||||
|
using Remora.Results;
|
||||||
|
|
||||||
|
namespace Boyfriend.Services;
|
||||||
|
|
||||||
|
public class UtilityService : IHostedService {
|
||||||
|
private readonly IDiscordRestGuildAPI _guildApi;
|
||||||
|
private readonly IDiscordRestUserAPI _userApi;
|
||||||
|
|
||||||
|
public UtilityService(IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi) {
|
||||||
|
_guildApi = guildApi;
|
||||||
|
_userApi = userApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken ct) {
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken ct) {
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<string?>> CheckInteractionsAsync(
|
||||||
|
Snowflake guildId, Snowflake interacterId, Snowflake targetId, string action, CancellationToken ct = default) {
|
||||||
|
if (interacterId == targetId)
|
||||||
|
return Result<string?>.FromSuccess($"UserCannot{action}Themselves".Localized());
|
||||||
|
|
||||||
|
var guildResult = await _guildApi.GetGuildAsync(guildId, ct: ct);
|
||||||
|
if (!guildResult.IsDefined(out var guild))
|
||||||
|
return Result<string?>.FromError(guildResult);
|
||||||
|
|
||||||
|
if (targetId == guild.OwnerID) return Result<string?>.FromSuccess($"UserCannot{action}Owner".Localized());
|
||||||
|
|
||||||
|
var currentUserResult = await _userApi.GetCurrentUserAsync(ct);
|
||||||
|
if (!currentUserResult.IsDefined(out var currentUser))
|
||||||
|
return Result<string?>.FromError(currentUserResult);
|
||||||
|
|
||||||
|
if (currentUser.ID == targetId)
|
||||||
|
return Result<string?>.FromSuccess($"UserCannot{action}Bot".Localized());
|
||||||
|
|
||||||
|
if (interacterId == guild.OwnerID) return Result<string?>.FromSuccess(null);
|
||||||
|
|
||||||
|
var targetMemberResult = await _guildApi.GetGuildMemberAsync(guildId, targetId, ct);
|
||||||
|
if (!targetMemberResult.IsDefined(out var targetMember))
|
||||||
|
return Result<string?>.FromSuccess(null);
|
||||||
|
|
||||||
|
var currentMemberResult = await _guildApi.GetGuildMemberAsync(guildId, currentUser.ID, ct);
|
||||||
|
if (!currentMemberResult.IsDefined(out var currentMember))
|
||||||
|
return Result<string?>.FromError(currentMemberResult);
|
||||||
|
|
||||||
|
var rolesResult = await _guildApi.GetGuildRolesAsync(guildId, ct);
|
||||||
|
if (!rolesResult.IsDefined(out var roles))
|
||||||
|
return Result<string?>.FromError(rolesResult);
|
||||||
|
|
||||||
|
var interacterResult = await _guildApi.GetGuildMemberAsync(guildId, interacterId, ct);
|
||||||
|
if (!interacterResult.IsDefined(out var interacter))
|
||||||
|
return Result<string?>.FromError(interacterResult);
|
||||||
|
|
||||||
|
var targetRoles = roles.Where(r => targetMember.Roles.Contains(r.ID));
|
||||||
|
var interacterRoles = roles.Where(r => interacter.Roles.Contains(r.ID));
|
||||||
|
var botRoles = roles.Where(r => currentMember.Roles.Contains(r.ID));
|
||||||
|
|
||||||
|
var targetBotRoleDiff = targetRoles.MaxOrDefault(r => r.Position) - botRoles.Max(r => r.Position);
|
||||||
|
var targetInteracterRoleDiff = targetRoles.MaxOrDefault(r => r.Position) - interacterRoles.Max(r => r.Position);
|
||||||
|
|
||||||
|
if (targetInteracterRoleDiff >= 0)
|
||||||
|
return Result<string?>.FromSuccess($"UserCannot{action}Target".Localized());
|
||||||
|
|
||||||
|
if (targetBotRoleDiff >= 0)
|
||||||
|
return Result<string?>.FromSuccess($"BotCannot{action}Target".Localized());
|
||||||
|
|
||||||
|
return Result<string?>.FromSuccess(null);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue