ton of stuff

- new command handler
- multilanguage support
- time out support
- responses
- a ton of stuff
This commit is contained in:
l1ttleO 2022-01-18 22:38:15 +05:00
parent 1c9caf6d75
commit f30485dd71
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
29 changed files with 1686 additions and 520 deletions

View file

@ -1,4 +1,5 @@
using System.Text.Json; using System.Globalization;
using System.Text.Json;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
@ -6,9 +7,6 @@ namespace Boyfriend;
public static class Boyfriend { public static class Boyfriend {
public static void Main()
=> Init().GetAwaiter().GetResult();
private static readonly DiscordSocketConfig Config = new() { private static readonly DiscordSocketConfig Config = new() {
MessageCacheSize = 250, MessageCacheSize = 250,
GatewayIntents = GatewayIntents.All GatewayIntents = GatewayIntents.All
@ -18,15 +16,19 @@ public static class Boyfriend {
private static readonly Dictionary<ulong, GuildConfig> GuildConfigDictionary = new(); private static readonly Dictionary<ulong, GuildConfig> GuildConfigDictionary = new();
public static void Main() {
Init().GetAwaiter().GetResult();
}
private static async Task Init() { private static async Task Init() {
Client.Log += Log; Client.Log += Log;
var token = (await File.ReadAllTextAsync("token.txt")).Trim(); var token = (await File.ReadAllTextAsync("token.txt")).Trim();
await Client.LoginAsync(TokenType.Bot, token); await Client.LoginAsync(TokenType.Bot, token);
await Client.StartAsync(); await Client.StartAsync();
await Client.SetActivityAsync(new Game("Retrospecter - Electrospasm", ActivityType.Listening)); await Client.SetActivityAsync(new Game("Retrospecter - Chiller", ActivityType.Listening));
await new EventHandler().InitEvents(); new EventHandler().InitEvents();
await Task.Delay(-1); await Task.Delay(-1);
} }
@ -45,29 +47,28 @@ public static class Boyfriend {
try { try {
config = await JsonSerializer.DeserializeAsync<GuildConfig>(openStream) ?? throw new Exception(); config = await JsonSerializer.DeserializeAsync<GuildConfig>(openStream) ?? throw new Exception();
} catch (JsonException) { } catch (JsonException) {
config = new GuildConfig(guild.Id, "ru", "!", false, true, true, 0, 0, 0); Messages.Culture = new CultureInfo("ru");
config = new GuildConfig(guild.Id, "ru", "!", false, true,
true, Messages.DefaultWelcomeMessage, 0, 0, 0, 0);
} }
GuildConfigDictionary.Add(guild.Id, config); GuildConfigDictionary.Add(guild.Id, config);
} }
} }
public static GuildConfig GetGuildConfig(IGuild guild) { public static GuildConfig GetGuildConfig(IGuild guild) {
Messages.Culture = new CultureInfo("ru");
var toReturn = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id] var toReturn = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id]
: new GuildConfig(guild.Id, "ru", "!", false, true, true, 0, 0, 0); : new GuildConfig(guild.Id, "ru", "!", false, true, true, Messages.DefaultWelcomeMessage, 0, 0, 0, 0);
if (toReturn.Id != guild.Id) throw new Exception(); if (toReturn.Id != guild.Id) throw new Exception();
return toReturn; return toReturn;
} }
public static IGuild FindGuild(ITextChannel channel) { public static IGuild FindGuild(IMessageChannel channel) {
foreach (var guild in Client.Guilds) { foreach (var guild in Client.Guilds)
if (guild.Channels.Any(x => x == channel)) return guild; if (guild.Channels.Any(x => x == channel))
} return guild;
throw new Exception("Не удалось найти сервер по каналу!"); throw new Exception(Messages.CouldntFindGuildByChannel);
}
public static void ThrowFatal(Exception e) {
throw e;
} }
} }

View file

@ -11,10 +11,26 @@
<PackageProjectUrl>https://github.com/l1ttleO/Boyfriend-CSharp</PackageProjectUrl> <PackageProjectUrl>https://github.com/l1ttleO/Boyfriend-CSharp</PackageProjectUrl>
<RepositoryUrl>https://github.com/l1ttleO/Boyfriend-CSharp</RepositoryUrl> <RepositoryUrl>https://github.com/l1ttleO/Boyfriend-CSharp</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageVersion>1.0.1</PackageVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.0.0-dev-20211206.5" /> <PackageReference Include="Discord.Net" Version="3.1.0"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Messages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Messages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Messages.resx</DependentUpon>
</Compile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,3 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeEditing/Localization/Localizable/@EntryValue">No</s:String> <s:String x:Key="/Default/CodeEditing/Localization/Localizable/@EntryValue">No</s:String>
</wpf:ResourceDictionary> </wpf:ResourceDictionary>

View file

@ -1,48 +1,43 @@
using Discord; using System.Text.RegularExpressions;
using Boyfriend.Commands;
using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
namespace Boyfriend; namespace Boyfriend;
public static class CommandHandler { public static class CommandHandler {
public static async Task HandleCommand(SocketUserMessage message, int argPos) { public static readonly Command[] Commands = {
var context = new SocketCommandContext(Boyfriend.Client, message); new BanCommand(), new ClearCommand(), new HelpCommand(),
var result = await EventHandler.Commands.ExecuteAsync(context, argPos, null); new KickCommand(), new MuteCommand()
};
await HandleErrors(context, result); public static async Task HandleCommand(SocketUserMessage message) {
var context = new SocketCommandContext(Boyfriend.Client, message);
foreach (var command in Commands) {
var regex = new Regex(Regex.Escape(Boyfriend.GetGuildConfig(context.Guild).Prefix));
if (!command.GetAliases().Contains(regex.Replace(message.Content, "", 1).Split()[0])) continue;
var args = message.Content.Split().Skip(1).ToArray();
try {
if (command.GetArgumentsAmountRequired() > args.Length)
throw new ApplicationException(string.Format(Messages.NotEnoughArguments,
command.GetArgumentsAmountRequired(), args.Length));
await command.Run(context, args);
} }
private static async Task HandleErrors(SocketCommandContext context, IResult result) { catch (Exception e) {
var channel = context.Channel; var signature = e switch {
var reason = Utils.WrapInline(result.ErrorReason); ApplicationException => ":x:",
switch (result.Error) { UnauthorizedAccessException => ":no_entry_sign:",
case CommandError.Exception: _ => ":stop_sign:"
await channel.SendMessageAsync(reason); };
await context.Channel.SendMessageAsync($"{signature} `{e.Message}`");
if (e.StackTrace != null && e is not ApplicationException or UnauthorizedAccessException)
await context.Channel.SendMessageAsync(Utils.Wrap(e.StackTrace));
}
break; break;
case CommandError.Unsuccessful:
await channel.SendMessageAsync($"Выполнение команды завершилось неудачей: {reason}");
break;
case CommandError.MultipleMatches:
await channel.SendMessageAsync($"Обнаружены повторяющиеся типы аргументов! {reason}");
break;
case CommandError.ParseFailed:
await channel.SendMessageAsync($"Не удалось обработать команду: {reason}");
break;
case CommandError.UnknownCommand:
await channel.SendMessageAsync($"Неизвестная команда! {reason}");
break;
case CommandError.UnmetPrecondition:
await channel.SendMessageAsync($"У тебя недостаточно прав для выполнения этой к: {reason}");
break;
case CommandError.BadArgCount:
await channel.SendMessageAsync($"Неверное количество аргументов! {reason}");
break;
case CommandError.ObjectNotFound:
await channel.SendMessageAsync($"Нету нужных аргументов! {reason}");
break;
case null:
break;
default:
throw new Exception("CommandError");
} }
} }
@ -50,25 +45,25 @@ public static class CommandHandler {
GuildPermission forBot = GuildPermission.StartEmbeddedActivities) { GuildPermission forBot = GuildPermission.StartEmbeddedActivities) {
if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck; if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck;
if (!(await user.Guild.GetCurrentUserAsync()).GuildPermissions.Has(forBot)) if (!(await user.Guild.GetCurrentUserAsync()).GuildPermissions.Has(forBot))
throw new Exception("У меня недостаточно прав для выполнения этой команды!"); throw new UnauthorizedAccessException(Messages.CommandNoPermissionBot);
if (!user.GuildPermissions.Has(toCheck)) if (!user.GuildPermissions.Has(toCheck))
throw new Exception("У тебя недостаточно прав для выполнения этой команды!"); throw new UnauthorizedAccessException(Messages.CommandNoPermissionUser);
} }
public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) { public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) {
if (actor.Guild != target.Guild) if (actor.Guild != target.Guild)
throw new Exception("Участники находятся в разных гильдиях!"); throw new UnauthorizedAccessException(Messages.InteractionsDifferentGuilds);
var me = await target.Guild.GetCurrentUserAsync(); var me = await target.Guild.GetCurrentUserAsync();
if (actor.Id == actor.Guild.OwnerId) return; if (actor.Id == actor.Guild.OwnerId) return;
if (target.Id == target.Guild.OwnerId) if (target.Id == target.Guild.OwnerId)
throw new Exception("Ты не можешь взаимодействовать с владельцем сервера!"); throw new UnauthorizedAccessException(Messages.InteractionsOwner);
if (actor == target) if (actor == target)
throw new Exception("Ты не можешь взаимодействовать с самим собой!"); throw new UnauthorizedAccessException(Messages.InteractionsYourself);
if (target == me) if (target == me)
throw new Exception("Ты не можешь со мной взаимодействовать!"); throw new UnauthorizedAccessException(Messages.InteractionsMe);
if (actor.Hierarchy <= target.Hierarchy)
throw new Exception("Ты не можешь взаимодействовать с этим участником!");
if (me.Hierarchy <= target.Hierarchy) if (me.Hierarchy <= target.Hierarchy)
throw new Exception("Я не могу взаимодействовать с этим участником!"); throw new UnauthorizedAccessException(Messages.InteractionsFailedBot);
if (actor.Hierarchy <= target.Hierarchy)
throw new UnauthorizedAccessException(Messages.InteractionsFailedUser);
} }
} }

View file

@ -0,0 +1,61 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class BanCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
var toBan = await Utils.ParseUser(args[0]);
var reason = Utils.JoinString(args, 1);
TimeSpan duration;
try {
duration = Utils.GetTimeSpan(args[1]);
reason = Utils.JoinString(args, 2);
}
catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
}
var author = context.Guild.GetUser(context.User.Id);
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var memberToBan = context.Guild.GetUser(toBan.Id);
if (memberToBan != null)
await CommandHandler.CheckInteractions(author, memberToBan);
await BanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id),
toBan, duration, reason);
}
public static async Task BanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toBan,
TimeSpan duration, string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toBan, string.Format(Messages.YouWereBanned, author.Mention, guild.Name,
Utils.WrapInline(reason)));
var guildBanMessage = $"({author.Username}#{author.Discriminator}) {reason}";
await guild.AddBanAsync(toBan, 0, guildBanMessage);
var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason));
await Utils.SilentSendAsync(channel, string.Format(Messages.BanResponse, toBan.Mention,
Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
var task = new Task(() => UnbanCommand.UnbanUser(guild, null, guild.GetCurrentUserAsync().Result, toBan,
Messages.PunishmentExpired));
await Utils.StartDelayed(task, duration, () => guild.GetBanAsync(toBan).Result != null);
}
public override List<string> GetAliases() {
return new List<string> {"ban", "бан"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Банит пользователя";
}
}

View file

@ -1,45 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class BanModule : ModuleBase<SocketCommandContext> {
[Command("ban")]
[Summary("Банит пользователя")]
[Alias("бан")]
public async Task Run(string user, [Remainder]string reason) {
TimeSpan duration;
try {
var reasonArray = reason.Split();
duration = Utils.GetTimeSpan(reasonArray[0]);
reason = string.Join(" ", reasonArray.Skip(1));
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
}
var author = Context.Guild.GetUser(Context.User.Id);
var toBan = await Utils.ParseUser(user);
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var memberToBan = Context.Guild.GetUser(toBan.Id);
if (memberToBan != null)
await CommandHandler.CheckInteractions(author, memberToBan);
BanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toBan, duration, reason);
}
public static async void BanUser(IGuild guild, IGuildUser author, IUser toBan, TimeSpan duration, string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toBan, $"Тебя забанил {author.Mention} на сервере {guild.Name} за `{reason}`");
var guildBanMessage = $"({author.Username}#{author.Discriminator}) {reason}";
await guild.AddBanAsync(toBan, 0, guildBanMessage);
var notification = $"{authorMention} банит {toBan.Mention} за {Utils.WrapInline(reason)}";
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
var task = new Task(() => UnbanModule.UnbanUser(guild, guild.GetCurrentUserAsync().Result, toBan,
"Время наказания истекло"));
await Utils.StartDelayed(task, duration, () => guild.GetBanAsync(toBan).Result != null);
}
}

View file

@ -0,0 +1,49 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class ClearCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
int toDelete;
try {
toDelete = Convert.ToInt32(args[0]);
}
catch (Exception e) when (e is FormatException or OverflowException) {
throw new ApplicationException(Messages.ClearInvalidAmountSpecified);
}
if (context.Channel is not ITextChannel channel) return;
await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageMessages);
switch (toDelete) {
case < 1:
throw new ApplicationException(Messages.ClearNegativeAmount);
case > 200:
throw new ApplicationException(Messages.ClearAmountTooLarge);
default: {
var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
await channel.DeleteMessagesAsync(messages);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(context.Guild),
string.Format(Messages.MessagesDeleted, context.User.Mention, toDelete + 1,
Utils.MentionChannel(context.Channel.Id)));
break;
}
}
}
public override List<string> GetAliases() {
return new List<string> {"clear", "purge", "очистить", "стереть"};
}
public override int GetArgumentsAmountRequired() {
return 1;
}
public override string GetSummary() {
return "Очищает сообщения";
}
}

View file

@ -1,33 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class ClearModule : ModuleBase<SocketCommandContext> {
[Command("clear")]
[Summary("Удаляет указанное количество сообщений")]
[Alias("очистить")]
public async Task Run(int toDelete) {
if (Context.Channel is not ITextChannel channel) return;
await CommandHandler.CheckPermissions(Context.Guild.GetUser(Context.User.Id), GuildPermission.ManageMessages);
switch (toDelete) {
case < 1:
throw new Exception( "Указано отрицательное количество сообщений!");
case > 200:
throw new Exception("Указано слишком много сообщений!");
default: {
var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
await channel.DeleteMessagesAsync(messages);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Context.Guild),
$"{Context.User.Mention} удаляет {toDelete + 1} сообщений в канале " +
$"{Utils.MentionChannel(Context.Channel.Id)}");
break;
}
}
}
}

View file

@ -0,0 +1,19 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend.Commands;
public abstract class Command {
public abstract Task Run(SocketCommandContext context, string[] args);
public abstract List<string> GetAliases();
public abstract int GetArgumentsAmountRequired();
public abstract string GetSummary();
protected static async Task Warn(ISocketMessageChannel channel, string warning) {
await Utils.SilentSendAsync(channel as ITextChannel, ":warning: " + warning);
}
}

View file

@ -0,0 +1,30 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class HelpCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
var nl = Environment.NewLine;
var toSend = string.Format(Messages.CommandHelp, nl);
var prefix = Boyfriend.GetGuildConfig(context.Guild).Prefix;
toSend = CommandHandler.Commands.Aggregate(toSend,
(current, command) => current + $"`{prefix}{command.GetAliases()[0]}`: {command.GetSummary()}{nl}");
await context.Channel.SendMessageAsync(toSend);
}
public override List<string> GetAliases() {
return new List<string> {"help", "помощь", "справка"};
}
public override int GetArgumentsAmountRequired() {
return 0;
}
public override string GetSummary() {
return "Показывает эту справку";
}
}

View file

@ -1,25 +0,0 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class HelpModule : ModuleBase<SocketCommandContext> {
[Command("help")]
[Summary("Показывает эту справку")]
[Alias("помощь", "справка")]
public Task Run() {
var nl = Environment.NewLine;
var toSend = $"Справка по командам:{nl}";
var prefix = Boyfriend.GetGuildConfig(Context.Guild).Prefix;
foreach (var command in EventHandler.Commands.Commands) {
var aliases = command.Aliases.Aggregate("", (current, alias) =>
current + (current == "" ? "" : $", {prefix}") + alias);
toSend += $"`{prefix}{aliases}`: {command.Summary}{nl}";
}
Context.Channel.SendMessageAsync(toSend);
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,47 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class KickCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
var reason = Utils.JoinString(args, 1);
var author = context.Guild.GetUser(context.User.Id);
var toKick = await Utils.ParseMember(context.Guild, args[0]);
await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers);
await CommandHandler.CheckInteractions(author, toKick);
KickMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toKick,
reason);
}
private static async void KickMember(IGuild guild, ITextChannel? channel, IUser author, IGuildUser toKick,
string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toKick, string.Format(Messages.YouWereKicked, authorMention, guild.Name,
Utils.WrapInline(reason)));
var guildKickMessage = $"({author.Username}#{author.Discriminator}) {reason}";
await toKick.KickAsync(guildKickMessage);
var notification = string.Format(Messages.MemberKicked, authorMention, toKick.Mention,
Utils.WrapInline(reason));
await Utils.SilentSendAsync(channel, string.Format(Messages.KickResponse, toKick.Mention,
Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
public override List<string> GetAliases() {
return new List<string> {"kick", "кик"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Выгоняет участника";
}
}

View file

@ -1,34 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class KickModule : ModuleBase<SocketCommandContext> {
[Command("kick")]
[Summary("Выгоняет пользователя")]
[Alias("кик")]
public async Task Run(string user, [Remainder]string reason) {
var author = Context.Guild.GetUser(Context.User.Id);
var toKick = await Utils.ParseMember(Context.Guild, user);
await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers);
await CommandHandler.CheckInteractions(author, toKick);
KickMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toKick, reason);
}
private static async void KickMember(IGuild guild, IUser author, IGuildUser toKick, string reason) {
var authorMention = author.Mention;
await Utils.SendDirectMessage(toKick, $"Тебя кикнул {authorMention} на сервере {guild.Name} за " +
$"{Utils.WrapInline(reason)}");
var guildKickMessage = $"({author.Username}#{author.Discriminator}) {reason}";
await toKick.KickAsync(guildKickMessage);
var notification = $"{authorMention} выгоняет {toKick.Mention} за {Utils.WrapInline(reason)}";
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
}

View file

@ -0,0 +1,92 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class MuteCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
TimeSpan duration;
var reason = Utils.JoinString(args, 1);
try {
duration = Utils.GetTimeSpan(args[1]);
reason = Utils.JoinString(args, 2);
}
catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
}
var author = context.Guild.GetUser(context.User.Id);
var toMute = await Utils.ParseMember(context.Guild, args[0]);
if (toMute == null)
throw new ApplicationException(Messages.UserNotInGuild);
var role = Utils.GetMuteRole(context.Guild);
if (role != null && toMute.RoleIds.Any(x => x == role.Id) ||
toMute.TimedOutUntil != null && toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds()
> DateTimeOffset.Now.ToUnixTimeMilliseconds())
throw new ApplicationException(Messages.MemberAlreadyMuted);
var rolesRemoved = Boyfriend.GetGuildConfig(context.Guild).RolesRemovedOnMute;
if (rolesRemoved.ContainsKey(toMute.Id)) {
foreach (var roleId in rolesRemoved[toMute.Id]) await toMute.AddRoleAsync(roleId);
rolesRemoved.Remove(toMute.Id);
await Warn(context.Channel, Messages.RolesReturned);
return;
}
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toMute);
MuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toMute,
duration, reason);
}
private static async void MuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toMute,
TimeSpan duration, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var role = Utils.GetMuteRole(guild);
var config = Boyfriend.GetGuildConfig(guild);
if (config.RemoveRolesOnMute && role != null) {
var rolesRemoved = new List<ulong>();
try {
foreach (var roleId in toMute.RoleIds) {
if (roleId == guild.Id) continue;
await toMute.RemoveRoleAsync(roleId);
rolesRemoved.Add(roleId);
}
}
catch (NullReferenceException) { }
config.RolesRemovedOnMute.Add(toMute.Id, rolesRemoved);
await config.Save();
}
if (role != null)
await toMute.AddRoleAsync(role);
else
await toMute.SetTimeOutAsync(duration);
var notification = string.Format(Messages.MemberMuted, authorMention, toMute.Mention, Utils.WrapInline(reason));
await Utils.SilentSendAsync(channel, string.Format(Messages.MuteResponse, toMute.Mention,
Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
var task = new Task(() => UnmuteCommand.UnmuteMember(guild, null, guild.GetCurrentUserAsync().Result, toMute,
Messages.PunishmentExpired));
if (role != null)
await Utils.StartDelayed(task, duration, () => toMute.RoleIds.Any(x => x == role.Id));
}
public override List<string> GetAliases() {
return new List<string> {"mute", "мут", "мьют"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Глушит участника";
}
}

View file

@ -1,63 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class MuteModule : ModuleBase<SocketCommandContext> {
[Command("mute")]
[Summary("Глушит пользователя")]
[Alias("мут")]
public async Task Run(string user, [Remainder]string reason) {
TimeSpan duration;
try {
var reasonArray = reason.Split();
duration = Utils.GetTimeSpan(reasonArray[0]);
reason = string.Join(" ", reasonArray.Skip(1));
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
duration = TimeSpan.FromMilliseconds(-1);
}
var author = Context.Guild.GetUser(Context.User.Id);
var toMute = await Utils.ParseMember(Context.Guild, user);
if (toMute.RoleIds.Any(x => x == Utils.GetMuteRole(Context.Guild).Id))
throw new Exception("Участник уже заглушен!");
if (Boyfriend.GetGuildConfig(Context.Guild).RolesRemovedOnMute.ContainsKey(toMute.Id))
throw new Exception("Кто-то убрал роль мута самостоятельно!");
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toMute);
MuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toMute, duration, reason);
}
private static async void MuteMember(IGuild guild, IGuildUser author, IGuildUser toMute, TimeSpan duration,
string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var role = Utils.GetMuteRole(guild);
var config = Boyfriend.GetGuildConfig(guild);
if (config.RemoveRolesOnMute) {
var rolesRemoved = new List<ulong>();
try {
foreach (var roleId in toMute.RoleIds) {
if (roleId == guild.Id) continue;
await toMute.RemoveRoleAsync(roleId);
rolesRemoved.Add(roleId);
}
} catch (NullReferenceException) {}
config.RolesRemovedOnMute.Add(toMute.Id, rolesRemoved);
config.Save();
}
await toMute.AddRoleAsync(role);
var notification = $"{authorMention} глушит {toMute.Mention} за {Utils.WrapInline(reason)}";
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
var task = new Task(() => UnmuteModule.UnmuteMember(guild, guild.GetCurrentUserAsync().Result, toMute,
"Время наказания истекло"));
await Utils.StartDelayed(task, duration, () => toMute.RoleIds.Any(x => x == role.Id));
}
}

View file

@ -0,0 +1,25 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class PingCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
await context.Channel.SendMessageAsync($"{Utils.GetBeep(Boyfriend.GetGuildConfig(context.Guild).Lang)}" +
$"{Boyfriend.Client.Latency}{Messages.Milliseconds}");
}
public override List<string> GetAliases() {
return new List<string> {"ping", "пинг", "задержка"};
}
public override int GetArgumentsAmountRequired() {
return 0;
}
public override string GetSummary() {
return "Измеряет время обработки REST-запроса";
}
}

View file

@ -1,14 +0,0 @@
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class PingModule : ModuleBase<SocketCommandContext> {
[Command("ping")]
[Summary("Измеряет время обработки REST-запроса")]
[Alias("пинг")]
public async Task Run()
=> await ReplyAsync($"{Utils.GetBeep()}{Boyfriend.Client.Latency}мс");
}

View file

@ -0,0 +1,125 @@
using System.Globalization;
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class SettingsCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageGuild);
var config = Boyfriend.GetGuildConfig(context.Guild);
var guild = context.Guild;
if (args.Length == 0) {
var nl = Environment.NewLine;
var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel);
var admin = adminLogChannel == null ? Messages.ChannelNotSpecified : adminLogChannel.Mention;
var botLogChannel = guild.GetTextChannel(config.BotLogChannel);
var bot = botLogChannel == null ? Messages.ChannelNotSpecified : botLogChannel.Mention;
var muteRole = guild.GetRole(config.MuteRole);
var mute = muteRole == null ? Messages.RoleNotSpecified : muteRole.Mention;
var defaultRole = guild.GetRole(config.DefaultRole);
var defaultr = muteRole == null ? Messages.RoleNotSpecified : defaultRole.Mention;
var toSend = string.Format(Messages.CurrentSettings, nl) +
string.Format(Messages.CurrentSettingsLang, config.Lang, nl) +
string.Format(Messages.CurrentSettingsPrefix, config.Prefix, nl) +
string.Format(Messages.CurrentSettingsRemoveRoles, YesOrNo(config.RemoveRolesOnMute), nl) +
string.Format(Messages.CurrentSettingsUseSystemChannel, YesOrNo(config.UseSystemChannel), nl) +
string.Format(Messages.CurrentSettingsSendWelcomeMessages, YesOrNo(config.UseSystemChannel),
nl) +
string.Format(Messages.CurrentSettingsWelcomeMessage, config.WelcomeMessage, nl) +
string.Format(Messages.CurrentSettingsDefaultRole, defaultr, nl) +
string.Format(Messages.CurrentSettingsMuteRole, mute, nl) +
string.Format(Messages.CurrentSettingsAdminLogChannel, admin, nl) +
string.Format(Messages.CurrentSettingsBotLogChannel, bot);
await Utils.SilentSendAsync(context.Channel as ITextChannel ?? throw new Exception(), toSend);
return;
}
var setting = args[0].ToLower();
var value = args[1].ToLower();
var boolValue = ParseBool(args[1]);
var channel = await Utils.ParseChannelNullable(value) as IGuildChannel;
var role = Utils.ParseRoleNullable(guild, value);
switch (setting) {
case "lang" when value is not ("ru" or "en"):
throw new Exception(Messages.LanguageNotSupported);
case "lang":
config.Lang = value;
Messages.Culture = new CultureInfo(value);
break;
case "prefix":
config.Prefix = value;
break;
case "removerolesonmute":
config.RemoveRolesOnMute = GetBoolValue(boolValue);
break;
case "usesystemchannel":
config.UseSystemChannel = GetBoolValue(boolValue);
break;
case "sendwelcomemessages":
config.SendWelcomeMessages = GetBoolValue(boolValue);
break;
case "welcomemessage":
config.WelcomeMessage = value;
break;
case "defaultrole":
config.DefaultRole = GetRoleId(role);
break;
case "muterole":
config.MuteRole = GetRoleId(role);
break;
case "adminlogchannel":
config.AdminLogChannel = GetChannelId(channel);
break;
case "botlogchannel":
config.BotLogChannel = GetChannelId(channel);
break;
}
await config.Save();
await context.Channel.SendMessageAsync(Messages.SettingsUpdated);
}
private static bool? ParseBool(string toParse) {
try {
return bool.Parse(toParse.ToLower());
}
catch (FormatException) {
return null;
}
}
private static bool GetBoolValue(bool? from) {
return from ?? throw new Exception(Messages.InvalidBoolean);
}
private static ulong GetRoleId(IRole? role) {
return (role ?? throw new Exception(Messages.InvalidRoleSpecified)).Id;
}
private static ulong GetChannelId(IGuildChannel? channel) {
return (channel ?? throw new Exception(Messages.InvalidChannelSpecified)).Id;
}
private static string YesOrNo(bool isYes) {
return isYes ? Messages.Yes : Messages.No;
}
public override List<string> GetAliases() {
return new List<string> {"settings", "настройки", "config", "конфиг "};
}
public override int GetArgumentsAmountRequired() {
return 0;
}
public override string GetSummary() {
return "Настраивает бота отдельно для этого сервера";
}
}

View file

@ -1,115 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands;
public class SettingsModule : ModuleBase<SocketCommandContext> {
[Command("settings")]
[Summary("Настраивает бота")]
[Alias("config", "настройки", "конфиг")]
public async Task Run([Remainder] string s = "") {
await CommandHandler.CheckPermissions(Context.Guild.GetUser(Context.User.Id), GuildPermission.ManageGuild);
var config = Boyfriend.GetGuildConfig(Context.Guild);
var sArray = s.Split(" ");
var guild = Context.Guild;
if (s == "") {
var nl = Environment.NewLine;
var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel);
var admin = adminLogChannel == null ? "Не указан" : adminLogChannel.Mention;
var botLogChannel = guild.GetTextChannel(config.BotLogChannel);
var bot = botLogChannel == null ? "Не указан" : botLogChannel.Mention;
var muteRole = guild.GetRole(config.MuteRole);
var mute = muteRole == null ? "Не указана" : muteRole.Mention;
var toSend = $"Текущие настройки:{nl}" +
$"Язык (`lang`): `{config.Lang}`{nl}" +
$"Префикс (`prefix`): `{config.Prefix}`{nl}" +
$"Удалять роли при муте (`removeRolesOnMute`): {YesOrNo(config.RemoveRolesOnMute)}{nl}" +
"Использовать канал системных сообщений для уведомлений (`useSystemChannel`): " +
$"{YesOrNo(config.UseSystemChannel)}{nl}" +
$"Отправлять приветствия (`sendWelcomeMessages`): {YesOrNo(config.UseSystemChannel)}{nl}" +
$"Роль мута (`muteRole`): {mute}{nl}" +
$"Канал админ-уведомлений (`adminLogChannel`): " +
$"{admin}{nl}" +
$"Канал бот-уведомлений (`botLogChannel`): " +
$"{bot}";
await Utils.SilentSendAsync(Context.Channel as ITextChannel ?? throw new Exception(), toSend);
return;
}
var setting = sArray[0].ToLower();
var value = sArray[1].ToLower();
ITextChannel? channel;
try {
channel = await Utils.ParseChannel(value) as ITextChannel;
} catch (FormatException) {
channel = null;
}
IRole? role;
try {
role = Utils.ParseRole(guild, value);
}
catch (FormatException) {
role = null;
}
var boolValue = ParseBool(sArray[1]);
switch (setting) {
case "lang" when sArray[1].ToLower() != "ru":
throw new Exception("Язык не поддерживается!");
case "lang":
config.Lang = value;
break;
case "prefix":
config.Prefix = value;
break;
case "removerolesonmute":
config.RemoveRolesOnMute = boolValue ??
throw new Exception("Неверный параметр! Требуется `true` или `false");
break;
case "usesystemchannel":
config.UseSystemChannel = boolValue ??
throw new Exception("Неверный параметр! Требуется `true` или `false");
break;
case "sendwelcomemessages":
config.SendWelcomeMessages = boolValue ??
throw new Exception("Неверный параметр! Требуется `true` или `false");
break;
case "adminlogchannel":
config.AdminLogChannel = Convert.ToUInt64((channel ??
throw new Exception("Указан недействительный канал!"))
.Id);
break;
case "botlogchannel":
config.BotLogChannel = Convert.ToUInt64((channel ??
throw new Exception("Указан недействительный канал!"))
.Id);
break;
case "muterole":
config.MuteRole = Convert.ToUInt64((role ?? throw new Exception("Указана недействительная роль!"))
.Id);
break;
}
config.Save();
await Context.Channel.SendMessageAsync("Настройки успешно обновлены!");
}
private static bool? ParseBool(string toParse) {
try {
return bool.Parse(toParse.ToLower());
} catch (FormatException) {
return null;
}
}
private static string YesOrNo(bool isYes) {
return isYes ? "Да" : "Нет";
}
}

View file

@ -0,0 +1,43 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class UnbanCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
var toUnban = await Utils.ParseUser(args[0]);
if (context.Guild.GetBanAsync(toUnban.Id) == null)
throw new Exception(Messages.UserNotBanned);
UnbanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toUnban,
Utils.JoinString(args, 1));
}
public static async void UnbanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toUnban,
string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var authorMention = author.Mention;
var notification = string.Format(Messages.UserUnbanned, authorMention, toUnban.Mention,
Utils.WrapInline(reason));
await guild.RemoveBanAsync(toUnban);
await Utils.SilentSendAsync(channel, string.Format(Messages.UnbanResponse, toUnban.Mention,
Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
public override List<string> GetAliases() {
return new List<string> {"unban", "разбан"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Возвращает пользователя из бана";
}
}

View file

@ -1,29 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class UnbanModule : ModuleBase<SocketCommandContext> {
[Command("unban")]
[Summary("Возвращает пользователя из бана")]
[Alias("разбан")]
public async Task Run(string user, [Remainder] string reason) {
var toUnban = await Utils.ParseUser(user);
if (Context.Guild.GetBanAsync(toUnban.Id) == null)
throw new Exception("Пользователь не забанен!");
UnbanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnban, reason);
}
public static async void UnbanUser(IGuild guild, IGuildUser author, IUser toUnban, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
var authorMention = author.Mention;
var notification = $"{authorMention} возвращает из бана {toUnban.Mention} за {Utils.WrapInline(reason)}";
await guild.RemoveBanAsync(toUnban);
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
}

View file

@ -0,0 +1,61 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class UnmuteCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) {
var toUnmute = await Utils.ParseMember(context.Guild, args[0]);
var author = context.Guild.GetUser(context.User.Id);
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toUnmute);
var role = Utils.GetMuteRole(context.Guild);
if (role != null)
if (toUnmute.RoleIds.All(x => x != role.Id)) {
var rolesRemoved = Boyfriend.GetGuildConfig(context.Guild).RolesRemovedOnMute;
foreach (var roleId in rolesRemoved[toUnmute.Id]) await toUnmute.AddRoleAsync(roleId);
rolesRemoved.Remove(toUnmute.Id);
throw new ApplicationException(Messages.RolesReturned);
}
UnmuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id),
toUnmute, Utils.JoinString(args, 1));
}
public static async void UnmuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toUnmute,
string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var notification = string.Format(Messages.MemberUnmuted, authorMention, toUnmute.Mention,
Utils.WrapInline(reason));
await toUnmute.RemoveRoleAsync(Utils.GetMuteRole(guild));
var config = Boyfriend.GetGuildConfig(guild);
if (config.RolesRemovedOnMute.ContainsKey(toUnmute.Id)) {
foreach (var roleId in config.RolesRemovedOnMute[toUnmute.Id]) await toUnmute.AddRoleAsync(roleId);
config.RolesRemovedOnMute.Remove(toUnmute.Id);
}
await Utils.SilentSendAsync(channel, string.Format(Messages.UnmuteResponse, toUnmute.Mention,
Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
public override List<string> GetAliases() {
return new List<string> {"unmute", "размут"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Снимает мут с участника";
}
}

View file

@ -1,46 +0,0 @@
using Discord;
using Discord.Commands;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands;
public class UnmuteModule : ModuleBase<SocketCommandContext> {
[Command("unmute")]
[Summary("Возвращает пользователя из мута")]
[Alias("размут")]
public async Task Run(string user, [Remainder] string reason) {
var toUnmute = await Utils.ParseMember(Context.Guild, user);
var author = Context.Guild.GetUser(Context.User.Id);
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
await CommandHandler.CheckInteractions(author, toUnmute);
if (toUnmute.RoleIds.All(x => x != Utils.GetMuteRole(Context.Guild).Id)) {
var rolesRemoved = Boyfriend.GetGuildConfig(Context.Guild).RolesRemovedOnMute;
if (!rolesRemoved.ContainsKey(toUnmute.Id)) throw new Exception("Пользователь не в муте!");
rolesRemoved.Remove(toUnmute.Id);
throw new Exception("Пользователь не в муте, но я нашёл и удалил запись о его удалённых ролях!");
}
UnmuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnmute, reason);
}
public static async void UnmuteMember(IGuild guild, IGuildUser author, IGuildUser toUnmute, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
var authorMention = author.Mention;
var notification = $"{authorMention} возвращает из мута {toUnmute.Mention} за {Utils.WrapInline(reason)}";
await toUnmute.RemoveRoleAsync(Utils.GetMuteRole(guild));
var config = Boyfriend.GetGuildConfig(guild);
if (config.RolesRemovedOnMute.ContainsKey(toUnmute.Id)) {
foreach (var roleId in config.RolesRemovedOnMute[toUnmute.Id]) {
await toUnmute.AddRoleAsync(roleId);
}
config.RolesRemovedOnMute.Remove(toUnmute.Id);
}
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
}
}

View file

@ -1,4 +1,4 @@
using System.Reflection; using System.Globalization;
using Boyfriend.Commands; using Boyfriend.Commands;
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
@ -8,24 +8,25 @@ namespace Boyfriend;
public class EventHandler { public class EventHandler {
private readonly DiscordSocketClient _client = Boyfriend.Client; private readonly DiscordSocketClient _client = Boyfriend.Client;
public static readonly CommandService Commands = new();
public async Task InitEvents() { public void InitEvents() {
_client.Ready += ReadyEvent; _client.Ready += ReadyEvent;
_client.MessageDeleted += MessageDeletedEvent; _client.MessageDeleted += MessageDeletedEvent;
_client.MessageReceived += MessageReceivedEvent; _client.MessageReceived += MessageReceivedEvent;
_client.MessageUpdated += MessageUpdatedEvent; _client.MessageUpdated += MessageUpdatedEvent;
_client.UserJoined += UserJoinedEvent; _client.UserJoined += UserJoinedEvent;
await Commands.AddModulesAsync(Assembly.GetEntryAssembly(), null);
} }
private static async Task ReadyEvent() { private static async Task ReadyEvent() {
await Boyfriend.SetupGuildConfigs(); await Boyfriend.SetupGuildConfigs();
var i = new Random().Next(3);
foreach (var guild in Boyfriend.Client.Guilds) { foreach (var guild in Boyfriend.Client.Guilds) {
var channel = guild.GetTextChannel(Boyfriend.GetGuildConfig(guild).BotLogChannel); var config = Boyfriend.GetGuildConfig(guild);
Messages.Culture = new CultureInfo(config.Lang);
var channel = guild.GetTextChannel(config.BotLogChannel);
if (channel == null) continue; if (channel == null) continue;
await channel.SendMessageAsync($"{Utils.GetBeep()}Я запустился! (C#)"); await channel.SendMessageAsync(string.Format(Messages.Ready, Utils.GetBeep(config.Lang, i)));
} }
} }
@ -33,13 +34,11 @@ public class EventHandler {
Cacheable<IMessageChannel, ulong> channel) { Cacheable<IMessageChannel, ulong> channel) {
var msg = message.Value; var msg = message.Value;
var toSend = msg == null var toSend = msg == null
? $"Удалено сообщение в канале {Utils.MentionChannel(channel.Id)}, но я забыл что там было" ? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id))
: $"Удалено сообщение от {msg.Author.Mention} в канале " + : string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) +
$"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}"; $"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}";
try {
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel( await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(
Boyfriend.FindGuild(channel.Value as ITextChannel)), toSend); Boyfriend.FindGuild(channel.Value)), toSend);
} catch (ArgumentException) {}
} }
private static async Task MessageReceivedEvent(SocketMessage messageParam) { private static async Task MessageReceivedEvent(SocketMessage messageParam) {
@ -48,45 +47,54 @@ public class EventHandler {
var guild = user.Guild; var guild = user.Guild;
var argPos = 0; var argPos = 0;
var guildConfig = Boyfriend.GetGuildConfig(guild);
Messages.Culture = new CultureInfo(guildConfig.Lang);
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2)
&& !user.GuildPermissions.MentionEveryone) && !user.GuildPermissions.MentionEveryone)
BanModule.BanUser(guild, await guild.GetCurrentUserAsync(), user, TimeSpan.FromMilliseconds(-1), await BanCommand.BanUser(guild, null, await guild.GetCurrentUserAsync(), user,
"Более 3-ёх упоминаний в одном сообщении"); TimeSpan.FromMilliseconds(-1), Messages.AutobanReason);
var prevs = await message.Channel.GetMessagesAsync(3).FlattenAsync(); var prevs = await message.Channel.GetMessagesAsync(3).FlattenAsync();
var prevsArray = prevs as IMessage[] ?? prevs.ToArray(); var prevsArray = prevs as IMessage[] ?? prevs.ToArray();
var prev = prevsArray[1].Content; var prev = "";
var prevFailsafe = prevsArray[2].Content; var prevFailsafe = "";
if (message.Channel is not ITextChannel channel) throw new Exception(); try {
if (!(message.HasStringPrefix(Boyfriend.GetGuildConfig(guild).Prefix, ref argPos) prev = prevsArray[1].Content;
prevFailsafe = prevsArray[2].Content;
}
catch (IndexOutOfRangeException) { }
if (!(message.HasStringPrefix(guildConfig.Prefix, ref argPos)
|| message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos)) || message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos))
|| user == await Boyfriend.FindGuild(channel).GetCurrentUserAsync() || user == await guild.GetCurrentUserAsync()
|| user.IsBot && message.Content.Contains(prev) || message.Content.Contains(prevFailsafe)) || user.IsBot && message.Content.Contains(prev) || message.Content.Contains(prevFailsafe))
return; return;
await CommandHandler.HandleCommand(message, argPos); await CommandHandler.HandleCommand(message);
} }
private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket, private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket,
ISocketMessageChannel channel) { ISocketMessageChannel channel) {
var msg = messageCached.Value; var msg = messageCached.Value;
var nl = Environment.NewLine; var nl = Environment.NewLine;
if (msg.Content == messageSocket.Content) return; if (msg != null && msg.Content == messageSocket.Content) return;
var toSend = msg == null var toSend = msg == null
? $"Отредактировано сообщение от {messageSocket.Author.Mention} в канале" + ? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention,
$" {Utils.MentionChannel(channel.Id)}," + " но я забыл что там было до редактирования: " + Utils.MentionChannel(channel.Id)) +
Utils.Wrap(messageSocket.Content) Utils.Wrap(messageSocket.Content)
: $"Отредактировано сообщение от {msg.Author.Mention} " + : string.Format(Messages.CachedMessageEdited, msg.Author.Mention, Utils.MentionChannel(channel.Id), nl, nl,
$"в канале {Utils.MentionChannel(channel.Id)}." + Utils.Wrap(msg.Content), nl, nl, Utils.Wrap(messageSocket.Content));
$"{nl}До:{nl}{Utils.Wrap(msg.Content)}{nl}После:{nl}{Utils.Wrap(messageSocket.Content)}"; await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel)),
try {
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel as ITextChannel)),
toSend); toSend);
} catch (ArgumentException) {}
} }
private static async Task UserJoinedEvent(SocketGuildUser user) { private static async Task UserJoinedEvent(SocketGuildUser user) {
var guild = user.Guild; var guild = user.Guild;
await guild.SystemChannel.SendMessageAsync($"{user.Mention}, добро пожаловать на сервер {guild.Name}"); var config = Boyfriend.GetGuildConfig(guild);
if (config.SendWelcomeMessages)
await Utils.SilentSendAsync(guild.SystemChannel, string.Format(config.WelcomeMessage, user.Mention,
guild.Name));
if (config.DefaultRole != 0)
await user.AddRoleAsync(Utils.ParseRole(guild, config.DefaultRole.ToString()));
} }
} }

View file

@ -9,26 +9,31 @@ public class GuildConfig {
public bool RemoveRolesOnMute { get; set; } public bool RemoveRolesOnMute { get; set; }
public bool UseSystemChannel { get; set; } public bool UseSystemChannel { get; set; }
public bool SendWelcomeMessages { get; set; } public bool SendWelcomeMessages { get; set; }
public string WelcomeMessage { get; set; }
public ulong DefaultRole { get; set; }
public ulong MuteRole { get; set; } public ulong MuteRole { get; set; }
public ulong AdminLogChannel { get; set; } public ulong AdminLogChannel { get; set; }
public ulong BotLogChannel { get; set; } public ulong BotLogChannel { get; set; }
public Dictionary<ulong, List<ulong>> RolesRemovedOnMute { get; set; } public Dictionary<ulong, List<ulong>> RolesRemovedOnMute { get; set; }
public GuildConfig(ulong id, string lang, string prefix, bool removeRolesOnMute, bool useSystemChannel, public GuildConfig(ulong id, string lang, string prefix, bool removeRolesOnMute, bool useSystemChannel,
bool sendWelcomeMessages, ulong muteRole, ulong adminLogChannel, ulong botLogChannel) { bool sendWelcomeMessages, string welcomeMessage, ulong defaultRole, ulong muteRole, ulong adminLogChannel,
ulong botLogChannel) {
Id = id; Id = id;
Lang = lang; Lang = lang;
Prefix = prefix; Prefix = prefix;
RemoveRolesOnMute = removeRolesOnMute; RemoveRolesOnMute = removeRolesOnMute;
UseSystemChannel = useSystemChannel; UseSystemChannel = useSystemChannel;
SendWelcomeMessages = sendWelcomeMessages; SendWelcomeMessages = sendWelcomeMessages;
WelcomeMessage = welcomeMessage;
DefaultRole = defaultRole;
MuteRole = muteRole; MuteRole = muteRole;
AdminLogChannel = adminLogChannel; AdminLogChannel = adminLogChannel;
BotLogChannel = botLogChannel; BotLogChannel = botLogChannel;
RolesRemovedOnMute = new Dictionary<ulong, List<ulong>>(); RolesRemovedOnMute = new Dictionary<ulong, List<ulong>>();
} }
public async void Save() { public async Task Save() {
await using var stream = File.OpenWrite("config_" + Id + ".json"); await using var stream = File.OpenWrite("config_" + Id + ".json");
await JsonSerializer.SerializeAsync(stream, this); await JsonSerializer.SerializeAsync(stream, this);
} }

486
Boyfriend/Messages.Designer.cs generated Normal file
View file

@ -0,0 +1,486 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Boyfriend {
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Messages {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Messages() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Boyfriend.Messages", typeof(Messages).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
internal static string CouldntFindGuildByChannel {
get {
return ResourceManager.GetString("CouldntFindGuildByChannel", resourceCulture);
}
}
internal static string Ready {
get {
return ResourceManager.GetString("Ready", resourceCulture);
}
}
internal static string UncachedMessageDeleted {
get {
return ResourceManager.GetString("UncachedMessageDeleted", resourceCulture);
}
}
internal static string CachedMessageDeleted {
get {
return ResourceManager.GetString("CachedMessageDeleted", resourceCulture);
}
}
internal static string AutobanReason {
get {
return ResourceManager.GetString("AutobanReason", resourceCulture);
}
}
internal static string UncachedMessageEdited {
get {
return ResourceManager.GetString("UncachedMessageEdited", resourceCulture);
}
}
internal static string CachedMessageEdited {
get {
return ResourceManager.GetString("CachedMessageEdited", resourceCulture);
}
}
internal static string DefaultWelcomeMessage {
get {
return ResourceManager.GetString("DefaultWelcomeMessage", resourceCulture);
}
}
internal static string Beep1 {
get {
return ResourceManager.GetString("Beep1", resourceCulture);
}
}
internal static string Beep2 {
get {
return ResourceManager.GetString("Beep2", resourceCulture);
}
}
internal static string Beep3 {
get {
return ResourceManager.GetString("Beep3", resourceCulture);
}
}
internal static string InvalidAdminLogChannel {
get {
return ResourceManager.GetString("InvalidAdminLogChannel", resourceCulture);
}
}
internal static string MuteRoleRequired {
get {
return ResourceManager.GetString("MuteRoleRequired", resourceCulture);
}
}
internal static string CommandExecutionUnsuccessful {
get {
return ResourceManager.GetString("CommandExecutionUnsuccessful", resourceCulture);
}
}
internal static string RepeatedArgumentsDetected {
get {
return ResourceManager.GetString("RepeatedArgumentsDetected", resourceCulture);
}
}
internal static string CommandParseFailed {
get {
return ResourceManager.GetString("CommandParseFailed", resourceCulture);
}
}
internal static string UnknownCommand {
get {
return ResourceManager.GetString("UnknownCommand", resourceCulture);
}
}
internal static string BadArgumentCount {
get {
return ResourceManager.GetString("BadArgumentCount", resourceCulture);
}
}
internal static string ArgumentNotPresent {
get {
return ResourceManager.GetString("ArgumentNotPresent", resourceCulture);
}
}
internal static string CommandNoPermissionBot {
get {
return ResourceManager.GetString("CommandNoPermissionBot", resourceCulture);
}
}
internal static string CommandNoPermissionUser {
get {
return ResourceManager.GetString("CommandNoPermissionUser", resourceCulture);
}
}
internal static string InteractionsDifferentGuilds {
get {
return ResourceManager.GetString("InteractionsDifferentGuilds", resourceCulture);
}
}
internal static string InteractionsOwner {
get {
return ResourceManager.GetString("InteractionsOwner", resourceCulture);
}
}
internal static string InteractionsYourself {
get {
return ResourceManager.GetString("InteractionsYourself", resourceCulture);
}
}
internal static string InteractionsMe {
get {
return ResourceManager.GetString("InteractionsMe", resourceCulture);
}
}
internal static string InteractionsFailedUser {
get {
return ResourceManager.GetString("InteractionsFailedUser", resourceCulture);
}
}
internal static string InteractionsFailedBot {
get {
return ResourceManager.GetString("InteractionsFailedBot", resourceCulture);
}
}
internal static string YouWereBanned {
get {
return ResourceManager.GetString("YouWereBanned", resourceCulture);
}
}
internal static string UserBanned {
get {
return ResourceManager.GetString("UserBanned", resourceCulture);
}
}
internal static string PunishmentExpired {
get {
return ResourceManager.GetString("PunishmentExpired", resourceCulture);
}
}
internal static string ClearNegativeAmount {
get {
return ResourceManager.GetString("ClearNegativeAmount", resourceCulture);
}
}
internal static string ClearAmountTooLarge {
get {
return ResourceManager.GetString("ClearAmountTooLarge", resourceCulture);
}
}
internal static string MessagesDeleted {
get {
return ResourceManager.GetString("MessagesDeleted", resourceCulture);
}
}
internal static string CommandHelp {
get {
return ResourceManager.GetString("CommandHelp", resourceCulture);
}
}
internal static string YouWereKicked {
get {
return ResourceManager.GetString("YouWereKicked", resourceCulture);
}
}
internal static string MemberKicked {
get {
return ResourceManager.GetString("MemberKicked", resourceCulture);
}
}
internal static string MemberMuted {
get {
return ResourceManager.GetString("MemberMuted", resourceCulture);
}
}
internal static string Milliseconds {
get {
return ResourceManager.GetString("Milliseconds", resourceCulture);
}
}
internal static string MemberAlreadyMuted {
get {
return ResourceManager.GetString("MemberAlreadyMuted", resourceCulture);
}
}
internal static string MuteRoleManuallyRemoved {
get {
return ResourceManager.GetString("MuteRoleManuallyRemoved", resourceCulture);
}
}
internal static string ChannelNotSpecified {
get {
return ResourceManager.GetString("ChannelNotSpecified", resourceCulture);
}
}
internal static string RoleNotSpecified {
get {
return ResourceManager.GetString("RoleNotSpecified", resourceCulture);
}
}
internal static string CurrentSettings {
get {
return ResourceManager.GetString("CurrentSettings", resourceCulture);
}
}
internal static string CurrentSettingsLang {
get {
return ResourceManager.GetString("CurrentSettingsLang", resourceCulture);
}
}
internal static string CurrentSettingsPrefix {
get {
return ResourceManager.GetString("CurrentSettingsPrefix", resourceCulture);
}
}
internal static string CurrentSettingsRemoveRoles {
get {
return ResourceManager.GetString("CurrentSettingsRemoveRoles", resourceCulture);
}
}
internal static string CurrentSettingsUseSystemChannel {
get {
return ResourceManager.GetString("CurrentSettingsUseSystemChannel", resourceCulture);
}
}
internal static string CurrentSettingsSendWelcomeMessages {
get {
return ResourceManager.GetString("CurrentSettingsSendWelcomeMessages", resourceCulture);
}
}
internal static string CurrentSettingsDefaultRole {
get {
return ResourceManager.GetString("CurrentSettingsDefaultRole", resourceCulture);
}
}
internal static string CurrentSettingsMuteRole {
get {
return ResourceManager.GetString("CurrentSettingsMuteRole", resourceCulture);
}
}
internal static string CurrentSettingsAdminLogChannel {
get {
return ResourceManager.GetString("CurrentSettingsAdminLogChannel", resourceCulture);
}
}
internal static string CurrentSettingsBotLogChannel {
get {
return ResourceManager.GetString("CurrentSettingsBotLogChannel", resourceCulture);
}
}
internal static string LanguageNotSupported {
get {
return ResourceManager.GetString("LanguageNotSupported", resourceCulture);
}
}
internal static string SettingsUpdated {
get {
return ResourceManager.GetString("SettingsUpdated", resourceCulture);
}
}
internal static string InvalidBoolean {
get {
return ResourceManager.GetString("InvalidBoolean", resourceCulture);
}
}
internal static string InvalidRoleSpecified {
get {
return ResourceManager.GetString("InvalidRoleSpecified", resourceCulture);
}
}
internal static string InvalidChannelSpecified {
get {
return ResourceManager.GetString("InvalidChannelSpecified", resourceCulture);
}
}
internal static string Yes {
get {
return ResourceManager.GetString("Yes", resourceCulture);
}
}
internal static string No {
get {
return ResourceManager.GetString("No", resourceCulture);
}
}
internal static string UserNotBanned {
get {
return ResourceManager.GetString("UserNotBanned", resourceCulture);
}
}
internal static string MemberNotMuted {
get {
return ResourceManager.GetString("MemberNotMuted", resourceCulture);
}
}
internal static string RolesReturned {
get {
return ResourceManager.GetString("RolesReturned", resourceCulture);
}
}
internal static string MemberUnmuted {
get {
return ResourceManager.GetString("MemberUnmuted", resourceCulture);
}
}
internal static string UserUnbanned {
get {
return ResourceManager.GetString("UserUnbanned", resourceCulture);
}
}
internal static string CurrentSettingsWelcomeMessage {
get {
return ResourceManager.GetString("CurrentSettingsWelcomeMessage", resourceCulture);
}
}
internal static string NotEnoughArguments {
get {
return ResourceManager.GetString("NotEnoughArguments", resourceCulture);
}
}
internal static string ClearInvalidAmountSpecified {
get {
return ResourceManager.GetString("ClearInvalidAmountSpecified", resourceCulture);
}
}
internal static string BanResponse {
get {
return ResourceManager.GetString("BanResponse", resourceCulture);
}
}
internal static string KickResponse {
get {
return ResourceManager.GetString("KickResponse", resourceCulture);
}
}
internal static string UserNotInGuild {
get {
return ResourceManager.GetString("UserNotInGuild", resourceCulture);
}
}
internal static string MuteResponse {
get {
return ResourceManager.GetString("MuteResponse", resourceCulture);
}
}
internal static string UnbanResponse {
get {
return ResourceManager.GetString("UnbanResponse", resourceCulture);
}
}
internal static string UnmuteResponse {
get {
return ResourceManager.GetString("UnmuteResponse", resourceCulture);
}
}
}
}

246
Boyfriend/Messages.resx Normal file
View file

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true">
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="CouldntFindGuildByChannel" xml:space="preserve">
<value>Couldn't find guild by message!</value>
</data>
<data name="Ready" xml:space="preserve">
<value>{0}I'm ready! (C#)</value>
</data>
<data name="UncachedMessageDeleted" xml:space="preserve">
<value>Deleted message in {0}, but I forgot what was there</value>
</data>
<data name="CachedMessageDeleted" xml:space="preserve">
<value>Deleted message from {0} in channel </value>
</data>
<data name="AutobanReason" xml:space="preserve">
<value>Too many mentions in 1 message</value>
</data>
<data name="UncachedMessageEdited" xml:space="preserve">
<value>Message edited from {0} in channel {1}, but I forgot what was there before the edit</value>
</data>
<data name="CachedMessageEdited" xml:space="preserve">
<value>Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7}</value>
</data>
<data name="DefaultWelcomeMessage" xml:space="preserve">
<value>{0}, welcome to {1}</value>
</data>
<data name="Beep1" xml:space="preserve">
<value>Bah! </value>
</data>
<data name="Beep2" xml:space="preserve">
<value>Bop! </value>
</data>
<data name="Beep3" xml:space="preserve">
<value>Beep! </value>
</data>
<data name="InvalidAdminLogChannel" xml:space="preserve">
<value>Invalid admin log channel for guild</value>
</data>
<data name="MuteRoleRequired" xml:space="preserve">
<value>You must set up a mute role in settings!</value>
</data>
<data name="CommandExecutionUnsuccessful" xml:space="preserve">
<value>Command execution was unsuccessful: {0}</value>
</data>
<data name="RepeatedArgumentsDetected" xml:space="preserve">
<value>Repeated arguments detected! {0}</value>
</data>
<data name="CommandParseFailed" xml:space="preserve">
<value>Command parsing failed: {0}</value>
</data>
<data name="UnknownCommand" xml:space="preserve">
<value>Unknown command! {0}</value>
</data>
<data name="BadArgumentCount" xml:space="preserve">
<value>Invalid argument count! {0}</value>
</data>
<data name="ArgumentNotPresent" xml:space="preserve">
<value>Arguments not present! {0}</value>
</data>
<data name="CommandNoPermissionBot" xml:space="preserve">
<value>I do not have permission to execute this command!</value>
</data>
<data name="CommandNoPermissionUser" xml:space="preserve">
<value>You do not have permission to execute this command!</value>
</data>
<data name="InteractionsDifferentGuilds" xml:space="preserve">
<value>Members are in different guilds!</value>
</data>
<data name="InteractionsOwner" xml:space="preserve">
<value>You cannot interact with guild owner!</value>
</data>
<data name="InteractionsYourself" xml:space="preserve">
<value>You cannot interact with yourself!</value>
</data>
<data name="InteractionsMe" xml:space="preserve">
<value>You cannot interact with me!</value>
</data>
<data name="InteractionsFailedUser" xml:space="preserve">
<value>You cannot interact with this member!</value>
</data>
<data name="InteractionsFailedBot" xml:space="preserve">
<value>I cannot interact with this member!</value>
</data>
<data name="YouWereBanned" xml:space="preserve">
<value>You were banned by {0} in guild {1} for {2}</value>
</data>
<data name="UserBanned" xml:space="preserve">
<value>{0} banned {1} for {2}</value>
</data>
<data name="PunishmentExpired" xml:space="preserve">
<value>Punishment expired</value>
</data>
<data name="ClearNegativeAmount" xml:space="preserve">
<value>Negative message amount specified!</value>
</data>
<data name="ClearAmountTooLarge" xml:space="preserve">
<value>Too many messages specified!</value>
</data>
<data name="MessagesDeleted" xml:space="preserve">
<value>{0} deleted {1} messages in channel {2}</value>
</data>
<data name="CommandHelp" xml:space="preserve">
<value>Command help:{0}</value>
</data>
<data name="YouWereKicked" xml:space="preserve">
<value>You were kicked by {0} in guild {1} for {2}</value>
</data>
<data name="MemberKicked" xml:space="preserve">
<value>{0} kicked {1} for {2}</value>
</data>
<data name="MemberMuted" xml:space="preserve">
<value>{0} muted {1} for {2}</value>
</data>
<data name="Milliseconds" xml:space="preserve">
<value>ms</value>
</data>
<data name="MemberAlreadyMuted" xml:space="preserve">
<value>Member is already muted!</value>
</data>
<data name="MuteRoleManuallyRemoved" xml:space="preserve">
<value>Someone removed the mute role manually!</value>
</data>
<data name="ChannelNotSpecified" xml:space="preserve">
<value>Not specified</value>
</data>
<data name="RoleNotSpecified" xml:space="preserve">
<value>Not specified</value>
</data>
<data name="CurrentSettings" xml:space="preserve">
<value>Current settings:{0}</value>
</data>
<data name="CurrentSettingsLang" xml:space="preserve">
<value>Language (`lang`): `{0}`{1}</value>
</data>
<data name="CurrentSettingsPrefix" xml:space="preserve">
<value>Prefix (`prefix`): `{0}`{1}</value>
</data>
<data name="CurrentSettingsRemoveRoles" xml:space="preserve">
<value>Remove roles on mute (`removeRolesOnMute`): {0}{1}</value>
</data>
<data name="CurrentSettingsUseSystemChannel" xml:space="preserve">
<value>Use system channel for notifications (`useSystemChannel`): {0}{1}</value>
</data>
<data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve">
<value>Send welcome messages (`sendWelcomeMessages`): {0}{1}</value>
</data>
<data name="CurrentSettingsDefaultRole" xml:space="preserve">
<value>Default role (`defaultRole`): {0}{1}</value>
</data>
<data name="CurrentSettingsMuteRole" xml:space="preserve">
<value>Mute role (`muteRole`): {0}{1}</value>
</data>
<data name="CurrentSettingsAdminLogChannel" xml:space="preserve">
<value>Admin log channel (`adminLogChannel`): {0}{1}</value>
</data>
<data name="CurrentSettingsBotLogChannel" xml:space="preserve">
<value>Bot log channel (`botLogChannel`): {0}</value>
</data>
<data name="LanguageNotSupported" xml:space="preserve">
<value>Language not supported!</value>
</data>
<data name="SettingsUpdated" xml:space="preserve">
<value>Settings successfully updated</value>
</data>
<data name="InvalidBoolean" xml:space="preserve">
<value>Invalid argument! 'true' or 'false' required!</value>
</data>
<data name="InvalidRoleSpecified" xml:space="preserve">
<value>Invalid role specified!</value>
</data>
<data name="InvalidChannelSpecified" xml:space="preserve">
<value>Invalid channel specified!</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Yes</value>
</data>
<data name="No" xml:space="preserve">
<value>No</value>
</data>
<data name="UserNotBanned" xml:space="preserve">
<value>User not banned!</value>
</data>
<data name="MemberNotMuted" xml:space="preserve">
<value>Member not muted!</value>
</data>
<data name="RolesReturned" xml:space="preserve">
<value>Someone removed the mute role manually! I added back all roles that I removed during the mute</value>
</data>
<data name="MemberUnmuted" xml:space="preserve">
<value>{0} unmuted {1} for {2}</value>
</data>
<data name="UserUnbanned" xml:space="preserve">
<value>{0} unbanned {1} for {2}</value>
</data>
<data name="CurrentSettingsWelcomeMessage" xml:space="preserve">
<value>Welcome message: `{0}`{1}</value>
</data>
<data name="NotEnoughArguments" xml:space="preserve">
<value>Not enough arguments! Needed: {0}, provided: {1}</value>
</data>
<data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Invalid message amount specified!</value>
</data>
<data name="BanResponse" xml:space="preserve">
<value>:white_check_mark: Successfully banned {0} for {1}</value>
</data>
<data name="KickResponse" xml:space="preserve">
<value>:white_check_mark: Successfully kicked {0} for {1}</value>
</data>
<data name="UserNotInGuild" xml:space="preserve">
<value>The specified user is not a member of this server!</value>
</data>
<data name="MuteResponse" xml:space="preserve">
<value>:white_check_mark: Successfully muted {0} for {1}</value>
</data>
<data name="UnbanResponse" xml:space="preserve">
<value>:white_check_mark: Successfully unbanned {0} for {1}</value>
</data>
<data name="UnmuteResponse" xml:space="preserve">
<value>:white_check_mark: Successfully unmuted {0} for {1}</value>
</data>
</root>

237
Boyfriend/Messages.ru.resx Normal file
View file

@ -0,0 +1,237 @@
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="CouldntFindGuildByChannel" xml:space="preserve">
<value>Не удалось найти сервер по каналу!</value>
</data>
<data name="Ready" xml:space="preserve">
<value>{0}Я запустился! (C#)</value>
</data>
<data name="UncachedMessageDeleted" xml:space="preserve">
<value>Удалено сообщение в канале {0}, но я забыл что там было</value>
</data>
<data name="CachedMessageDeleted" xml:space="preserve">
<value>Удалено сообщение от {0} в канале </value>
</data>
<data name="AutobanReason" xml:space="preserve">
<value>Слишком много упоминаний в одном сообщении</value>
</data>
<data name="UncachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования</value>
</data>
<data name="CachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение от {0} в канале {1}.{2}До:{3}{4}{5}После:{6}{7}</value>
</data>
<data name="DefaultWelcomeMessage" xml:space="preserve">
<value>{0}, добро пожаловать на сервер {1}</value>
</data>
<data name="Beep1" xml:space="preserve">
<value>Бап! </value>
</data>
<data name="Beep2" xml:space="preserve">
<value>Боп! </value>
</data>
<data name="Beep3" xml:space="preserve">
<value>Бип! </value>
</data>
<data name="MuteRoleRequired" xml:space="preserve">
<value>Требуется указать роль мута в настройках!</value>
</data>
<data name="InvalidAdminLogChannel" xml:space="preserve">
<value>Неверный канал админ-логов для гильдии </value>
</data>
<data name="CommandExecutionUnsuccessful" xml:space="preserve">
<value>Выполнение команды завершилось неудачей: {0}</value>
</data>
<data name="RepeatedArgumentsDetected" xml:space="preserve">
<value>Обнаружены повторяющиеся типы аргументов! {0}</value>
</data>
<data name="CommandParseFailed" xml:space="preserve">
<value>Не удалось обработать команду: {0}</value>
</data>
<data name="UnknownCommand" xml:space="preserve">
<value>Неизвестная команда! {0}</value>
</data>
<data name="BadArgumentCount" xml:space="preserve">
<value>Неверное количество аргументов! {0}</value>
</data>
<data name="ArgumentNotPresent" xml:space="preserve">
<value>Нету нужных аргументов! {0}</value>
</data>
<data name="CommandNoPermissionBot" xml:space="preserve">
<value>У меня недостаточно прав для выполнения этой команды!</value>
</data>
<data name="CommandNoPermissionUser" xml:space="preserve">
<value>У тебя недостаточно прав для выполнения этой команды!</value>
</data>
<data name="InteractionsDifferentGuilds" xml:space="preserve">
<value>Участники находятся в разных гильдиях!</value>
</data>
<data name="InteractionsOwner" xml:space="preserve">
<value>Ты не можешь взаимодействовать с владельцем сервера!</value>
</data>
<data name="InteractionsYourself" xml:space="preserve">
<value>Ты не можешь взаимодействовать с самим собой!</value>
</data>
<data name="InteractionsMe" xml:space="preserve">
<value>Ты не можешь со мной взаимодействовать!</value>
</data>
<data name="InteractionsFailedUser" xml:space="preserve">
<value>Ты не можешь взаимодействовать с этим участником!</value>
</data>
<data name="InteractionsFailedBot" xml:space="preserve">
<value>Я не могу взаимодействовать с этим участником!</value>
</data>
<data name="YouWereBanned" xml:space="preserve">
<value>Тебя забанил {0} на сервере {1} за {2}</value>
</data>
<data name="UserBanned" xml:space="preserve">
<value>{0} банит {1} за {2}</value>
</data>
<data name="PunishmentExpired" xml:space="preserve">
<value>Время наказания истекло</value>
</data>
<data name="ClearNegativeAmount" xml:space="preserve">
<value>Указано отрицательное количество сообщений!</value>
</data>
<data name="ClearAmountTooLarge" xml:space="preserve">
<value>Указано слишком много сообщений!</value>
</data>
<data name="MessagesDeleted" xml:space="preserve">
<value>{0} удаляет {1} сообщений в канале {2}</value>
</data>
<data name="CommandHelp" xml:space="preserve">
<value>Справка по командам:{0}</value>
</data>
<data name="YouWereKicked" xml:space="preserve">
<value>Тебя кикнул {0} на сервере {1} за {2}</value>
</data>
<data name="MemberKicked" xml:space="preserve">
<value>{0} выгоняет {1} за {2}</value>
</data>
<data name="MemberMuted" xml:space="preserve">
<value>{0} глушит {1} за {2}</value>
</data>
<data name="Milliseconds" xml:space="preserve">
<value>мс</value>
</data>
<data name="MemberAlreadyMuted" xml:space="preserve">
<value>Участник уже заглушен!</value>
</data>
<data name="MuteRoleManuallyRemoved" xml:space="preserve">
<value>Кто-то убрал роль мута самостоятельно!</value>
</data>
<data name="ChannelNotSpecified" xml:space="preserve">
<value>Не указан</value>
</data>
<data name="RoleNotSpecified" xml:space="preserve">
<value>Не указана</value>
</data>
<data name="CurrentSettings" xml:space="preserve">
<value>Текущие настройки:{0}</value>
</data>
<data name="CurrentSettingsLang" xml:space="preserve">
<value>Язык (`lang`): `{0}`{1}</value>
</data>
<data name="CurrentSettingsPrefix" xml:space="preserve">
<value>Префикс (`prefix`): `{0}`{1}</value>
</data>
<data name="CurrentSettingsRemoveRoles" xml:space="preserve">
<value>Удалять роли при муте (`removeRolesOnMute`): {0}{1}</value>
</data>
<data name="CurrentSettingsUseSystemChannel" xml:space="preserve">
<value>Использовать канал системных сообщений для уведомлений (`useSystemChannel`): {0}{1}</value>
</data>
<data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve">
<value>Отправлять приветствия (`sendWelcomeMessages`): {0}{1}</value>
</data>
<data name="CurrentSettingsDefaultRole" xml:space="preserve">
<value>Стандартная роль (`defaultRole`): {0}{1}</value>
</data>
<data name="CurrentSettingsMuteRole" xml:space="preserve">
<value>Роль мута (`muteRole`): {0}{1}</value>
</data>
<data name="CurrentSettingsAdminLogChannel" xml:space="preserve">
<value>Канал админ-уведомлений (`adminLogChannel`): {0}{1}</value>
</data>
<data name="CurrentSettingsBotLogChannel" xml:space="preserve">
<value>Канал бот-уведомлений (`botLogChannel`): {0}</value>
</data>
<data name="LanguageNotSupported" xml:space="preserve">
<value>Язык не поддерживается!</value>
</data>
<data name="SettingsUpdated" xml:space="preserve">
<value>Настройки успешно обновлены!</value>
</data>
<data name="InvalidBoolean" xml:space="preserve">
<value>Неверный параметр! Требуется 'true' или 'false'</value>
</data>
<data name="InvalidRoleSpecified" xml:space="preserve">
<value>Указана недействительная роль!</value>
</data>
<data name="InvalidChannelSpecified" xml:space="preserve">
<value>Указан недействильный канал!</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Да</value>
</data>
<data name="No" xml:space="preserve">
<value>Нет</value>
</data>
<data name="UserNotBanned" xml:space="preserve">
<value>Пользователь не забанен!</value>
</data>
<data name="MemberNotMuted" xml:space="preserve">
<value>Участник не заглушен!</value>
</data>
<data name="RolesReturned" xml:space="preserve">
<value>Кто-то убрал роль мута самостоятельно! Я вернул все роли, которые забрал при муте</value>
</data>
<data name="MemberUnmuted" xml:space="preserve">
<value>{0} возвращает из мута {1} за {2}</value>
</data>
<data name="UserUnbanned" xml:space="preserve">
<value>{0} возвращает из бана {1} за {2}</value>
</data>
<data name="CurrentSettingsWelcomeMessage" xml:space="preserve">
<value>Приветствие: `{0}`{1}</value>
</data>
<data name="NotEnoughArguments" xml:space="preserve">
<value>Недостаточно аргументов! Требуется: {0}, указано: {1}</value>
</data>
<data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Указано неверное количество сообщений!</value>
</data>
<data name="BanResponse" xml:space="preserve">
<value>:white_check_mark: Успешно забанен {0} за {1}</value>
</data>
<data name="KickResponse" xml:space="preserve">
<value>:white_check_mark: Успешно выгнан {0} за {1}</value>
</data>
<data name="UserNotInGuild" xml:space="preserve">
<value>Указанный пользователь не является участником этого сервера!</value>
</data>
<data name="MuteResponse" xml:space="preserve">
<value>:white_check_mark: Успешно заглушен {0} за {1}</value>
</data>
<data name="UnbanResponse" xml:space="preserve">
<value>:white_check_mark: Успешно возвращён из бана {0} за {1}</value>
</data>
<data name="UnmuteResponse" xml:space="preserve">
<value>:white_check_mark: Успешно возвращён из мута {0} за {1}</value>
</data>
</root>

View file

@ -6,17 +6,23 @@ using Discord.Net;
namespace Boyfriend; namespace Boyfriend;
public static class Utils { public static class Utils {
public static string GetBeep() { private static readonly string[] Formats = {
var letters = new[] {"а", "о", "и"}; "%d'd'%h'h'%m'm'%s's'", "%d'd'%h'h'%m'm'", "%d'd'%h'h'%s's'", "%d'd'%h'h'", "%d'd'%m'm'%s's'", "%d'd'%m'm'",
return $"Б{letters[new Random().Next(3)]}п! "; "%d'd'%s's'", "%d'd'", "%h'h'%m'm'%s's'", "%h'h'%m'm'", "%h'h'%s's'", "%h'h'", "%m'm'%s's'", "%m'm'", "%s's'",
"%d'д'%h'ч'%m'м'%s'с'", "%d'д'%h'ч'%m'м'", "%d'д'%h'ч'%s'с'", "%d'д'%h'ч'", "%d'д'%m'м'%s'с'", "%d'д'%m'м'",
"%d'д'%s'с'", "%d'д'", "%h'ч'%m'м'%s'с'", "%h'ч'%m'м'", "%h'ч'%s'с'", "%h'ч'", "%m'м'%s'с'", "%m'м'", "%s'с'"
};
public static string GetBeep(string cultureInfo, int i = -1) {
Messages.Culture = new CultureInfo(cultureInfo);
var beeps = new[] {Messages.Beep1, Messages.Beep2, Messages.Beep3};
return beeps[i < 0 ? new Random().Next(3) : i];
} }
public static async Task<ITextChannel> GetAdminLogChannel(IGuild guild) { public static async Task<ITextChannel?> GetAdminLogChannel(IGuild guild) {
var adminLogChannel = await ParseChannel(Boyfriend.GetGuildConfig(guild).AdminLogChannel.ToString()); var adminLogChannel = await ParseChannelNullable(Boyfriend.GetGuildConfig(guild).AdminLogChannel.ToString());
if (adminLogChannel is ITextChannel channel) return adminLogChannel as ITextChannel;
return channel;
throw new Exception("Неверный канал админ-логов для гильдии " + guild.Id);
} }
public static string Wrap(string original) { public static string Wrap(string original) {
@ -43,6 +49,15 @@ public static class Utils {
return Convert.ToUInt64(Regex.Replace(mention, "[^0-9]", "")); return Convert.ToUInt64(Regex.Replace(mention, "[^0-9]", ""));
} }
private static ulong? ParseMentionNullable(string mention) {
try {
return ParseMention(mention) == 0 ? throw new FormatException() : ParseMention(mention);
}
catch (FormatException) {
return null;
}
}
public static async Task<IUser> ParseUser(string mention) { public static async Task<IUser> ParseUser(string mention) {
var user = Boyfriend.Client.GetUserAsync(ParseMention(mention)); var user = Boyfriend.Client.GetUserAsync(ParseMention(mention));
return await user; return await user;
@ -52,14 +67,22 @@ public static class Utils {
return await guild.GetUserAsync(ParseMention(mention)); return await guild.GetUserAsync(ParseMention(mention));
} }
public static async Task<IChannel> ParseChannel(string mention) { private static async Task<IChannel> ParseChannel(string mention) {
return await Boyfriend.Client.GetChannelAsync(ParseMention(mention)); return await Boyfriend.Client.GetChannelAsync(ParseMention(mention));
} }
public static IRole ParseRole(IGuild guild, string mention) { public static async Task<IChannel?> ParseChannelNullable(string mention) {
return ParseMentionNullable(mention) == null ? null : await ParseChannel(mention);
}
public static IRole? ParseRole(IGuild guild, string mention) {
return guild.GetRole(ParseMention(mention)); return guild.GetRole(ParseMention(mention));
} }
public static IRole? ParseRoleNullable(IGuild guild, string mention) {
return ParseMentionNullable(mention) == null ? null : ParseRole(guild, mention);
}
public static async Task SendDirectMessage(IUser user, string toSend) { public static async Task SendDirectMessage(IUser user, string toSend) {
try { try {
await user.SendMessageAsync(toSend); await user.SendMessageAsync(toSend);
@ -69,24 +92,23 @@ public static class Utils {
} }
} }
public static IRole GetMuteRole(IGuild guild) { public static IRole? GetMuteRole(IGuild guild) {
var role = guild.Roles.FirstOrDefault(x => x.Id == Boyfriend.GetGuildConfig(guild).MuteRole); var role = guild.Roles.FirstOrDefault(x => x.Id == Boyfriend.GetGuildConfig(guild).MuteRole);
if (role == null) throw new Exception("Требуется указать роль мута в настройках!");
return role; return role;
} }
public static async Task SilentSendAsync(ITextChannel channel, string text) { public static async Task SilentSendAsync(ITextChannel? channel, string text) {
if (channel == null) return;
try { try {
await channel.SendMessageAsync(text, false, null, null, AllowedMentions.None); await channel.SendMessageAsync(text, false, null, null, AllowedMentions.None);
} catch (ArgumentException) {} } catch (ArgumentException) {}
} }
private static readonly string[] Formats = {
"%d'd'%h'h'%m'm'%s's'", "%d'd'%h'h'%m'm'", "%d'd'%h'h'%s's'", "%d'd'%h'h'", "%d'd'%m'm'%s's'", "%d'd'%m'm'",
"%d'd'%s's'", "%d'd'", "%h'h'%m'm'%s's'", "%h'h'%m'm'", "%h'h'%s's'", "%h'h'", "%m'm'%s's'", "%m'm'", "%s's'"
};
public static TimeSpan GetTimeSpan(string from) { public static TimeSpan GetTimeSpan(string from) {
return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats, return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats,
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
} }
public static string JoinString(string[] args, int startIndex) {
return string.Join(" ", args, startIndex, args.Length - startIndex);
}
} }