Async message handling, CommandHandler rewrite and rename

This commit is contained in:
Octol1ttle 2022-08-30 20:15:01 +05:00
parent 53f13d88a5
commit ac63719a0b
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
19 changed files with 1061 additions and 711 deletions

View file

@ -133,6 +133,6 @@ public static class Boyfriend {
return guild; return guild;
} }
throw new Exception(Messages.CouldntFindGuildByChannel); throw new Exception("Could not find guild by channel!");
} }
} }

View file

@ -18,7 +18,6 @@
<PackageReference Include="Discord.Net" Version="3.8.0"/> <PackageReference Include="Discord.Net" Version="3.8.0"/>
<PackageReference Include="Humanizer.Core" Version="2.14.1"/> <PackageReference Include="Humanizer.Core" Version="2.14.1"/>
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/> <PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta1"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,124 +0,0 @@
using System.Text;
using System.Text.RegularExpressions;
using Boyfriend.Commands;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend;
public static class CommandHandler {
public static readonly Command[] Commands = {
new BanCommand(), new ClearCommand(), new HelpCommand(),
new KickCommand(), new MuteCommand(), new PingCommand(),
new SettingsCommand(), new UnbanCommand(), new UnmuteCommand()
};
private static readonly Dictionary<string, Regex> RegexCache = new();
private static readonly Regex MentionRegex = new(Regex.Escape("<@855023234407333888>"));
public static readonly StringBuilder StackedReplyMessage = new();
public static readonly StringBuilder StackedPublicFeedback = new();
public static readonly StringBuilder StackedPrivateFeedback = new();
#pragma warning disable CA2211
public static bool ConfigWriteScheduled = false; // Can't be private
#pragma warning restore CA2211
private static bool _handlerBusy;
public static async Task HandleCommand(SocketUserMessage message) {
while (_handlerBusy) await Task.Delay(200);
_handlerBusy = true;
StackedReplyMessage.Clear();
StackedPrivateFeedback.Clear();
StackedPublicFeedback.Clear();
var context = new SocketCommandContext(Boyfriend.Client, message);
var guild = context.Guild;
var config = Boyfriend.GetGuildConfig(guild.Id);
Regex regex;
if (RegexCache.ContainsKey(config["Prefix"])) { regex = RegexCache[config["Prefix"]]; } else {
regex = new Regex(Regex.Escape(config["Prefix"]));
RegexCache.Add(config["Prefix"], regex);
}
var list = message.Content.Split("\n");
var currentLine = 0;
foreach (var line in list) {
currentLine++;
await RunCommands(line, regex, context, currentLine == list.Length);
}
if (ConfigWriteScheduled) await Boyfriend.WriteGuildConfig(guild.Id);
var adminChannel = Utils.GetAdminLogChannel(guild.Id);
var systemChannel = guild.SystemChannel;
if (StackedPrivateFeedback.Length > 0 && adminChannel != null && adminChannel.Id != message.Channel.Id)
await Utils.SilentSendAsync(adminChannel, StackedPrivateFeedback.ToString());
if (StackedPublicFeedback.Length > 0 && systemChannel != null && systemChannel.Id != adminChannel?.Id
&& systemChannel.Id != message.Channel.Id)
await Utils.SilentSendAsync(systemChannel, StackedPublicFeedback.ToString());
_handlerBusy = false;
}
private static async Task RunCommands(string line, Regex regex, SocketCommandContext context, bool shouldAwait) {
foreach (var command in Commands) {
var lineNoMention = MentionRegex.Replace(line, "", 1);
if (!command.Aliases.Contains(regex.Replace(lineNoMention, "", 1).Trim().ToLower().Split()[0]))
continue;
await context.Channel.TriggerTypingAsync();
var args = line.Split().Skip(1).ToArray();
if (command.ArgsLengthRequired <= args.Length)
if (shouldAwait)
await command.Run(context, args);
else
_ = command.Run(context, args);
else
StackedReplyMessage.AppendFormat(Messages.NotEnoughArguments, command.ArgsLengthRequired.ToString(),
args.Length.ToString()).AppendLine();
if (StackedReplyMessage.Length <= 1675 && !shouldAwait) continue;
await context.Message.ReplyAsync(StackedReplyMessage.ToString(), false, null, AllowedMentions.None);
StackedReplyMessage.Clear();
}
}
public static string HasPermission(ref SocketGuildUser user, GuildPermission toCheck,
GuildPermission forBot = GuildPermission.StartEmbeddedActivities) {
var me = user.Guild.CurrentUser;
if (user.Id == user.Guild.OwnerId || (me.GuildPermissions.Has(GuildPermission.Administrator) &&
user.GuildPermissions.Has(GuildPermission.Administrator))) return "";
if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck;
if (!me.GuildPermissions.Has(forBot))
return Messages.CommandNoPermissionBot;
return !user.GuildPermissions.Has(toCheck) ? Messages.CommandNoPermissionUser : "";
}
public static string CanInteract(ref SocketGuildUser actor, ref SocketGuildUser target) {
if (actor.Guild != target.Guild)
return Messages.InteractionsDifferentGuilds;
if (actor.Id == actor.Guild.OwnerId)
return "";
if (target.Id == target.Guild.OwnerId)
return Messages.InteractionsOwner;
if (actor == target)
return Messages.InteractionsYourself;
var me = target.Guild.CurrentUser;
if (target == me)
return Messages.InteractionsMe;
if (me.Hierarchy <= target.Hierarchy)
return Messages.InteractionsFailedBot;
return actor.Hierarchy <= target.Hierarchy ? Messages.InteractionsFailedUser : "";
}
}

View file

@ -0,0 +1,271 @@
using System.Text;
using System.Text.RegularExpressions;
using Boyfriend.Commands;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend;
public class CommandProcessor {
private const string Success = ":white_check_mark: ";
private const string MissingArgument = ":keyboard: ";
private const string InvalidArgument = ":construction: ";
private const string NoAccess = ":no_entry_sign: ";
private const string CantInteract = ":vertical_traffic_light: ";
public static readonly Command[] Commands = {
new BanCommand(), new ClearCommand(), new HelpCommand(),
new KickCommand(), new MuteCommand(), new PingCommand(),
new SettingsCommand(), new UnbanCommand(), new UnmuteCommand()
};
private static readonly Dictionary<string, Regex> RegexCache = new();
private static readonly Regex MentionRegex = new(Regex.Escape("<@855023234407333888>"));
private readonly StringBuilder _stackedPrivateFeedback = new();
private readonly StringBuilder _stackedPublicFeedback = new();
private readonly StringBuilder _stackedReplyMessage = new();
private readonly List<Task> _tasks = new();
public readonly SocketCommandContext Context;
public bool ConfigWriteScheduled = false;
public CommandProcessor(SocketUserMessage message) {
Context = new SocketCommandContext(Boyfriend.Client, message);
}
public async Task HandleCommand() {
_stackedReplyMessage.Clear();
_stackedPrivateFeedback.Clear();
_stackedPublicFeedback.Clear();
var guild = Context.Guild;
var config = Boyfriend.GetGuildConfig(guild.Id);
if (GetMember().Roles.Contains(Utils.GetMuteRole(guild))) {
await Context.Message.ReplyAsync(Messages.UserCannotUnmuteThemselves);
return;
}
Regex regex;
if (RegexCache.ContainsKey(config["Prefix"])) { regex = RegexCache[config["Prefix"]]; } else {
regex = new Regex(Regex.Escape(config["Prefix"]));
RegexCache.Add(config["Prefix"], regex);
}
var list = Context.Message.Content.Split("\n");
foreach (var line in list) {
RunCommandOnLine(line, regex);
if (_stackedReplyMessage.Length > 0) _ = Context.Channel.TriggerTypingAsync();
}
await Task.WhenAll(_tasks);
if (ConfigWriteScheduled) await Boyfriend.WriteGuildConfig(guild.Id);
if (_stackedReplyMessage.Length > 0) _ = Context.Message.ReplyAsync(_stackedReplyMessage.ToString());
var adminChannel = Utils.GetAdminLogChannel(guild.Id);
var systemChannel = guild.SystemChannel;
if (_stackedPrivateFeedback.Length > 0 && adminChannel != null && adminChannel.Id != Context.Message.Channel.Id)
_ = Utils.SilentSendAsync(adminChannel, _stackedPrivateFeedback.ToString());
if (_stackedPublicFeedback.Length > 0 && systemChannel != null && systemChannel.Id != adminChannel?.Id
&& systemChannel.Id != Context.Message.Channel.Id)
_ = Utils.SilentSendAsync(systemChannel, _stackedPublicFeedback.ToString());
}
private void RunCommandOnLine(string line, Regex regex) {
foreach (var command in Commands) {
var lineNoMention = regex.Replace(MentionRegex.Replace(line, "", 1), "", 1);
if (lineNoMention == line
|| !command.Aliases.Contains(regex.Replace(lineNoMention, "", 1).Trim().ToLower().Split()[0]))
continue;
var args = line.Split().Skip(1).ToArray();
_tasks.Add(command.Run(this, args));
}
}
public void Reply(string response, string? customEmoji = null) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{customEmoji ?? Success}{response}", Context.Message);
}
public void Audit(string action, bool isPublic = true) {
var format = string.Format(Messages.FeedbackFormat, Context.User.Mention, action);
if (isPublic) Utils.SafeAppendToBuilder(_stackedPublicFeedback, format, Context.Guild.SystemChannel);
Utils.SafeAppendToBuilder(_stackedPrivateFeedback, format, Utils.GetAdminLogChannel(Context.Guild.Id));
}
public string? GetRemaining(string[] from, int startIndex, string? argument) {
if (startIndex >= from.Length && argument != null)
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{MissingArgument}{Utils.GetMessage($"Missing{argument}")}", Context.Message);
else
return string.Join(" ", from, startIndex, from.Length - startIndex);
return null;
}
public SocketUser? GetUser(string[] args, int index, string? argument) {
if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{MissingArgument}{Messages.MissingUser}",
Context.Message);
return null;
}
var user = Utils.ParseUser(args[index]);
if (user == null && argument != null)
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{InvalidArgument}{string.Format(Messages.InvalidUser, args[index])}", Context.Message);
return user;
}
public bool HasPermission(GuildPermission permission) {
if (!Context.Guild.CurrentUser.GuildPermissions.Has(permission)) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{NoAccess}{Utils.GetMessage($"BotCannot{permission}")}",
Context.Message);
return false;
}
if (Context.Guild.GetUser(Context.User.Id).GuildPermissions.Has(permission)
|| Context.Guild.Owner.Id == Context.User.Id) return true;
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{NoAccess}{Utils.GetMessage($"UserCannot{permission}")}",
Context.Message);
return false;
}
public SocketGuildUser? GetMember(SocketUser user, string? argument) {
var member = Context.Guild.GetUser(user.Id);
if (member == null && argument != null)
Utils.SafeAppendToBuilder(_stackedReplyMessage, $":x: {Messages.UserNotInGuild}", Context.Message);
return member;
}
public SocketGuildUser? GetMember(string[] args, int index, string? argument) {
if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{MissingArgument}{Messages.MissingMember}",
Context.Message);
return null;
}
var member = Context.Guild.GetUser(Utils.ParseMention(args[index]));
if (member == null && argument != null)
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{InvalidArgument}{string.Format(Messages.InvalidMember, Utils.Wrap(args[index]))}", Context.Message);
return member;
}
private SocketGuildUser GetMember() {
return Context.Guild.GetUser(Context.User.Id);
}
public ulong? GetBan(string[] args, int index) {
if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{MissingArgument}{Messages.MissingUser}",
Context.Message);
return null;
}
var id = Utils.ParseMention(args[index]);
if (Context.Guild.GetBanAsync(id) != null) return id;
Utils.SafeAppendToBuilder(_stackedReplyMessage, Messages.UserNotBanned, Context.Message);
return null;
}
public int? GetNumberRange(string[] args, int index, int min, int max, string? argument) {
if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{MissingArgument}{string.Format(Messages.MissingNumber, min.ToString(), max.ToString())}",
Context.Message);
return null;
}
if (!int.TryParse(args[index], out var i)) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{InvalidArgument}{string.Format(Utils.GetMessage($"{argument}Invalid"), min.ToString(), max.ToString(), Utils.Wrap(args[index]))}",
Context.Message);
return null;
}
if (argument == null) return i;
if (i < min) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{InvalidArgument}{string.Format(Utils.GetMessage($"{argument}TooSmall"), min.ToString())}",
Context.Message);
return null;
}
if (i <= max) return i;
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{InvalidArgument}{string.Format(Utils.GetMessage($"{argument}TooLarge"), max.ToString())}",
Context.Message);
return null;
}
public static TimeSpan GetTimeSpan(string[] args, int index) {
var infinity = TimeSpan.FromMilliseconds(-1);
if (index >= args.Length)
return infinity;
var chars = args[index].AsSpan();
var numberBuilder = Boyfriend.StringBuilder;
int days = 0, hours = 0, minutes = 0, seconds = 0;
foreach (var c in chars)
if (char.IsDigit(c)) { numberBuilder.Append(c); } else {
if (numberBuilder.Length == 0) return infinity;
switch (c) {
case 'd' or 'D' or 'д' or 'Д':
days += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 'h' or 'H' or 'ч' or 'Ч':
hours += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 'm' or 'M' or 'м' or 'М':
minutes += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 's' or 'S' or 'с' or 'С':
seconds += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
default: return infinity;
}
}
numberBuilder.Clear();
return new TimeSpan(days, hours, minutes, seconds);
}
public bool CanInteractWith(SocketGuildUser user, string action) {
if (Context.Guild.Owner.Id == Context.User.Id) return true;
if (Context.Guild.Owner.Id == user.Id) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{CantInteract}{Utils.GetMessage($"UserCannot{action}Owner")}", Context.Message);
return false;
}
if (Context.User.Id == user.Id) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{CantInteract}{Utils.GetMessage($"UserCannot{action}Themselves")}", Context.Message);
return false;
}
if (Context.Guild.CurrentUser.Id == user.Id) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{CantInteract}{Utils.GetMessage($"UserCannot{action}Bot")}", Context.Message);
return false;
}
if (Context.Guild.CurrentUser.Hierarchy <= user.Hierarchy) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{CantInteract}{Utils.GetMessage($"BotCannot{action}Target")}", Context.Message);
return false;
}
if (GetMember().Hierarchy > user.Hierarchy) return true;
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{CantInteract}{Utils.GetMessage($"UserCannot{action}Target")}", Context.Message);
return false;
}
}

View file

@ -1,73 +1,43 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class BanCommand : Command { public class BanCommand : Command {
public override string[] Aliases { get; } = { "ban", "бан" }; public override string[] Aliases { get; } = { "ban", "бан" };
public override int ArgsLengthRequired => 2;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var toBan = Utils.ParseUser(args[0]); var toBan = cmd.GetUser(args, 0, "ToBan");
if (toBan == null || !cmd.HasPermission(GuildPermission.BanMembers)) return;
if (toBan == null) { var memberToBan = cmd.GetMember(toBan, null);
Error(Messages.UserDoesntExist, false); if (memberToBan != null && !cmd.CanInteractWith(memberToBan, "Ban")) return;
return;
}
var guild = context.Guild; var duration = CommandProcessor.GetTimeSpan(args, 1);
var author = (SocketGuildUser)context.User; var reason = cmd.GetRemaining(args, duration.TotalSeconds < 1 ? 1 : 2, "BanReason");
if (reason == null) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.BanMembers); await BanUser(cmd, toBan, duration, reason);
if (permissionCheckResponse is not "") {
Error(permissionCheckResponse, true);
return;
}
var reason = Utils.JoinString(ref args, 2);
var memberToBan = Utils.ParseMember(guild, args[0]);
if (memberToBan != null) {
var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref memberToBan);
if (interactionCheckResponse is not "") {
Error(interactionCheckResponse, true);
return;
}
}
var duration = Utils.GetTimeSpan(ref args[1]) ?? TimeSpan.FromMilliseconds(-1);
if (duration.TotalSeconds < 0) {
Warn(Messages.DurationParseFailed);
reason = Utils.JoinString(ref args, 1);
if (reason is "") {
Error(Messages.ReasonRequired, false);
return;
}
}
await BanUser(guild, author, toBan, duration, reason);
} }
public static async Task BanUser(SocketGuild guild, SocketGuildUser author, SocketUser toBan, TimeSpan duration, public static async Task BanUser(CommandProcessor cmd, SocketUser toBan, TimeSpan duration, string reason) {
string reason) { var author = cmd.Context.User;
var guildBanMessage = $"({author}) {reason}"; var guild = cmd.Context.Guild;
await Utils.SendDirectMessage(toBan, await Utils.SendDirectMessage(toBan,
string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.Wrap(reason))); string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.Wrap(reason)));
var guildBanMessage = $"({author}) {reason}";
await guild.AddBanAsync(toBan, 0, guildBanMessage); await guild.AddBanAsync(toBan, 0, guildBanMessage);
var feedback = string.Format(Messages.FeedbackUserBanned, toBan.Mention, var feedback = string.Format(Messages.FeedbackUserBanned, toBan.Mention,
Utils.GetHumanizedTimeOffset(ref duration), Utils.Wrap(reason)); Utils.GetHumanizedTimeOffset(duration), Utils.Wrap(reason));
Success(feedback, author.Mention, false, false); cmd.Reply(feedback, ":hammer: ");
await Utils.SendFeedback(feedback, guild.Id, author.Mention, true); cmd.Audit(feedback);
if (duration.TotalSeconds > 0) { if (duration.TotalSeconds > 0) {
var _ = async () => { var _ = async () => {
await Task.Delay(duration); await Task.Delay(duration);
await UnbanCommand.UnbanUser(guild, guild.CurrentUser, toBan, Messages.PunishmentExpired); await UnbanCommand.UnbanUser(cmd, toBan.Id, Messages.PunishmentExpired);
}; };
} }
} }

View file

@ -1,46 +1,23 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class ClearCommand : Command { public class ClearCommand : Command {
public override string[] Aliases { get; } = { "clear", "purge", "очистить", "стереть" }; public override string[] Aliases { get; } = { "clear", "purge", "очистить", "стереть" };
public override int ArgsLengthRequired => 1;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var user = (SocketGuildUser)context.User; if (cmd.Context.Channel is not SocketTextChannel channel) throw new Exception();
if (context.Channel is not SocketTextChannel channel) throw new Exception(); if (!cmd.HasPermission(GuildPermission.ManageMessages)) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref user, GuildPermission.ManageMessages); var toDelete = cmd.GetNumberRange(args, 0, 1, 200, "ClearAmount");
if (permissionCheckResponse is not "") { if (toDelete == null) return;
Error(permissionCheckResponse, true); var messages = await channel.GetMessagesAsync((int)(toDelete + 1)).FlattenAsync();
return;
}
if (!int.TryParse(args[0], out var toDelete)) { var user = (SocketGuildUser)cmd.Context.User;
Error(Messages.ClearInvalidAmountSpecified, false); await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(user.ToString()!));
return;
}
switch (toDelete) { cmd.Audit(string.Format(Messages.FeedbackMessagesCleared, (toDelete + 1).ToString()));
case < 1:
Error(Messages.ClearNegativeAmount, false);
break;
case > 200:
Error(Messages.ClearAmountTooLarge, false);
break;
default:
var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(user.ToString()!));
await Utils.SendFeedback(
string.Format(Messages.FeedbackMessagesCleared, (toDelete + 1).ToString(), channel.Mention),
context.Guild.Id, user.Mention);
break;
}
} }
} }

View file

@ -1,31 +1,7 @@
using System.Text; namespace Boyfriend.Commands;
using Discord.Commands;
namespace Boyfriend.Commands;
public abstract class Command { public abstract class Command {
public abstract string[] Aliases { get; } public abstract string[] Aliases { get; }
public abstract int ArgsLengthRequired { get; } public abstract Task Run(CommandProcessor cmd, string[] args);
public abstract Task Run(SocketCommandContext context, string[] args);
protected static void Output(ref StringBuilder message) {
CommandHandler.StackedReplyMessage.Append(message).AppendLine();
}
protected static void Success(string message, string userMention, bool sendPublicFeedback = false,
bool sendPrivateFeedback = true) {
CommandHandler.StackedReplyMessage.Append(":white_check_mark: ").AppendLine(message);
if (sendPrivateFeedback)
Utils.StackFeedback(ref message, ref userMention, sendPublicFeedback);
}
protected static void Warn(string message) {
CommandHandler.StackedReplyMessage.Append(":warning: ").AppendLine(message);
}
protected static void Error(string message, bool accessDenied) {
var symbol = accessDenied ? ":no_entry_sign: " : ":x: ";
CommandHandler.StackedReplyMessage.Append(symbol).AppendLine(message);
}
} }

View file

@ -1,20 +1,18 @@
using Discord.Commands; using Humanizer;
using Humanizer;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class HelpCommand : Command { public class HelpCommand : Command {
public override string[] Aliases { get; } = {"help", "помощь", "справка"}; public override string[] Aliases { get; } = { "help", "помощь", "справка" };
public override int ArgsLengthRequired => 0;
public override Task Run(SocketCommandContext context, string[] args) { public override Task Run(CommandProcessor cmd, string[] args) {
var prefix = Boyfriend.GetGuildConfig(context.Guild.Id)["Prefix"]; var prefix = Boyfriend.GetGuildConfig(cmd.Context.Guild.Id)["Prefix"];
var toSend = Boyfriend.StringBuilder.Append(Messages.CommandHelp); var toSend = Boyfriend.StringBuilder.Append(Messages.CommandHelp);
foreach (var command in CommandHandler.Commands) foreach (var command in CommandProcessor.Commands)
toSend.Append( toSend.Append(
$"\n`{prefix}{command.Aliases[0]}`: {Utils.GetMessage($"CommandDescription{command.Aliases[0].Titleize()}")}"); $"\n`{prefix}{command.Aliases[0]}`: {Utils.GetMessage($"CommandDescription{command.Aliases[0].Titleize()}")}");
Output(ref toSend); cmd.Reply(toSend.ToString(), ":page_facing_up: ");
toSend.Clear(); toSend.Clear();
return Task.CompletedTask; return Task.CompletedTask;

View file

@ -1,49 +1,31 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class KickCommand : Command { public class KickCommand : Command {
public override string[] Aliases { get; } = { "kick", "кик", "выгнать" }; public override string[] Aliases { get; } = { "kick", "кик", "выгнать" };
public override int ArgsLengthRequired => 2;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var author = (SocketGuildUser)context.User; var toKick = cmd.GetMember(args, 0, "ToKick");
if (toKick == null || !cmd.HasPermission(GuildPermission.KickMembers)) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.KickMembers); if (!cmd.CanInteractWith(toKick, "Kick")) return;
if (permissionCheckResponse is not "") {
Error(permissionCheckResponse, true);
return;
}
var toKick = Utils.ParseMember(context.Guild, args[0]); await KickMember(cmd, toKick, cmd.GetRemaining(args, 1, "KickReason"));
if (toKick == null) {
Error(Messages.UserNotInGuild, false);
return;
}
var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref toKick);
if (interactionCheckResponse is not "") {
Error(interactionCheckResponse, true);
return;
}
await KickMember(context.Guild, author, toKick, Utils.JoinString(ref args, 1));
Success(
string.Format(Messages.FeedbackMemberKicked, toKick.Mention,
Utils.Wrap(Utils.JoinString(ref args, 1))), author.Mention);
} }
private static async Task KickMember(IGuild guild, SocketUser author, SocketGuildUser toKick, string reason) { private static async Task KickMember(CommandProcessor cmd, SocketGuildUser toKick, string? reason) {
var authorMention = author.Mention; if (reason == null) return;
var guildKickMessage = $"({author}) {reason}"; var guildKickMessage = $"({cmd.Context.User}) {reason}";
await Utils.SendDirectMessage(toKick, await Utils.SendDirectMessage(toKick,
string.Format(Messages.YouWereKicked, authorMention, guild.Name, Utils.Wrap(reason))); string.Format(Messages.YouWereKicked, cmd.Context.User.Mention, cmd.Context.Guild.Name,
Utils.Wrap(reason)));
await toKick.KickAsync(guildKickMessage); await toKick.KickAsync(guildKickMessage);
var format = string.Format(Messages.FeedbackMemberKicked, toKick.Mention, Utils.Wrap(reason));
cmd.Reply(format, ":police_car: ");
cmd.Audit(format);
} }
} }

View file

@ -1,5 +1,4 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.Net; using Discord.Net;
using Discord.WebSocket; using Discord.WebSocket;
@ -7,72 +6,45 @@ namespace Boyfriend.Commands;
public class MuteCommand : Command { public class MuteCommand : Command {
public override string[] Aliases { get; } = { "mute", "timeout", "заглушить", "мут" }; public override string[] Aliases { get; } = { "mute", "timeout", "заглушить", "мут" };
public override int ArgsLengthRequired => 2;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var toMute = Utils.ParseMember(context.Guild, args[0]); var toMute = cmd.GetMember(args, 0, "ToMute");
var reason = Utils.JoinString(ref args, 2); if (toMute == null) return;
var duration = Utils.GetTimeSpan(ref args[1]) ?? TimeSpan.FromMilliseconds(-1); var duration = CommandProcessor.GetTimeSpan(args, 1);
if (duration.TotalSeconds < 0) { var reason = cmd.GetRemaining(args, duration.TotalSeconds < 1 ? 1 : 2, "MuteReason");
Warn(Messages.DurationParseFailed); if (reason == null) return;
reason = Utils.JoinString(ref args, 1); var role = Utils.GetMuteRole(cmd.Context.Guild);
}
if (reason is "") {
Error(Messages.ReasonRequired, false);
return;
}
if (toMute == null) {
Error(Messages.UserNotInGuild, false);
return;
}
var guild = context.Guild;
var role = Utils.GetMuteRole(ref guild);
if (role != null) { if (role != null) {
if (toMute.Roles.Contains(role) || (toMute.TimedOutUntil != null && if (toMute.Roles.Contains(role) || (toMute.TimedOutUntil != null &&
toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() > toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() >
DateTimeOffset.Now.ToUnixTimeMilliseconds())) { DateTimeOffset.Now.ToUnixTimeMilliseconds())) {
Error(Messages.MemberAlreadyMuted, false); cmd.Reply(Messages.MemberAlreadyMuted, ":x: ");
return; return;
} }
} }
var rolesRemoved = Boyfriend.GetRemovedRoles(context.Guild.Id); var rolesRemoved = Boyfriend.GetRemovedRoles(cmd.Context.Guild.Id);
if (rolesRemoved.ContainsKey(toMute.Id)) { if (rolesRemoved.ContainsKey(toMute.Id)) {
foreach (var roleId in rolesRemoved[toMute.Id]) await toMute.AddRoleAsync(roleId); foreach (var roleId in rolesRemoved[toMute.Id]) await toMute.AddRoleAsync(roleId);
rolesRemoved.Remove(toMute.Id); rolesRemoved.Remove(toMute.Id);
CommandHandler.ConfigWriteScheduled = true; cmd.ConfigWriteScheduled = true;
Warn(Messages.RolesReturned); cmd.Reply(Messages.RolesReturned, ":warning: ");
} }
var author = (SocketGuildUser)context.User; if (!cmd.HasPermission(GuildPermission.ModerateMembers) || !cmd.CanInteractWith(toMute, "Mute")) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ModerateMembers); await MuteMember(cmd, toMute, duration, reason);
if (permissionCheckResponse is not "") {
Error(permissionCheckResponse, true);
return;
}
var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref toMute);
if (interactionCheckResponse is not "") {
Error(interactionCheckResponse, true);
return;
}
await MuteMember(guild, author, toMute, duration, reason);
} }
private static async Task MuteMember(SocketGuild guild, SocketUser author, SocketGuildUser toMute, private static async Task MuteMember(CommandProcessor cmd, SocketGuildUser toMute,
TimeSpan duration, string reason) { TimeSpan duration, string reason) {
var guild = cmd.Context.Guild;
var config = Boyfriend.GetGuildConfig(guild.Id); var config = Boyfriend.GetGuildConfig(guild.Id);
var requestOptions = Utils.GetRequestOptions($"({author}) {reason}"); var requestOptions = Utils.GetRequestOptions($"({cmd.Context.User}) {reason}");
var role = Utils.GetMuteRole(ref guild); var role = Utils.GetMuteRole(guild);
var hasDuration = duration.TotalSeconds > 0; var hasDuration = duration.TotalSeconds > 0;
if (role != null) { if (role != null) {
@ -84,30 +56,30 @@ public class MuteCommand : Command {
await toMute.RemoveRoleAsync(role); await toMute.RemoveRoleAsync(role);
rolesRemoved.Add(userRole.Id); rolesRemoved.Add(userRole.Id);
} catch (HttpException e) { } catch (HttpException e) {
Warn(string.Format(Messages.RoleRemovalFailed, $"<@&{userRole}>", Utils.Wrap(e.Reason))); cmd.Reply(string.Format(Messages.RoleRemovalFailed, $"<@&{userRole}>", Utils.Wrap(e.Reason)),
":warning: ");
} }
Boyfriend.GetRemovedRoles(guild.Id).Add(toMute.Id, rolesRemoved.AsReadOnly()); Boyfriend.GetRemovedRoles(guild.Id).Add(toMute.Id, rolesRemoved.AsReadOnly());
CommandHandler.ConfigWriteScheduled = true; cmd.ConfigWriteScheduled = true;
if (hasDuration) {
var copy = duration;
var _ = async () => {
await Task.Delay(copy);
await UnmuteCommand.UnmuteMember(guild, guild.CurrentUser, toMute, Messages.PunishmentExpired);
};
}
} }
await toMute.AddRoleAsync(role, requestOptions); await toMute.AddRoleAsync(role, requestOptions);
if (hasDuration) {
var _ = async () => {
await Task.Delay(duration);
await UnmuteCommand.UnmuteMember(cmd, toMute, Messages.PunishmentExpired);
};
}
} else { } else {
if (!hasDuration || duration.TotalDays > 28) { if (!hasDuration || duration.TotalDays > 28) {
Error(Messages.DurationRequiredForTimeOuts, false); cmd.Reply(Messages.DurationRequiredForTimeOuts, ":x: ");
return; return;
} }
if (toMute.IsBot) { if (toMute.IsBot) {
Error(Messages.CannotTimeOutBot, false); cmd.Reply(Messages.CannotTimeOutBot, ":x: ");
return; return;
} }
@ -115,9 +87,9 @@ public class MuteCommand : Command {
} }
var feedback = string.Format(Messages.FeedbackMemberMuted, toMute.Mention, var feedback = string.Format(Messages.FeedbackMemberMuted, toMute.Mention,
Utils.GetHumanizedTimeOffset(ref duration), Utils.GetHumanizedTimeOffset(duration),
Utils.Wrap(reason)); Utils.Wrap(reason));
Success(feedback, author.Mention, false, false); cmd.Reply(feedback, ":mute: ");
await Utils.SendFeedback(feedback, guild.Id, author.Mention, true); cmd.Audit(feedback);
} }
} }

View file

@ -1,17 +1,16 @@
using Discord.Commands; namespace Boyfriend.Commands;
namespace Boyfriend.Commands;
public class PingCommand : Command { public class PingCommand : Command {
public override string[] Aliases { get; } = {"ping", "latency", "pong", "пинг", "задержка", "понг"}; public override string[] Aliases { get; } = { "ping", "latency", "pong", "пинг", "задержка", "понг" };
public override int ArgsLengthRequired => 0;
public override Task Run(SocketCommandContext context, string[] args) { public override Task Run(CommandProcessor cmd, string[] args) {
var builder = Boyfriend.StringBuilder; var builder = Boyfriend.StringBuilder;
builder.Append(Utils.GetBeep()).Append(Boyfriend.Client.Latency).Append(Messages.Milliseconds); builder.Append(Utils.GetBeep())
.Append(Math.Abs(DateTimeOffset.Now.Subtract(cmd.Context.Message.Timestamp).TotalMilliseconds))
.Append(Messages.Milliseconds);
Output(ref builder); cmd.Reply(builder.ToString(), ":signal_strength: ");
builder.Clear(); builder.Clear();
return Task.CompletedTask; return Task.CompletedTask;

View file

@ -1,23 +1,14 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class SettingsCommand : Command { public class SettingsCommand : Command {
public override string[] Aliases { get; } = { "settings", "config", "настройки", "конфиг" }; public override string[] Aliases { get; } = { "settings", "config", "настройки", "конфиг" };
public override int ArgsLengthRequired => 0;
public override Task Run(SocketCommandContext context, string[] args) { public override Task Run(CommandProcessor cmd, string[] args) {
var author = (SocketGuildUser)context.User; if (!cmd.HasPermission(GuildPermission.ManageGuild)) return Task.CompletedTask;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ManageGuild); var guild = cmd.Context.Guild;
if (permissionCheckResponse is not "") {
Error(permissionCheckResponse, true);
return Task.CompletedTask;
}
var guild = context.Guild;
var config = Boyfriend.GetGuildConfig(guild.Id); var config = Boyfriend.GetGuildConfig(guild.Id);
if (args.Length == 0) { if (args.Length == 0) {
@ -48,7 +39,7 @@ public class SettingsCommand : Command {
.AppendFormat(format, currentValue).AppendLine(); .AppendFormat(format, currentValue).AppendLine();
} }
Output(ref currentSettings); cmd.Reply(currentSettings.ToString(), ":gear: ");
currentSettings.Clear(); currentSettings.Clear();
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -66,19 +57,20 @@ public class SettingsCommand : Command {
} }
if (!exists) { if (!exists) {
Error(Messages.SettingDoesntExist, false); cmd.Reply(Messages.SettingDoesntExist, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
string value; string? value;
if (args.Length >= 2) { if (args.Length >= 2) {
value = Utils.JoinString(ref args, 1); value = cmd.GetRemaining(args, 1, "Setting");
if (value == null) return Task.CompletedTask;
if (selectedSetting is "EventStartedReceivers") { if (selectedSetting is "EventStartedReceivers") {
value = value.Replace(" ", "").ToLower(); value = value.Replace(" ", "").ToLower();
if (value.StartsWith(",") || value.Count(x => x == ',') > 1 || if (value.StartsWith(",") || value.Count(x => x == ',') > 1 ||
(!value.Contains("interested") && !value.Contains("role"))) { (!value.Contains("interested") && !value.Contains("role"))) {
Error(Messages.InvalidSettingValue, false); cmd.Reply(Messages.InvalidSettingValue, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
@ -91,7 +83,7 @@ public class SettingsCommand : Command {
_ => value _ => value
}; };
if (!IsBool(value)) { if (!IsBool(value)) {
Error(Messages.InvalidSettingValue, false); cmd.Reply(Messages.InvalidSettingValue, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
@ -124,22 +116,23 @@ public class SettingsCommand : Command {
config[selectedSetting] = Boyfriend.DefaultConfig[selectedSetting]; config[selectedSetting] = Boyfriend.DefaultConfig[selectedSetting];
} else { } else {
if (value == config[selectedSetting]) { if (value == config[selectedSetting]) {
Error(string.Format(Messages.SettingsNothingChanged, localizedSelectedSetting, formattedValue), false); cmd.Reply(string.Format(Messages.SettingsNothingChanged, localizedSelectedSetting, formattedValue),
":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
if (selectedSetting is "Lang" && value is not "ru" and not "en") { if (selectedSetting is "Lang" && value is not "ru" and not "en") {
Error(Messages.LanguageNotSupported, false); cmd.Reply(Messages.LanguageNotSupported, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
if (selectedSetting.EndsWith("Channel") && guild.GetTextChannel(mention) == null) { if (selectedSetting.EndsWith("Channel") && guild.GetTextChannel(mention) == null) {
Error(Messages.InvalidChannel, false); cmd.Reply(Messages.InvalidChannel, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
if (selectedSetting.EndsWith("Role") && guild.GetRole(mention) == null) { if (selectedSetting.EndsWith("Role") && guild.GetRole(mention) == null) {
Error(Messages.InvalidRole, false); cmd.Reply(Messages.InvalidRole, ":x: ");
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -153,10 +146,11 @@ public class SettingsCommand : Command {
localizedSelectedSetting = Utils.GetMessage($"Settings{selectedSetting}"); localizedSelectedSetting = Utils.GetMessage($"Settings{selectedSetting}");
} }
CommandHandler.ConfigWriteScheduled = true; cmd.ConfigWriteScheduled = true;
Success(string.Format(Messages.FeedbackSettingsUpdated, localizedSelectedSetting, formattedValue), var replyFormat = string.Format(Messages.FeedbackSettingsUpdated, localizedSelectedSetting, formattedValue);
author.Mention); cmd.Reply(replyFormat, ":control_knobs: ");
cmd.Audit(replyFormat, false);
return Task.CompletedTask; return Task.CompletedTask;
} }

View file

@ -1,45 +1,27 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class UnbanCommand : Command { public class UnbanCommand : Command {
public override string[] Aliases { get; } = { "unban", "разбан" }; public override string[] Aliases { get; } = { "unban", "разбан" };
public override int ArgsLengthRequired => 2;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var author = (SocketGuildUser)context.User; if (!cmd.HasPermission(GuildPermission.BanMembers)) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.BanMembers); var id = cmd.GetBan(args, 0);
if (permissionCheckResponse is not "") { if (id == null) return;
Error(permissionCheckResponse, true); var reason = cmd.GetRemaining(args, 1, "UnbanReason");
return; if (reason == null) return;
}
var toUnban = Utils.ParseUser(args[0]); await UnbanUser(cmd, id.Value, reason);
if (toUnban == null) {
Error(Messages.UserDoesntExist, false);
return;
}
var reason = Utils.JoinString(ref args, 1);
await UnbanUser(context.Guild, author, toUnban, reason);
} }
public static async Task UnbanUser(SocketGuild guild, SocketGuildUser author, SocketUser toUnban, string reason) { public static async Task UnbanUser(CommandProcessor cmd, ulong id, string reason) {
if (guild.GetBanAsync(toUnban.Id) == null) { var requestOptions = Utils.GetRequestOptions($"({cmd.Context.User}) {reason}");
Error(Messages.UserNotBanned, false); await cmd.Context.Guild.RemoveBanAsync(id, requestOptions);
return;
}
var requestOptions = Utils.GetRequestOptions($"({author}) {reason}"); var feedback = string.Format(Messages.FeedbackUserUnbanned, $"<@{id.ToString()}>", Utils.Wrap(reason));
await guild.RemoveBanAsync(toUnban, requestOptions); cmd.Reply(feedback);
cmd.Audit(feedback);
var feedback = string.Format(Messages.FeedbackUserUnbanned, toUnban.Mention, Utils.Wrap(reason));
Success(feedback, author.Mention, false, false);
await Utils.SendFeedback(feedback, guild.Id, author.Mention, true);
} }
} }

View file

@ -1,59 +1,39 @@
using Discord; using Discord;
using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class UnmuteCommand : Command { public class UnmuteCommand : Command {
public override string[] Aliases { get; } = { "unmute", "размут" }; public override string[] Aliases { get; } = { "unmute", "размут" };
public override int ArgsLengthRequired => 2;
public override async Task Run(SocketCommandContext context, string[] args) { public override async Task Run(CommandProcessor cmd, string[] args) {
var author = (SocketGuildUser)context.User; if (!cmd.HasPermission(GuildPermission.ModerateMembers)) return;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ModerateMembers); var toUnmute = cmd.GetMember(args, 0, "ToUnmute");
if (permissionCheckResponse is not "") { var reason = cmd.GetRemaining(args, 1, "UnmuteReason");
Error(permissionCheckResponse, true); if (toUnmute == null || reason == null || !cmd.CanInteractWith(toUnmute, "Unmute")) return;
return; await UnmuteMember(cmd, toUnmute, reason);
}
var toUnmute = Utils.ParseMember(context.Guild, args[0]);
if (toUnmute == null) {
Error(Messages.UserDoesntExist, false);
return;
}
var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref toUnmute);
if (interactionCheckResponse is not "") {
Error(interactionCheckResponse, true);
return;
}
var reason = Utils.JoinString(ref args, 1);
await UnmuteMember(context.Guild, author, toUnmute, reason);
} }
public static async Task UnmuteMember(SocketGuild guild, SocketGuildUser author, SocketGuildUser toUnmute, public static async Task UnmuteMember(CommandProcessor cmd, SocketGuildUser toUnmute,
string reason) { string reason) {
var requestOptions = Utils.GetRequestOptions($"({author}) {reason}"); var requestOptions = Utils.GetRequestOptions($"({cmd.Context.User}) {reason}");
var role = Utils.GetMuteRole(ref guild); var role = Utils.GetMuteRole(cmd.Context.Guild);
if (role != null && toUnmute.Roles.Contains(role)) { if (role != null && toUnmute.Roles.Contains(role)) {
var rolesRemoved = Boyfriend.GetRemovedRoles(guild.Id); var rolesRemoved = Boyfriend.GetRemovedRoles(cmd.Context.Guild.Id);
if (rolesRemoved.ContainsKey(toUnmute.Id)) { if (rolesRemoved.ContainsKey(toUnmute.Id)) {
await toUnmute.AddRolesAsync(rolesRemoved[toUnmute.Id]); await toUnmute.AddRolesAsync(rolesRemoved[toUnmute.Id]);
rolesRemoved.Remove(toUnmute.Id); rolesRemoved.Remove(toUnmute.Id);
CommandHandler.ConfigWriteScheduled = true; cmd.ConfigWriteScheduled = true;
} }
await toUnmute.RemoveRoleAsync(role, requestOptions); await toUnmute.RemoveRoleAsync(role, requestOptions);
} } else {
else {
if (toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() < if (toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() <
DateTimeOffset.Now.ToUnixTimeMilliseconds()) { DateTimeOffset.Now.ToUnixTimeMilliseconds()) {
Error(Messages.MemberNotMuted, false); cmd.Reply(Messages.MemberNotMuted, ":x: ");
return; return;
} }
@ -61,7 +41,7 @@ public class UnmuteCommand : Command {
} }
var feedback = string.Format(Messages.FeedbackMemberUnmuted, toUnmute.Mention, Utils.Wrap(reason)); var feedback = string.Format(Messages.FeedbackMemberUnmuted, toUnmute.Mention, Utils.Wrap(reason));
Success(feedback, author.Mention, false, false); cmd.Reply(feedback, ":loud_sound: ");
await Utils.SendFeedback(feedback, guild.Id, author.Mention, true); cmd.Audit(feedback);
} }
} }

View file

@ -64,7 +64,7 @@ public class EventHandler {
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) && if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) &&
!user.GuildPermissions.MentionEveryone) { !user.GuildPermissions.MentionEveryone) {
await BanCommand.BanUser(guild, guild.CurrentUser, user, TimeSpan.FromMilliseconds(-1), await BanCommand.BanUser(new CommandProcessor(message), user, TimeSpan.FromMilliseconds(-1),
Messages.AutobanReason); Messages.AutobanReason);
return; return;
} }
@ -83,7 +83,7 @@ public class EventHandler {
(message.Content.Contains(prev) || message.Content.Contains(prevFailsafe)))) (message.Content.Contains(prev) || message.Content.Contains(prevFailsafe))))
return; return;
_ = CommandHandler.HandleCommand(message); _ = new CommandProcessor(message).HandleCommand();
} }
private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket, private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket,

View file

@ -95,6 +95,87 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to I cannot ban users from this guild!.
/// </summary>
internal static string BotCannotBanMembers {
get {
return ResourceManager.GetString("BotCannotBanMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot ban this user!.
/// </summary>
internal static string BotCannotBanTarget {
get {
return ResourceManager.GetString("BotCannotBanTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot kick members from this guild!.
/// </summary>
internal static string BotCannotKickMembers {
get {
return ResourceManager.GetString("BotCannotKickMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot kick this member!.
/// </summary>
internal static string BotCannotKickTarget {
get {
return ResourceManager.GetString("BotCannotKickTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot manage this guild!.
/// </summary>
internal static string BotCannotManageGuild {
get {
return ResourceManager.GetString("BotCannotManageGuild", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot manage messages in this guild!.
/// </summary>
internal static string BotCannotManageMessages {
get {
return ResourceManager.GetString("BotCannotManageMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot moderate members in this guild!.
/// </summary>
internal static string BotCannotModerateMembers {
get {
return ResourceManager.GetString("BotCannotModerateMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot mute this member!.
/// </summary>
internal static string BotCannotMuteTarget {
get {
return ResourceManager.GetString("BotCannotMuteTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot unmute this member!.
/// </summary>
internal static string BotCannotUnmuteTarget {
get {
return ResourceManager.GetString("BotCannotUnmuteTarget", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Deleted message from {0} in channel {1}: {2}. /// Looks up a localized string similar to Deleted message from {0} in channel {1}: {2}.
/// </summary> /// </summary>
@ -132,7 +213,16 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Too many messages specified!. /// Looks up a localized string similar to You need to specify an integer from {0} to {1} instead of {2}!.
/// </summary>
internal static string ClearAmountInvalid {
get {
return ResourceManager.GetString("ClearAmountInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You specified more than {0} messages!.
/// </summary> /// </summary>
internal static string ClearAmountTooLarge { internal static string ClearAmountTooLarge {
get { get {
@ -141,20 +231,11 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid message amount specified!. /// Looks up a localized string similar to You specified less than {0} messages!.
/// </summary> /// </summary>
internal static string ClearInvalidAmountSpecified { internal static string ClearAmountTooSmall {
get { get {
return ResourceManager.GetString("ClearInvalidAmountSpecified", resourceCulture); return ResourceManager.GetString("ClearAmountTooSmall", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Negative message amount specified!.
/// </summary>
internal static string ClearNegativeAmount {
get {
return ResourceManager.GetString("ClearNegativeAmount", resourceCulture);
} }
} }
@ -266,15 +347,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Couldn&apos;t find guild by message!.
/// </summary>
internal static string CouldntFindGuildByChannel {
get {
return ResourceManager.GetString("CouldntFindGuildByChannel", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Current settings:. /// Looks up a localized string similar to Current settings:.
/// </summary> /// </summary>
@ -293,15 +365,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to I couldn&apos;t parse the specified duration! One of the components could be outside it&apos;s valid range (e.g. `24h` or `60m`).
/// </summary>
internal static string DurationParseFailed {
get {
return ResourceManager.GetString("DurationParseFailed", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to I cannot mute someone for more than 28 days using timeouts! Either specify a duration shorter than 28 days, or set a mute role in settings. /// Looks up a localized string similar to I cannot mute someone for more than 28 days using timeouts! Either specify a duration shorter than 28 days, or set a mute role in settings.
/// </summary> /// </summary>
@ -428,60 +491,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Members are in different guilds!.
/// </summary>
internal static string InteractionsDifferentGuilds {
get {
return ResourceManager.GetString("InteractionsDifferentGuilds", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot interact with this member!.
/// </summary>
internal static string InteractionsFailedBot {
get {
return ResourceManager.GetString("InteractionsFailedBot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot interact with this member!.
/// </summary>
internal static string InteractionsFailedUser {
get {
return ResourceManager.GetString("InteractionsFailedUser", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot interact with me!.
/// </summary>
internal static string InteractionsMe {
get {
return ResourceManager.GetString("InteractionsMe", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot interact with guild owner!.
/// </summary>
internal static string InteractionsOwner {
get {
return ResourceManager.GetString("InteractionsOwner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot interact with yourself!.
/// </summary>
internal static string InteractionsYourself {
get {
return ResourceManager.GetString("InteractionsYourself", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to This channel does not exist!. /// Looks up a localized string similar to This channel does not exist!.
/// </summary> /// </summary>
@ -491,6 +500,15 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to You need to specify a guild member instead of {0}!.
/// </summary>
internal static string InvalidMember {
get {
return ResourceManager.GetString("InvalidMember", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to This role does not exist!. /// Looks up a localized string similar to This role does not exist!.
/// </summary> /// </summary>
@ -509,6 +527,15 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to You need to specify a user instead of {0}!.
/// </summary>
internal static string InvalidUser {
get {
return ResourceManager.GetString("InvalidUser", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Language not supported!. /// Looks up a localized string similar to Language not supported!.
/// </summary> /// </summary>
@ -536,15 +563,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0} unmuted {1} for {2}.
/// </summary>
internal static string MemberUnmuted {
get {
return ResourceManager.GetString("MemberUnmuted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to ms. /// Looks up a localized string similar to ms.
/// </summary> /// </summary>
@ -554,6 +572,87 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to You need to specify a reason to ban this user!.
/// </summary>
internal static string MissingBanReason {
get {
return ResourceManager.GetString("MissingBanReason", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a reason to kick this member!.
/// </summary>
internal static string MissingKickReason {
get {
return ResourceManager.GetString("MissingKickReason", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a guild member!.
/// </summary>
internal static string MissingMember {
get {
return ResourceManager.GetString("MissingMember", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a reason to mute this member!.
/// </summary>
internal static string MissingMuteReason {
get {
return ResourceManager.GetString("MissingMuteReason", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify an integer from {0} to {1}!.
/// </summary>
internal static string MissingNumber {
get {
return ResourceManager.GetString("MissingNumber", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a setting to change!.
/// </summary>
internal static string MissingSetting {
get {
return ResourceManager.GetString("MissingSetting", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a reason to unban this user!.
/// </summary>
internal static string MissingUnbanReason {
get {
return ResourceManager.GetString("MissingUnbanReason", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a reason for unmute this member!.
/// </summary>
internal static string MissingUnmuteReason {
get {
return ResourceManager.GetString("MissingUnmuteReason", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You need to specify a user!.
/// </summary>
internal static string MissingUser {
get {
return ResourceManager.GetString("MissingUser", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to No. /// Looks up a localized string similar to No.
/// </summary> /// </summary>
@ -563,15 +662,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Not enough arguments! Needed: {0}, provided: {1}.
/// </summary>
internal static string NotEnoughArguments {
get {
return ResourceManager.GetString("NotEnoughArguments", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Punishment expired. /// Looks up a localized string similar to Punishment expired.
/// </summary> /// </summary>
@ -590,15 +680,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to You must specify a reason!.
/// </summary>
internal static string ReasonRequired {
get {
return ResourceManager.GetString("ReasonRequired", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Not specified. /// Looks up a localized string similar to Not specified.
/// </summary> /// </summary>
@ -807,34 +888,196 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Message deleted in {0}, but I forgot what was there. /// Looks up a localized string similar to You cannot ban me!.
/// </summary> /// </summary>
internal static string UncachedMessageDeleted { internal static string UserCannotBanBot {
get { get {
return ResourceManager.GetString("UncachedMessageDeleted", resourceCulture); return ResourceManager.GetString("UserCannotBanBot", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Message edited from {0} in channel {1}, but I forgot what was there before the edit: {2}. /// Looks up a localized string similar to You cannot ban users from this guild!.
/// </summary> /// </summary>
internal static string UncachedMessageEdited { internal static string UserCannotBanMembers {
get { get {
return ResourceManager.GetString("UncachedMessageEdited", resourceCulture); return ResourceManager.GetString("UserCannotBanMembers", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to That user doesn&apos;t exist!. /// Looks up a localized string similar to You cannot ban the owner of this guild!.
/// </summary> /// </summary>
internal static string UserDoesntExist { internal static string UserCannotBanOwner {
get { get {
return ResourceManager.GetString("UserDoesntExist", resourceCulture); return ResourceManager.GetString("UserCannotBanOwner", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to User not banned!. /// Looks up a localized string similar to You cannot ban this user!.
/// </summary>
internal static string UserCannotBanTarget {
get {
return ResourceManager.GetString("UserCannotBanTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot ban yourself!.
/// </summary>
internal static string UserCannotBanThemselves {
get {
return ResourceManager.GetString("UserCannotBanThemselves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot kick me!.
/// </summary>
internal static string UserCannotKickBot {
get {
return ResourceManager.GetString("UserCannotKickBot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot kick members from this guild!.
/// </summary>
internal static string UserCannotKickMembers {
get {
return ResourceManager.GetString("UserCannotKickMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot kick the owner of this guild!.
/// </summary>
internal static string UserCannotKickOwner {
get {
return ResourceManager.GetString("UserCannotKickOwner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot kick this member!.
/// </summary>
internal static string UserCannotKickTarget {
get {
return ResourceManager.GetString("UserCannotKickTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot kick yourself!.
/// </summary>
internal static string UserCannotKickThemselves {
get {
return ResourceManager.GetString("UserCannotKickThemselves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot manage this guild!.
/// </summary>
internal static string UserCannotManageGuild {
get {
return ResourceManager.GetString("UserCannotManageGuild", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot manage messages in this guild!.
/// </summary>
internal static string UserCannotManageMessages {
get {
return ResourceManager.GetString("UserCannotManageMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot moderate members in this guild!.
/// </summary>
internal static string UserCannotModerateMembers {
get {
return ResourceManager.GetString("UserCannotModerateMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot mute me!.
/// </summary>
internal static string UserCannotMuteBot {
get {
return ResourceManager.GetString("UserCannotMuteBot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot mute the owner of this guild!.
/// </summary>
internal static string UserCannotMuteOwner {
get {
return ResourceManager.GetString("UserCannotMuteOwner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot mute this member!.
/// </summary>
internal static string UserCannotMuteTarget {
get {
return ResourceManager.GetString("UserCannotMuteTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot mute yourself!.
/// </summary>
internal static string UserCannotMuteThemselves {
get {
return ResourceManager.GetString("UserCannotMuteThemselves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ....
/// </summary>
internal static string UserCannotUnmuteBot {
get {
return ResourceManager.GetString("UserCannotUnmuteBot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t need to unmute the owner of this guild!.
/// </summary>
internal static string UserCannotUnmuteOwner {
get {
return ResourceManager.GetString("UserCannotUnmuteOwner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot unmute this user!.
/// </summary>
internal static string UserCannotUnmuteTarget {
get {
return ResourceManager.GetString("UserCannotUnmuteTarget", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are muted!.
/// </summary>
internal static string UserCannotUnmuteThemselves {
get {
return ResourceManager.GetString("UserCannotUnmuteThemselves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This user is not banned!.
/// </summary> /// </summary>
internal static string UserNotBanned { internal static string UserNotBanned {
get { get {
@ -851,15 +1094,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0} unbanned {1} for {2}.
/// </summary>
internal static string UserUnbanned {
get {
return ResourceManager.GetString("UserUnbanned", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Yes. /// Looks up a localized string similar to Yes.
/// </summary> /// </summary>

View file

@ -24,24 +24,15 @@
PublicKeyToken=b77a5c561934e089 PublicKeyToken=b77a5c561934e089
</value> </value>
</resheader> </resheader>
<data name="CouldntFindGuildByChannel" xml:space="preserve">
<value>Couldn't find guild by message!</value>
</data>
<data name="Ready" xml:space="preserve"> <data name="Ready" xml:space="preserve">
<value>{0}I'm ready! (C#)</value> <value>{0}I'm ready! (C#)</value>
</data> </data>
<data name="UncachedMessageDeleted" xml:space="preserve">
<value>Message deleted in {0}, but I forgot what was there</value>
</data>
<data name="CachedMessageDeleted" xml:space="preserve"> <data name="CachedMessageDeleted" xml:space="preserve">
<value>Deleted message from {0} in channel {1}: {2}</value> <value>Deleted message from {0} in channel {1}: {2}</value>
</data> </data>
<data name="AutobanReason" xml:space="preserve"> <data name="AutobanReason" xml:space="preserve">
<value>Too many mentions in 1 message</value> <value>Too many mentions in 1 message</value>
</data> </data>
<data name="UncachedMessageEdited" xml:space="preserve">
<value>Message edited from {0} in channel {1}, but I forgot what was there before the edit: {2}</value>
</data>
<data name="CachedMessageEdited" xml:space="preserve"> <data name="CachedMessageEdited" xml:space="preserve">
<value>Edited message in channel {0}: {1} -&gt; {2}</value> <value>Edited message in channel {0}: {1} -&gt; {2}</value>
</data> </data>
@ -63,35 +54,17 @@
<data name="CommandNoPermissionUser" xml:space="preserve"> <data name="CommandNoPermissionUser" xml:space="preserve">
<value>You do not have permission to execute this command!</value> <value>You do not have permission to execute this command!</value>
</data> </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"> <data name="YouWereBanned" xml:space="preserve">
<value>You were banned by {0} in guild {1} for {2}</value> <value>You were banned by {0} in guild {1} for {2}</value>
</data> </data>
<data name="PunishmentExpired" xml:space="preserve"> <data name="PunishmentExpired" xml:space="preserve">
<value>Punishment expired</value> <value>Punishment expired</value>
</data> </data>
<data name="ClearNegativeAmount" xml:space="preserve"> <data name="ClearAmountTooSmall" xml:space="preserve">
<value>Negative message amount specified!</value> <value>You specified less than {0} messages!</value>
</data> </data>
<data name="ClearAmountTooLarge" xml:space="preserve"> <data name="ClearAmountTooLarge" xml:space="preserve">
<value>Too many messages specified!</value> <value>You specified more than {0} messages!</value>
</data> </data>
<data name="CommandHelp" xml:space="preserve"> <data name="CommandHelp" xml:space="preserve">
<value>Command help:</value> <value>Command help:</value>
@ -148,7 +121,7 @@
<value>No</value> <value>No</value>
</data> </data>
<data name="UserNotBanned" xml:space="preserve"> <data name="UserNotBanned" xml:space="preserve">
<value>User not banned!</value> <value>This user is not banned!</value>
</data> </data>
<data name="MemberNotMuted" xml:space="preserve"> <data name="MemberNotMuted" xml:space="preserve">
<value>Member not muted!</value> <value>Member not muted!</value>
@ -156,20 +129,11 @@
<data name="RolesReturned" xml:space="preserve"> <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> <value>Someone removed the mute role manually! I added back all roles that I removed during the mute</value>
</data> </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="SettingsWelcomeMessage" xml:space="preserve"> <data name="SettingsWelcomeMessage" xml:space="preserve">
<value>Welcome message</value> <value>Welcome message</value>
</data> </data>
<data name="NotEnoughArguments" xml:space="preserve"> <data name="ClearAmountInvalid" xml:space="preserve">
<value>Not enough arguments! Needed: {0}, provided: {1}</value> <value>You need to specify an integer from {0} to {1} instead of {2}!</value>
</data>
<data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Invalid message amount specified!</value>
</data> </data>
<data name="FeedbackUserBanned" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>Banned {0} for{1}: {2}</value> <value>Banned {0} for{1}: {2}</value>
@ -192,9 +156,6 @@
<data name="InvalidChannel" xml:space="preserve"> <data name="InvalidChannel" xml:space="preserve">
<value>This channel does not exist!</value> <value>This channel does not exist!</value>
</data> </data>
<data name="DurationParseFailed" xml:space="preserve">
<value>I couldn't parse the specified duration! One of the components could be outside it's valid range (e.g. `24h` or `60m`)</value>
</data>
<data name="RoleRemovalFailed" xml:space="preserve"> <data name="RoleRemovalFailed" xml:space="preserve">
<value>I couldn't remove role {0} because of an error! {1}</value> <value>I couldn't remove role {0} because of an error! {1}</value>
</data> </data>
@ -237,9 +198,6 @@
<data name="EventCompleted" xml:space="preserve"> <data name="EventCompleted" xml:space="preserve">
<value>Event {0} has completed! Duration: {1}</value> <value>Event {0} has completed! Duration: {1}</value>
</data> </data>
<data name="UserDoesntExist" xml:space="preserve">
<value>That user doesn't exist!</value>
</data>
<data name="FeedbackFormat" xml:space="preserve"> <data name="FeedbackFormat" xml:space="preserve">
<value>*[{0}: {1}]*</value> <value>*[{0}: {1}]*</value>
</data> </data>
@ -297,7 +255,127 @@
<data name="CommandDescriptionUnmute" xml:space="preserve"> <data name="CommandDescriptionUnmute" xml:space="preserve">
<value>Unmutes a member</value> <value>Unmutes a member</value>
</data> </data>
<data name="ReasonRequired" xml:space="preserve"> <data name="MissingNumber" xml:space="preserve">
<value>You must specify a reason!</value> <value>You need to specify an integer from {0} to {1}!</value>
</data>
<data name="MissingUser" xml:space="preserve">
<value>You need to specify a user!</value>
</data>
<data name="InvalidUser" xml:space="preserve">
<value>You need to specify a user instead of {0}!</value>
</data>
<data name="MissingMember" xml:space="preserve">
<value>You need to specify a guild member!</value>
</data>
<data name="InvalidMember" xml:space="preserve">
<value>You need to specify a guild member instead of {0}!</value>
</data>
<data name="UserCannotBanMembers" xml:space="preserve">
<value>You cannot ban users from this guild!</value>
</data>
<data name="UserCannotManageMessages" xml:space="preserve">
<value>You cannot manage messages in this guild!</value>
</data>
<data name="UserCannotKickMembers" xml:space="preserve">
<value>You cannot kick members from this guild!</value>
</data>
<data name="UserCannotModerateMembers" xml:space="preserve">
<value>You cannot moderate members in this guild!</value>
</data>
<data name="UserCannotManageGuild" xml:space="preserve">
<value>You cannot manage this guild!</value>
</data>
<data name="BotCannotBanMembers" xml:space="preserve">
<value>I cannot ban users from this guild!</value>
</data>
<data name="BotCannotManageMessages" xml:space="preserve">
<value>I cannot manage messages in this guild!</value>
</data>
<data name="BotCannotKickMembers" xml:space="preserve">
<value>I cannot kick members from this guild!</value>
</data>
<data name="BotCannotModerateMembers" xml:space="preserve">
<value>I cannot moderate members in this guild!</value>
</data>
<data name="BotCannotManageGuild" xml:space="preserve">
<value>I cannot manage this guild!</value>
</data>
<data name="MissingBanReason" xml:space="preserve">
<value>You need to specify a reason to ban this user!</value>
</data>
<data name="MissingKickReason" xml:space="preserve">
<value>You need to specify a reason to kick this member!</value>
</data>
<data name="MissingMuteReason" xml:space="preserve">
<value>You need to specify a reason to mute this member!</value>
</data>
<data name="MissingUnbanReason" xml:space="preserve">
<value>You need to specify a reason to unban this user!</value>
</data>
<data name="MissingUnmuteReason" xml:space="preserve">
<value>You need to specify a reason for unmute this member!</value>
</data>
<data name="MissingSetting" xml:space="preserve">
<value>You need to specify a setting to change!</value>
</data>
<data name="UserCannotBanOwner" xml:space="preserve">
<value>You cannot ban the owner of this guild!</value>
</data>
<data name="UserCannotBanThemselves" xml:space="preserve">
<value>You cannot ban yourself!</value>
</data>
<data name="UserCannotBanBot" xml:space="preserve">
<value>You cannot ban me!</value>
</data>
<data name="BotCannotBanTarget" xml:space="preserve">
<value>I cannot ban this user!</value>
</data>
<data name="UserCannotBanTarget" xml:space="preserve">
<value>You cannot ban this user!</value>
</data>
<data name="UserCannotKickOwner" xml:space="preserve">
<value>You cannot kick the owner of this guild!</value>
</data>
<data name="UserCannotKickThemselves" xml:space="preserve">
<value>You cannot kick yourself!</value>
</data>
<data name="UserCannotKickBot" xml:space="preserve">
<value>You cannot kick me!</value>
</data>
<data name="BotCannotKickTarget" xml:space="preserve">
<value>I cannot kick this member!</value>
</data>
<data name="UserCannotKickTarget" xml:space="preserve">
<value>You cannot kick this member!</value>
</data>
<data name="UserCannotMuteOwner" xml:space="preserve">
<value>You cannot mute the owner of this guild!</value>
</data>
<data name="UserCannotMuteThemselves" xml:space="preserve">
<value>You cannot mute yourself!</value>
</data>
<data name="UserCannotMuteBot" xml:space="preserve">
<value>You cannot mute me!</value>
</data>
<data name="BotCannotMuteTarget" xml:space="preserve">
<value>I cannot mute this member!</value>
</data>
<data name="UserCannotMuteTarget" xml:space="preserve">
<value>You cannot mute this member!</value>
</data>
<data name="UserCannotUnmuteOwner" xml:space="preserve">
<value>You don't need to unmute the owner of this guild!</value>
</data>
<data name="UserCannotUnmuteThemselves" xml:space="preserve">
<value>You are muted!</value>
</data>
<data name="UserCannotUnmuteBot" xml:space="preserve">
<value>...</value>
</data>
<data name="BotCannotUnmuteTarget" xml:space="preserve">
<value>I cannot unmute this member!</value>
</data>
<data name="UserCannotUnmuteTarget" xml:space="preserve">
<value>You cannot unmute this user!</value>
</data> </data>
</root> </root>

View file

@ -15,24 +15,15 @@
PublicKeyToken=b77a5c561934e089 PublicKeyToken=b77a5c561934e089
</value> </value>
</resheader> </resheader>
<data name="CouldntFindGuildByChannel" xml:space="preserve">
<value>Не удалось найти сервер по каналу!</value>
</data>
<data name="Ready" xml:space="preserve"> <data name="Ready" xml:space="preserve">
<value>{0}Я запустился! (C#)</value> <value>{0}Я запустился! (C#)</value>
</data> </data>
<data name="UncachedMessageDeleted" xml:space="preserve">
<value>Удалено сообщение в канале {0}, но я забыл что там было</value>
</data>
<data name="CachedMessageDeleted" xml:space="preserve"> <data name="CachedMessageDeleted" xml:space="preserve">
<value>Удалено сообщение от {0} в канале {1}: {2}</value> <value>Удалено сообщение от {0} в канале {1}: {2}</value>
</data> </data>
<data name="AutobanReason" xml:space="preserve"> <data name="AutobanReason" xml:space="preserve">
<value>Слишком много упоминаний в одном сообщении</value> <value>Слишком много упоминаний в одном сообщении</value>
</data> </data>
<data name="UncachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования: {2}</value>
</data>
<data name="CachedMessageEdited" xml:space="preserve"> <data name="CachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение в канале {0}: {1} -&gt; {2}</value> <value>Отредактировано сообщение в канале {0}: {1} -&gt; {2}</value>
</data> </data>
@ -54,35 +45,17 @@
<data name="CommandNoPermissionUser" xml:space="preserve"> <data name="CommandNoPermissionUser" xml:space="preserve">
<value>У тебя недостаточно прав для выполнения этой команды!</value> <value>У тебя недостаточно прав для выполнения этой команды!</value>
</data> </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"> <data name="YouWereBanned" xml:space="preserve">
<value>Тебя забанил {0} на сервере {1} за {2}</value> <value>Тебя забанил {0} на сервере {1} за {2}</value>
</data> </data>
<data name="PunishmentExpired" xml:space="preserve"> <data name="PunishmentExpired" xml:space="preserve">
<value>Время наказания истекло</value> <value>Время наказания истекло</value>
</data> </data>
<data name="ClearNegativeAmount" xml:space="preserve"> <data name="ClearAmountTooSmall" xml:space="preserve">
<value>Указано отрицательное количество сообщений!</value> <value>Указано менее {0} сообщений!</value>
</data> </data>
<data name="ClearAmountTooLarge" xml:space="preserve"> <data name="ClearAmountTooLarge" xml:space="preserve">
<value>Указано слишком много сообщений!</value> <value>Указано более {0} сообщений!</value>
</data> </data>
<data name="CommandHelp" xml:space="preserve"> <data name="CommandHelp" xml:space="preserve">
<value>Справка по командам:</value> <value>Справка по командам:</value>
@ -136,7 +109,7 @@
<value>Нет</value> <value>Нет</value>
</data> </data>
<data name="UserNotBanned" xml:space="preserve"> <data name="UserNotBanned" xml:space="preserve">
<value>Пользователь не забанен!</value> <value>Этот пользователь не забанен!</value>
</data> </data>
<data name="MemberNotMuted" xml:space="preserve"> <data name="MemberNotMuted" xml:space="preserve">
<value>Участник не заглушен!</value> <value>Участник не заглушен!</value>
@ -144,20 +117,11 @@
<data name="RolesReturned" xml:space="preserve"> <data name="RolesReturned" xml:space="preserve">
<value>Кто-то убрал роль мута самостоятельно! Я вернул все роли, которые забрал при муте</value> <value>Кто-то убрал роль мута самостоятельно! Я вернул все роли, которые забрал при муте</value>
</data> </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="SettingsWelcomeMessage" xml:space="preserve"> <data name="SettingsWelcomeMessage" xml:space="preserve">
<value>Приветствие</value> <value>Приветствие</value>
</data> </data>
<data name="NotEnoughArguments" xml:space="preserve"> <data name="ClearAmountInvalid" xml:space="preserve">
<value>Недостаточно аргументов! Требуется: {0}, указано: {1}</value> <value>Надо указать целое число от {0} до {1} вместо {2}!</value>
</data>
<data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Указано неверное количество сообщений!</value>
</data> </data>
<data name="FeedbackUserBanned" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>Забанен {0} на{1}: {2}</value> <value>Забанен {0} на{1}: {2}</value>
@ -180,9 +144,6 @@
<data name="InvalidChannel" xml:space="preserve"> <data name="InvalidChannel" xml:space="preserve">
<value>Этот канал не существует!</value> <value>Этот канал не существует!</value>
</data> </data>
<data name="DurationParseFailed" xml:space="preserve">
<value>Мне не удалось обработать продолжительность! Один из компонентов может быть за пределами допустимого диапазона (например, `24ч` или `60м`)</value>
</data>
<data name="RoleRemovalFailed" xml:space="preserve"> <data name="RoleRemovalFailed" xml:space="preserve">
<value>Я не смог забрать роль {0} в связи с ошибкой! {1}</value> <value>Я не смог забрать роль {0} в связи с ошибкой! {1}</value>
</data> </data>
@ -228,9 +189,6 @@
<data name="EventCompleted" xml:space="preserve"> <data name="EventCompleted" xml:space="preserve">
<value>Событие {0} завершено! Продолжительность: {1}</value> <value>Событие {0} завершено! Продолжительность: {1}</value>
</data> </data>
<data name="UserDoesntExist" xml:space="preserve">
<value>Такого пользователя не существует!</value>
</data>
<data name="FeedbackFormat" xml:space="preserve"> <data name="FeedbackFormat" xml:space="preserve">
<value>*[{0}: {1}]* </value> <value>*[{0}: {1}]* </value>
</data> </data>
@ -288,7 +246,127 @@
<data name="CommandDescriptionUnmute" xml:space="preserve"> <data name="CommandDescriptionUnmute" xml:space="preserve">
<value>Разглушает участника</value> <value>Разглушает участника</value>
</data> </data>
<data name="ReasonRequired" xml:space="preserve"> <data name="MissingNumber" xml:space="preserve">
<value>Требуется указать причину!</value> <value>Надо указать целое число от {0} до {1}!</value>
</data>
<data name="MissingUser" xml:space="preserve">
<value>Надо указать пользователя!</value>
</data>
<data name="InvalidUser" xml:space="preserve">
<value>Надо указать пользователя вместо {0}!</value>
</data>
<data name="MissingMember" xml:space="preserve">
<value>Надо указать участника сервера!</value>
</data>
<data name="InvalidMember" xml:space="preserve">
<value>Надо указать участника сервера вместо {0}!</value>
</data>
<data name="UserCannotBanMembers" xml:space="preserve">
<value>Ты не можешь банить пользователей на этом сервере!</value>
</data>
<data name="UserCannotManageMessages" xml:space="preserve">
<value>Ты не можешь управлять сообщениями этого сервера!</value>
</data>
<data name="UserCannotKickMembers" xml:space="preserve">
<value>Ты не можешь выгонять участников с этого сервера!</value>
</data>
<data name="UserCannotModerateMembers" xml:space="preserve">
<value>Ты не можешь модерировать участников этого сервера!</value>
</data>
<data name="UserCannotManageGuild" xml:space="preserve">
<value>Ты не можешь настраивать этот сервер!</value>
</data>
<data name="BotCannotBanMembers" xml:space="preserve">
<value>Я не могу банить пользователей на этом сервере!</value>
</data>
<data name="BotCannotManageMessages" xml:space="preserve">
<value>Я не могу управлять сообщениями этого сервера!</value>
</data>
<data name="BotCannotKickMembers" xml:space="preserve">
<value>Я не могу выгонять участников с этого сервера!</value>
</data>
<data name="BotCannotModerateMembers" xml:space="preserve">
<value>Я не могу модерировать участников этого сервера!</value>
</data>
<data name="BotCannotManageGuild" xml:space="preserve">
<value>Я не могу настраивать этот сервер!</value>
</data>
<data name="MissingBanReason" xml:space="preserve">
<value>Надо указать причину для бана этого участника!</value>
</data>
<data name="MissingKickReason" xml:space="preserve">
<value>Надо указать причину для кика этого участника!</value>
</data>
<data name="MissingMuteReason" xml:space="preserve">
<value>Надо указать причину для мута этого участника!</value>
</data>
<data name="MissingSetting" xml:space="preserve">
<value>Надо указать настройку, которую нужно изменить!</value>
</data>
<data name="MissingUnbanReason" xml:space="preserve">
<value>Надо указать причину для разбана этого пользователя!</value>
</data>
<data name="MissingUnmuteReason" xml:space="preserve">
<value>Надо указать причину для размута этого участника!</value>
</data>
<data name="UserCannotBanBot" xml:space="preserve">
<value>Ты не можешь меня забанить!</value>
</data>
<data name="UserCannotBanOwner" xml:space="preserve">
<value>Ты не можешь забанить владельца этого сервера!</value>
</data>
<data name="UserCannotBanTarget" xml:space="preserve">
<value>Ты не можешь забанить этого участника!</value>
</data>
<data name="UserCannotBanThemselves" xml:space="preserve">
<value>Ты не можешь себя забанить!</value>
</data>
<data name="BotCannotBanTarget" xml:space="preserve">
<value>Я не могу забанить этого пользователя!</value>
</data>
<data name="UserCannotKickOwner" xml:space="preserve">
<value>Ты не можешь выгнать владельца этого сервера!</value>
</data>
<data name="UserCannotKickThemselves" xml:space="preserve">
<value>Ты не можешь себя выгнать!</value>
</data>
<data name="UserCannotKickBot" xml:space="preserve">
<value>Ты не можешь меня выгнать!</value>
</data>
<data name="BotCannotKickTarget" xml:space="preserve">
<value>Я не могу выгнать этого участника</value>
</data>
<data name="UserCannotKickTarget" xml:space="preserve">
<value>Ты не можешь выгнать этого участника!</value>
</data>
<data name="UserCannotMuteOwner" xml:space="preserve">
<value>Ты не можешь заглушить владельца этого сервера!</value>
</data>
<data name="UserCannotMuteThemselves" xml:space="preserve">
<value>Ты не можешь себя заглушить!</value>
</data>
<data name="UserCannotMuteBot" xml:space="preserve">
<value>Ты не можешь заглушить меня!</value>
</data>
<data name="BotCannotMuteTarget" xml:space="preserve">
<value>Я не могу заглушить этого пользователя!</value>
</data>
<data name="UserCannotMuteTarget" xml:space="preserve">
<value>Ты не можешь заглушить этого участника!</value>
</data>
<data name="UserCannotUnmuteOwner" xml:space="preserve">
<value>Тебе не надо возвращать из мута владельца этого сервера!</value>
</data>
<data name="UserCannotUnmuteThemselves" xml:space="preserve">
<value>Ты заглушен!</value>
</data>
<data name="UserCannotUnmuteBot" xml:space="preserve">
<value>... </value>
</data>
<data name="UserCannotUnmuteTarget" xml:space="preserve">
<value>Ты не можешь вернуть из мута этого пользователя!</value>
</data>
<data name="BotCannotUnmuteTarget" xml:space="preserve">
<value>Я не могу вернуть из мута этого пользователя!</value>
</data> </data>
</root> </root>

View file

@ -1,5 +1,6 @@
using System.Globalization; using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Discord; using Discord;
using Discord.Net; using Discord.Net;
@ -65,7 +66,7 @@ public static class Utils {
} }
} }
public static SocketRole? GetMuteRole(ref SocketGuild guild) { public static SocketRole? GetMuteRole(SocketGuild guild) {
var id = ulong.Parse(Boyfriend.GetGuildConfig(guild.Id)["MuteRole"]); var id = ulong.Parse(Boyfriend.GetGuildConfig(guild.Id)["MuteRole"]);
if (MuteRoleCache.ContainsKey(id)) return MuteRoleCache[id]; if (MuteRoleCache.ContainsKey(id)) return MuteRoleCache[id];
SocketRole? role = null; SocketRole? role = null;
@ -90,41 +91,6 @@ public static class Utils {
await channel.SendMessageAsync(text, false, null, null, allowRoles ? AllowRoles : AllowedMentions.None); await channel.SendMessageAsync(text, false, null, null, allowRoles ? AllowRoles : AllowedMentions.None);
} }
public static TimeSpan? GetTimeSpan(ref string from) {
var chars = from.AsSpan();
var numberBuilder = Boyfriend.StringBuilder;
int days = 0, hours = 0, minutes = 0, seconds = 0;
foreach (var c in chars)
if (char.IsDigit(c)) { numberBuilder.Append(c); } else {
if (numberBuilder.Length == 0) return null;
switch (c) {
case 'd' or 'D' or 'д' or 'Д':
days += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 'h' or 'H' or 'ч' or 'Ч':
hours += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 'm' or 'M' or 'м' or 'М':
minutes += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
case 's' or 'S' or 'с' or 'С':
seconds += int.Parse(numberBuilder.ToString());
numberBuilder.Clear();
break;
default: return null;
}
}
numberBuilder.Clear();
return new TimeSpan(days, hours, minutes, seconds);
}
public static string JoinString(ref string[] args, int startIndex) {
return string.Join(" ", args, startIndex, args.Length - startIndex);
}
public static RequestOptions GetRequestOptions(string reason) { public static RequestOptions GetRequestOptions(string reason) {
var options = RequestOptions.Default; var options = RequestOptions.Default;
@ -139,7 +105,12 @@ public static class Utils {
var toReturn = var toReturn =
typeof(Messages).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) typeof(Messages).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null)
?.ToString()! ?? throw new Exception($"Could not find localized property: {propertyName}"); ?.ToString();
if (toReturn == null) {
Console.WriteLine($@"Could not find localized property: {propertyName}");
return name;
}
ReflectionMessageCache.Add(name, toReturn); ReflectionMessageCache.Add(name, toReturn);
return toReturn; return toReturn;
} }
@ -154,13 +125,7 @@ public static class Utils {
await SilentSendAsync(systemChannel, toSend); await SilentSendAsync(systemChannel, toSend);
} }
public static void StackFeedback(ref string feedback, ref string mention, bool isPublic) { public static string GetHumanizedTimeOffset(TimeSpan span) {
var toAppend = string.Format(Messages.FeedbackFormat, mention, feedback);
CommandHandler.StackedPrivateFeedback.AppendLine(toAppend);
if (isPublic) CommandHandler.StackedPublicFeedback.AppendLine(toAppend);
}
public static string GetHumanizedTimeOffset(ref TimeSpan span) {
return span.TotalSeconds > 0 return span.TotalSeconds > 0
? $" {span.Humanize(2, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Month, culture: Messages.Culture)}" ? $" {span.Humanize(2, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Month, culture: Messages.Culture)}"
: Messages.Ever; : Messages.Ever;
@ -169,4 +134,23 @@ public static class Utils {
public static void SetCurrentLanguage(ulong guildId) { public static void SetCurrentLanguage(ulong guildId) {
Messages.Culture = CultureInfoCache[Boyfriend.GetGuildConfig(guildId)["Lang"]]; Messages.Culture = CultureInfoCache[Boyfriend.GetGuildConfig(guildId)["Lang"]];
} }
public static void SafeAppendToBuilder(StringBuilder appendTo, string appendWhat, SocketTextChannel? channel) {
if (channel == null) return;
if (appendTo.Length + appendWhat.Length > 2000) {
_ = SilentSendAsync(channel, appendTo.ToString());
appendTo.Clear();
}
appendTo.AppendLine(appendWhat);
}
public static void SafeAppendToBuilder(StringBuilder appendTo, string appendWhat, SocketUserMessage message) {
if (appendTo.Length + appendWhat.Length > 2000) {
_ = message.ReplyAsync(appendTo.ToString(), false, null, AllowedMentions.None);
appendTo.Clear();
}
appendTo.AppendLine(appendWhat);
}
} }