1
0
Fork 1
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:
Octol1ttle 2023-06-08 21:04:17 +05:00
parent a83aa03cf0
commit 806746925e
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
10 changed files with 275 additions and 81 deletions

View file

@ -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;
}
} }

View file

@ -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);
} }
} }

View file

@ -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();

View file

@ -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
View file

@ -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);
} }
} }
} }

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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();

View 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);
}
}