1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-01-31 09:09:00 +03:00

another two or three refactors

This commit is contained in:
l1ttleO 2022-05-14 18:12:24 +05:00
parent 868b6bcaa7
commit 790f77aa49
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
21 changed files with 1447 additions and 944 deletions

View file

@ -1,5 +1,8 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32407.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boyfriend", "Boyfriend\Boyfriend.csproj", "{21640A7A-75C2-4515-A1DF-CE8B6EEBD260}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boyfriend", "Boyfriend\Boyfriend.csproj", "{21640A7A-75C2-4515-A1DF-CE8B6EEBD260}"
EndProject EndProject
Global Global
@ -13,4 +16,10 @@ Global
{21640A7A-75C2-4515-A1DF-CE8B6EEBD260}.Release|Any CPU.ActiveCfg = Release|Any CPU {21640A7A-75C2-4515-A1DF-CE8B6EEBD260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21640A7A-75C2-4515-A1DF-CE8B6EEBD260}.Release|Any CPU.Build.0 = Release|Any CPU {21640A7A-75C2-4515-A1DF-CE8B6EEBD260}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2EDBF9CE-35F0-4810-93F7-FE0159EFD865}
EndGlobalSection
EndGlobal EndGlobal

View file

@ -1,4 +1,5 @@
using System.Globalization; using System.Collections.ObjectModel;
using System.Text;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -6,6 +7,8 @@ using Newtonsoft.Json;
namespace Boyfriend; namespace Boyfriend;
public static class Boyfriend { public static class Boyfriend {
public static readonly StringBuilder StringBuilder = new();
private static readonly Dictionary<ulong, SocketGuild> GuildCache = new();
private static readonly DiscordSocketConfig Config = new() { private static readonly DiscordSocketConfig Config = new() {
MessageCacheSize = 250, MessageCacheSize = 250,
@ -13,8 +16,34 @@ public static class Boyfriend {
}; };
public static readonly DiscordSocketClient Client = new(Config); public static readonly DiscordSocketClient Client = new(Config);
private static readonly Game Activity = new("Retrospecter - Genocide", ActivityType.Listening);
private static readonly Dictionary<ulong, GuildConfig> GuildConfigDictionary = new(); private static readonly Dictionary<ulong, Dictionary<string, string>> GuildConfigDictionary = new();
private static readonly Dictionary<ulong, Dictionary<ulong, ReadOnlyCollection<ulong>>> RemovedRolesDictionary =
new();
private static readonly Dictionary<string, string> EmptyGuildConfig = new();
private static readonly Dictionary<ulong, ReadOnlyCollection<ulong>> EmptyRemovedRoles = new();
public static readonly Dictionary<string, string> DefaultConfig = new() {
{"Lang", "en"},
{"Prefix", "!"},
{"RemoveRolesOnMute", "false"},
{"SendWelcomeMessages", "true"},
{"ReceiveStartupMessages", "false"},
{"FrowningFace", "true"},
{"WelcomeMessage", Messages.DefaultWelcomeMessage},
{"EventStartedReceivers", "interested,role"},
{"StarterRole", "0"},
{"MuteRole", "0"},
{"EventNotifyReceiverRole", "0"},
{"AdminLogChannel", "0"},
{"BotLogChannel", "0"},
{"EventCreatedChannel", "0"},
{"EventStartedChannel", "0"},
{"EventCancelledChannel", "0"},
{"EventCompletedChannel", "0"}
};
public static void Main() { public static void Main() {
Init().GetAwaiter().GetResult(); Init().GetAwaiter().GetResult();
@ -27,7 +56,7 @@ public static class Boyfriend {
await Client.LoginAsync(TokenType.Bot, token); await Client.LoginAsync(TokenType.Bot, token);
await Client.StartAsync(); await Client.StartAsync();
await Client.SetActivityAsync(new Game("Retrospecter - Expurgation", ActivityType.Listening)); await Client.SetActivityAsync(Activity);
new EventHandler().InitEvents(); new EventHandler().InitEvents();
@ -40,36 +69,64 @@ public static class Boyfriend {
return Task.CompletedTask; return Task.CompletedTask;
} }
public static async Task SetupGuildConfigs() { public static async Task WriteGuildConfig(ulong id) {
foreach (var guild in Client.Guilds) { var json = JsonConvert.SerializeObject(GuildConfigDictionary[id], Formatting.Indented);
var path = "config_" + guild.Id + ".json"; var removedRoles = JsonConvert.SerializeObject(RemovedRolesDictionary[id], Formatting.Indented);
if (!File.Exists(path)) File.Create(path);
var config = JsonConvert.DeserializeObject<GuildConfig>(await File.ReadAllTextAsync(path)); await File.WriteAllTextAsync($"config_{id}.json", json);
if (config == null) { await File.WriteAllTextAsync($"removedroles_{id}.json", removedRoles);
Messages.Culture = new CultureInfo("ru");
config = new GuildConfig(guild.Id);
}
config.Validate();
GuildConfigDictionary.Add(config.Id.GetValueOrDefault(0), config);
}
} }
public static GuildConfig GetGuildConfig(IGuild guild) { public static Dictionary<string, string> GetGuildConfig(ulong id) {
Messages.Culture = new CultureInfo("ru"); if (!RemovedRolesDictionary.ContainsKey(id))
RemovedRolesDictionary.Add(id, EmptyRemovedRoles);
var config = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id] if (GuildConfigDictionary.ContainsKey(id)) return GuildConfigDictionary[id];
: new GuildConfig(guild.Id);
config.Validate(); var path = $"config_{id}.json";
if (!File.Exists(path)) File.Create(path).Dispose();
var json = File.ReadAllText(path);
var config = JsonConvert.DeserializeObject<Dictionary<string, string>>(json) ?? EmptyGuildConfig;
foreach (var key in DefaultConfig.Keys)
if (!config.ContainsKey(key))
config.Add(key, DefaultConfig[key]);
foreach (var key in config.Keys)
if (!DefaultConfig.ContainsKey(key))
config.Remove(key);
GuildConfigDictionary.Add(id, config);
return config; return config;
} }
public static IGuild FindGuild(IMessageChannel channel) { public static Dictionary<ulong, ReadOnlyCollection<ulong>> GetRemovedRoles(ulong id) {
if (RemovedRolesDictionary.ContainsKey(id)) return RemovedRolesDictionary[id];
var path = $"removedroles_{id}.json";
if (!File.Exists(path)) File.Create(path);
var json = File.ReadAllText(path);
var removedRoles = JsonConvert.DeserializeObject<Dictionary<ulong, ReadOnlyCollection<ulong>>>(json) ??
EmptyRemovedRoles;
RemovedRolesDictionary.Add(id, removedRoles);
return removedRoles;
}
public static SocketGuild FindGuild(ulong channel) {
if (GuildCache.ContainsKey(channel)) return GuildCache[channel];
foreach (var guild in Client.Guilds) foreach (var guild in Client.Guilds)
if (guild.Channels.Any(x => x == channel)) foreach (var x in guild.Channels) {
if (x.Id != channel) continue;
GuildCache.Add(channel, guild);
return guild; return guild;
}
throw new Exception(Messages.CouldntFindGuildByChannel); throw new Exception(Messages.CouldntFindGuildByChannel);
} }

View file

@ -15,7 +15,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.3.2"/> <PackageReference Include="Discord.Net" Version="3.6.1"/>
<PackageReference Include="Humanizer.Core" Version="2.14.1"/>
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
</ItemGroup> </ItemGroup>

View file

@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using System.Text;
using System.Text.RegularExpressions;
using Boyfriend.Commands; using Boyfriend.Commands;
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
@ -7,75 +8,104 @@ using Discord.WebSocket;
namespace Boyfriend; namespace Boyfriend;
public static class CommandHandler { public static class CommandHandler {
public static readonly Command[] Commands = { public static readonly Command[] Commands = {
new BanCommand(), new ClearCommand(), new HelpCommand(), new BanCommand(), new ClearCommand(), new HelpCommand(),
new KickCommand(), new MuteCommand(), new PingCommand(), new KickCommand(), new MuteCommand(), new PingCommand(),
new SettingsCommand(), new UnbanCommand(), new UnmuteCommand() new SettingsCommand(), new UnbanCommand(), new UnmuteCommand()
}; };
public static async Task HandleCommand(SocketUserMessage message) { private static readonly Dictionary<string, Regex> RegexCache = new();
var context = new SocketCommandContext(Boyfriend.Client, message);
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; // HOW IT CAN BE PRIVATE????
#pragma warning restore CA2211
public static async Task HandleCommand(SocketUserMessage message) {
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++;
foreach (var command in Commands) { foreach (var command in Commands) {
var regex = new Regex(Regex.Escape(Boyfriend.GetGuildConfig(context.Guild).Prefix!)); if (!command.Aliases.Contains(regex.Replace(line, "", 1).ToLower().Split()[0]))
if (!command.GetAliases().Contains(regex.Replace(message.Content, "", 1).Split()[0]))
continue; continue;
var args = message.Content.Split().Skip(1).ToArray(); await context.Channel.TriggerTypingAsync();
try {
if (command.GetArgumentsAmountRequired() > args.Length) var args = line.Split().Skip(1).ToArray();
throw new ApplicationException(string.Format(Messages.NotEnoughArguments,
command.GetArgumentsAmountRequired(), args.Length)); if (command.ArgsLengthRequired <= args.Length)
await command.Run(context, args); await command.Run(context, args);
} catch (Exception e) { else
var signature = e switch { StackedReplyMessage.AppendFormat(Messages.NotEnoughArguments, command.ArgsLengthRequired.ToString(),
ApplicationException => ":x:", args.Length.ToString());
UnauthorizedAccessException => ":no_entry_sign:",
_ => ":stop_sign:"
};
var stacktrace = e.StackTrace;
var toSend = $"{signature} `{e.Message}`";
if (stacktrace != null && e is not ApplicationException && e is not UnauthorizedAccessException)
toSend += $"{Environment.NewLine}{Utils.Wrap(stacktrace)}";
await context.Channel.SendMessageAsync(toSend);
throw;
}
break; if (currentLine != list.Length) continue;
if (ConfigWriteScheduled) await Boyfriend.WriteGuildConfig(guild.Id);
await context.Message.ReplyAsync(StackedReplyMessage.ToString(), false, null, AllowedMentions.None);
var adminChannel = Utils.GetAdminLogChannel(guild.Id);
var systemChannel = guild.SystemChannel;
if (adminChannel != null)
await Utils.SilentSendAsync(adminChannel, StackedPrivateFeedback.ToString());
if (systemChannel != null)
await Utils.SilentSendAsync(systemChannel, StackedPublicFeedback.ToString());
}
} }
} }
public static async Task CheckPermissions(IGuildUser user, GuildPermission toCheck, public static string HasPermission(ref SocketGuildUser user, GuildPermission toCheck,
GuildPermission forBot = GuildPermission.StartEmbeddedActivities) { GuildPermission forBot = GuildPermission.StartEmbeddedActivities) {
var me = await user.Guild.GetCurrentUserAsync(); 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 (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck;
if (user.Id != user.Guild.OwnerId
&& (!me.GuildPermissions.Has(GuildPermission.Administrator)
|| !user.GuildPermissions.Has(GuildPermission.Administrator))) {
if (!me.GuildPermissions.Has(forBot)) if (!me.GuildPermissions.Has(forBot))
throw new UnauthorizedAccessException(Messages.CommandNoPermissionBot); return Messages.CommandNoPermissionBot;
if (!user.GuildPermissions.Has(toCheck))
throw new UnauthorizedAccessException(Messages.CommandNoPermissionUser); return !user.GuildPermissions.Has(toCheck) ? Messages.CommandNoPermissionUser : "";
}
} }
public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) { public static string CanInteract(ref SocketGuildUser actor, ref SocketGuildUser target) {
var me = await target.Guild.GetCurrentUserAsync();
if (actor.Guild != target.Guild) if (actor.Guild != target.Guild)
throw new Exception(Messages.InteractionsDifferentGuilds); return Messages.InteractionsDifferentGuilds;
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 UnauthorizedAccessException(Messages.InteractionsOwner); return Messages.InteractionsOwner;
if (actor == target) if (actor == target)
throw new UnauthorizedAccessException(Messages.InteractionsYourself); return Messages.InteractionsYourself;
var me = target.Guild.CurrentUser;
if (target == me) if (target == me)
throw new UnauthorizedAccessException(Messages.InteractionsMe); return Messages.InteractionsMe;
if (me.Hierarchy <= target.Hierarchy) if (me.Hierarchy <= target.Hierarchy)
throw new UnauthorizedAccessException(Messages.InteractionsFailedBot); return Messages.InteractionsFailedBot;
if (actor.Hierarchy <= target.Hierarchy)
throw new UnauthorizedAccessException(Messages.InteractionsFailedUser); return actor.Hierarchy <= target.Hierarchy ? Messages.InteractionsFailedUser : "";
} }
} }

View file

@ -1,73 +1,72 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class BanCommand : Command { public class BanCommand : Command {
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(SocketCommandContext context, string[] args) {
var reason = Utils.JoinString(args, 1); var toBan = Utils.ParseUser(args[0]);
TimeSpan duration; if (toBan == null) {
try { Error(Messages.UserDoesntExist, false);
duration = Utils.GetTimeSpan(args[1]); return;
reason = Utils.JoinString(args, 2);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
await Warn(context.Channel as ITextChannel, Messages.DurationParseFailed);
duration = TimeSpan.FromMilliseconds(-1);
} }
await BanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), var guild = context.Guild;
await Utils.ParseUser(args[0]), duration, reason); var author = (SocketGuildUser) context.User;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.BanMembers);
if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return;
} }
public static async Task BanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toBan, var reason = Utils.JoinString(ref args, 2);
TimeSpan duration, string reason) { var memberToBan = Utils.ParseMember(guild, args[0]);
var authorMention = author.Mention;
var guildBanMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}";
var memberToBan = await guild.GetUserAsync(toBan.Id);
var expiresIn = duration.TotalSeconds > 0 ? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine,
DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds) : "";
var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason),
expiresIn);
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers); if (memberToBan != null) {
if (memberToBan != null) var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref memberToBan);
await CommandHandler.CheckInteractions(author, memberToBan); if (interactionCheckResponse != "") {
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);
}
await BanUser(guild, author, toBan, duration, reason);
}
public static async Task BanUser(SocketGuild guild, SocketGuildUser author, SocketUser toBan, TimeSpan duration,
string reason) {
var guildBanMessage = $"({author}) {reason}";
await Utils.SendDirectMessage(toBan, await Utils.SendDirectMessage(toBan,
string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.WrapInline(reason))); string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.WrapInline(reason)));
await guild.AddBanAsync(toBan, 0, guildBanMessage); await guild.AddBanAsync(toBan, 0, guildBanMessage);
await Utils.SilentSendAsync(channel, var feedback = string.Format(Messages.FeedbackUserBanned, toBan.Mention,
string.Format(Messages.BanResponse, toBan.Mention, Utils.WrapInline(reason))); Utils.GetHumanizedTimeOffset(ref duration), Utils.WrapInline(reason));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); Success(feedback, author.Mention, false, false);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification); await Utils.SendFeedback(feedback, guild.Id, author.Mention, true);
if (duration.TotalSeconds > 0) { if (duration.TotalSeconds > 0) {
await Task.Run(async () => { async void DelayUnban() {
await Task.Delay(duration); await Task.Delay(duration);
try { await UnbanCommand.UnbanUser(guild, guild.CurrentUser, toBan, Messages.PunishmentExpired);
await UnbanCommand.UnbanUser(guild, null, await guild.GetCurrentUserAsync(), toBan,
Messages.PunishmentExpired);
} catch (ApplicationException) {}
});
}
} }
public override List<string> GetAliases() { var task = new Task(DelayUnban);
return new List<string> {"ban", "бан"}; task.Start();
} }
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Банит пользователя";
} }
} }

View file

@ -1,54 +1,46 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class ClearCommand : Command { public class ClearCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) { public override string[] Aliases { get; } = {"clear", "purge", "очистить", "стереть"};
var user = context.User; public override int ArgsLengthRequired => 1;
int toDelete; public override async Task Run(SocketCommandContext context, string[] args) {
try { var user = (SocketGuildUser) context.User;
toDelete = Convert.ToInt32(args[0]);
} catch (Exception e) when (e is FormatException or OverflowException) { if (context.Channel is not SocketTextChannel channel) throw new Exception();
throw new ApplicationException(Messages.ClearInvalidAmountSpecified);
var permissionCheckResponse = CommandHandler.HasPermission(ref user, GuildPermission.ManageMessages);
if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return;
} }
if (context.Channel is not ITextChannel channel) return; if (!int.TryParse(args[0], out var toDelete)) {
Error(Messages.ClearInvalidAmountSpecified, false);
await CommandHandler.CheckPermissions(context.Guild.GetUser(user.Id), GuildPermission.ManageMessages); return;
}
switch (toDelete) { switch (toDelete) {
case < 1: case < 1:
throw new ApplicationException(Messages.ClearNegativeAmount); Error(Messages.ClearNegativeAmount, false);
break;
case > 200: case > 200:
throw new ApplicationException(Messages.ClearAmountTooLarge); Error(Messages.ClearAmountTooLarge, false);
default: { break;
default:
var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync(); var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(Utils.GetNameAndDiscrim(user))); 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);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(context.Guild),
string.Format(Messages.MessagesDeleted, user.Mention, toDelete + 1,
Utils.MentionChannel(context.Channel.Id)));
break; 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,18 +1,32 @@
using Discord; using System.Text;
using Discord.Commands; using Discord.Commands;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public abstract class Command { public abstract class Command {
public abstract string[] Aliases { get; }
public abstract int ArgsLengthRequired { get; }
public abstract Task Run(SocketCommandContext context, string[] args); public abstract Task Run(SocketCommandContext context, string[] args);
public abstract List<string> GetAliases(); protected static void Output(ref StringBuilder message) {
CommandHandler.StackedReplyMessage.Append(message).AppendLine();
}
public abstract int GetArgumentsAmountRequired(); 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);
}
public abstract string GetSummary(); protected static void Warn(string message) {
CommandHandler.StackedReplyMessage.Append(":warning: ").AppendLine(message);
}
protected static async Task Warn(ITextChannel? channel, string warning) { protected static void Error(string message, bool accessDenied) {
await Utils.SilentSendAsync(channel, ":warning: " + warning); var symbol = accessDenied ? ":no_entry_sign: " : ":x: ";
CommandHandler.StackedReplyMessage.Append(symbol).AppendLine(message);
} }
} }

View file

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

View file

@ -1,50 +1,49 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class KickCommand : Command { public class KickCommand : Command {
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(SocketCommandContext context, string[] args) {
var author = context.Guild.GetUser(context.User.Id); var author = (SocketGuildUser) context.User;
var toKick = await Utils.ParseMember(context.Guild, args[0]);
await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers); var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.KickMembers);
await CommandHandler.CheckInteractions(author, toKick); if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
await KickMember(context.Guild, context.Channel as ITextChannel, author, toKick, Utils.JoinString(args, 1)); return;
} }
private static async Task KickMember(IGuild guild, ITextChannel? channel, IUser author, IGuildUser toKick, var toKick = Utils.ParseMember(context.Guild, args[0]);
string reason) {
var authorMention = author.Mention;
var guildKickMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}";
var notification = string.Format(Messages.MemberKicked, authorMention, toKick.Mention,
Utils.WrapInline(reason));
await Utils.SendDirectMessage(toKick, string.Format(Messages.YouWereKicked, authorMention, guild.Name, if (toKick == null) {
Utils.WrapInline(reason))); Error(Messages.UserNotInGuild, false);
return;
}
var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref toKick);
if (interactionCheckResponse != "") {
Error(interactionCheckResponse, true);
return;
}
await KickMember(context.Guild, author, toKick, Utils.JoinString(ref args, 1));
Success(
string.Format(Messages.FeedbackMemberKicked, toKick.Mention,
Utils.WrapInline(Utils.JoinString(ref args, 1))), author.Mention);
}
private static async Task KickMember(IGuild guild, SocketUser author, SocketGuildUser toKick, string reason) {
var authorMention = author.Mention;
var guildKickMessage = $"({author}) {reason}";
await Utils.SendDirectMessage(toKick,
string.Format(Messages.YouWereKicked, authorMention, guild.Name, Utils.WrapInline(reason)));
await toKick.KickAsync(guildKickMessage); await toKick.KickAsync(guildKickMessage);
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,117 +1,122 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.Net; using Discord.Net;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class MuteCommand : Command { public class MuteCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) { public override string[] Aliases { get; } = {"mute", "timeout", "заглушить", "мут"};
var author = context.Guild.GetUser(context.User.Id); public override int ArgsLengthRequired => 2;
var config = Boyfriend.GetGuildConfig(context.Guild);
var reason = Utils.JoinString(args, 1);
var role = Utils.GetMuteRole(context.Guild);
var rolesRemoved = config.RolesRemovedOnMute!;
var toMute = await Utils.ParseMember(context.Guild, args[0]);
TimeSpan duration; public override async Task Run(SocketCommandContext context, string[] args) {
try { var toMute = Utils.ParseMember(context.Guild, args[0]);
duration = Utils.GetTimeSpan(args[1]); var reason = Utils.JoinString(ref args, 2);
reason = Utils.JoinString(args, 2);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) { var duration = Utils.GetTimeSpan(ref args[1]) ?? TimeSpan.FromMilliseconds(-1);
await Warn(context.Channel as ITextChannel, Messages.DurationParseFailed); if (duration.TotalSeconds < 0) {
duration = TimeSpan.FromMilliseconds(-1); Warn(Messages.DurationParseFailed);
reason = Utils.JoinString(ref args, 1);
} }
if (toMute == null) if (toMute == null) {
throw new ApplicationException(Messages.UserNotInGuild); Error(Messages.UserNotInGuild, false);
return;
}
if (role != null && toMute.RoleIds.Any(x => x == role.Id) || toMute.TimedOutUntil != null && var guild = context.Guild;
toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() > DateTimeOffset.Now.ToUnixTimeMilliseconds()) var role = Utils.GetMuteRole(ref guild);
throw new ApplicationException(Messages.MemberAlreadyMuted);
if (role != null) {
var hasMuteRole = false;
foreach (var x in toMute.Roles) {
if (x != role) continue;
hasMuteRole = true;
break;
}
if (hasMuteRole || (toMute.TimedOutUntil != null && toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() >
DateTimeOffset.Now.ToUnixTimeMilliseconds())) {
Error(Messages.MemberAlreadyMuted, false);
return;
}
}
var rolesRemoved = Boyfriend.GetRemovedRoles(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);
await config.Save(); CommandHandler.ConfigWriteScheduled = true;
throw new ApplicationException(Messages.RolesReturned); Warn(Messages.RolesReturned);
} }
await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles); var author = (SocketGuildUser) context.User;
await CommandHandler.CheckInteractions(author, toMute);
await MuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toMute, var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ModerateMembers);
duration, reason); if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return;
} }
private static async Task MuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toMute, var interactionCheckResponse = CommandHandler.CanInteract(ref author, ref toMute);
if (interactionCheckResponse != "") {
Error(interactionCheckResponse, true);
return;
}
await MuteMember(guild, author, toMute, duration, reason);
Success(
string.Format(Messages.FeedbackMemberMuted, toMute.Mention, Utils.GetHumanizedTimeOffset(ref duration),
Utils.WrapInline(reason)), author.Mention, true);
}
private static async Task MuteMember(SocketGuild guild, SocketUser author, SocketGuildUser toMute,
TimeSpan duration, string reason) { TimeSpan duration, string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles); var config = Boyfriend.GetGuildConfig(guild.Id);
var authorMention = author.Mention; var requestOptions = Utils.GetRequestOptions($"({author}) {reason}");
var config = Boyfriend.GetGuildConfig(guild); var role = Utils.GetMuteRole(ref guild);
var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}");
var role = Utils.GetMuteRole(guild);
var hasDuration = duration.TotalSeconds > 0; var hasDuration = duration.TotalSeconds > 0;
var expiresIn = hasDuration ? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine,
DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds) : "";
var notification = string.Format(Messages.MemberMuted, authorMention, toMute.Mention, Utils.WrapInline(reason),
expiresIn);
if (role != null) { if (role != null) {
if (config.RemoveRolesOnMute.GetValueOrDefault(false)) { if (config["RemoveRolesOnMute"] == "true") {
var rolesRemoved = new List<ulong>(); var rolesRemoved = new List<ulong>();
foreach (var roleId in toMute.RoleIds) { foreach (var userRole in toMute.Roles)
try { try {
if (roleId == guild.Id) continue; if (userRole == guild.EveryoneRole || userRole == role) continue;
if (roleId == role.Id) continue; await toMute.RemoveRoleAsync(role);
await toMute.RemoveRoleAsync(roleId); rolesRemoved.Add(userRole.Id);
rolesRemoved.Add(roleId);
} catch (HttpException e) { } catch (HttpException e) {
await Warn(channel, Warn(string.Format(Messages.RoleRemovalFailed, $"<@&{userRole}>", Utils.WrapInline(e.Reason)));
string.Format(Messages.RoleRemovalFailed, $"<@&{roleId}>", Utils.WrapInline(e.Reason)));
}
} }
config.RolesRemovedOnMute!.Add(toMute.Id, rolesRemoved); Boyfriend.GetRemovedRoles(guild.Id).Add(toMute.Id, rolesRemoved.AsReadOnly());
await config.Save(); CommandHandler.ConfigWriteScheduled = true;
if (hasDuration) if (hasDuration) {
await Task.Run(async () => { async void DelayUnmute() {
await Task.Delay(duration); await Task.Delay(duration);
try { await UnmuteCommand.UnmuteMember(guild, guild.CurrentUser, toMute, Messages.PunishmentExpired);
await UnmuteCommand.UnmuteMember(guild, null, await guild.GetCurrentUserAsync(), toMute, }
Messages.PunishmentExpired);
} catch (ApplicationException) {} var task = new Task(DelayUnmute);
}); task.Start();
}
} }
await toMute.AddRoleAsync(role, requestOptions); await toMute.AddRoleAsync(role, requestOptions);
} else { } else {
if (!hasDuration) if (!hasDuration) {
throw new ApplicationException(Messages.DurationRequiredForTimeOuts); Error(Messages.DurationRequiredForTimeOuts, false);
if (toMute.IsBot) return;
throw new ApplicationException(Messages.CannotTimeOutBot); }
if (toMute.IsBot) {
Error(Messages.CannotTimeOutBot, false);
return;
}
await toMute.SetTimeOutAsync(duration, requestOptions); await toMute.SetTimeOutAsync(duration, requestOptions);
} }
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);
}
public override List<string> GetAliases() {
return new List<string> {"mute", "мут", "мьют"};
}
public override int GetArgumentsAmountRequired() {
return 2;
}
public override string GetSummary() {
return "Глушит участника";
} }
} }

View file

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

View file

@ -1,111 +1,156 @@
using System.Reflection; using Discord;
using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class SettingsCommand : Command { public class SettingsCommand : Command {
public override async Task Run(SocketCommandContext context, string[] args) { public override string[] Aliases { get; } = {"settings", "config", "настройки", "конфиг"};
var config = Boyfriend.GetGuildConfig(context.Guild); public override int ArgsLengthRequired => 0;
var guild = context.Guild;
await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageGuild); public override Task Run(SocketCommandContext context, string[] args) {
var author = (SocketGuildUser) context.User;
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ManageGuild);
if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return Task.CompletedTask;
}
var guild = context.Guild;
var config = Boyfriend.GetGuildConfig(guild.Id);
if (args.Length == 0) { if (args.Length == 0) {
var nl = Environment.NewLine; var currentSettings = Boyfriend.StringBuilder.AppendLine(Messages.CurrentSettings);
dynamic forCheck;
var adminLogChannel = (forCheck = guild.GetTextChannel(config.AdminLogChannel.GetValueOrDefault(0))) == null foreach (var setting in Boyfriend.DefaultConfig) {
? Messages.ChannelNotSpecified : forCheck.Mention; var format = "{0}";
var botLogChannel = (forCheck = guild.GetTextChannel(config.BotLogChannel.GetValueOrDefault(0))) == null var currentValue = config[setting.Key];
? Messages.ChannelNotSpecified : forCheck.Mention;
var muteRole = (forCheck = guild.GetRole(config.MuteRole.GetValueOrDefault(0))) == null if (setting.Key.EndsWith("Channel")) {
? Messages.RoleNotSpecified : forCheck.Mention; if (guild.GetTextChannel(Convert.ToUInt64(currentValue)) != null)
var defaultRole = (forCheck = guild.GetRole(config.DefaultRole.GetValueOrDefault(0))) == null format = "<#{0}>";
? Messages.RoleNotSpecified : forCheck.Mention; else
var toSend = string.Format(Messages.CurrentSettings, nl) + currentValue = Messages.ChannelNotSpecified;
string.Format(Messages.CurrentSettingsLang, config.Lang, nl) + } else if (setting.Key.EndsWith("Role")) {
string.Format(Messages.CurrentSettingsPrefix, config.Prefix, nl) + if (guild.GetRole(Convert.ToUInt64(currentValue)) != null)
string.Format(Messages.CurrentSettingsRemoveRoles, format = "<@&{0}>";
YesOrNo(config.RemoveRolesOnMute.GetValueOrDefault(false)), nl) + else
string.Format(Messages.CurrentSettingsUseSystemChannel, currentValue = Messages.RoleNotSpecified;
YesOrNo(config.UseSystemChannel.GetValueOrDefault(true)), nl) + } else {
string.Format(Messages.CurrentSettingsSendWelcomeMessages, if (IsBool(currentValue))
YesOrNo(config.SendWelcomeMessages.GetValueOrDefault(true)), nl) + currentValue = YesOrNo(currentValue == "true");
string.Format(Messages.CurrentSettingsReceiveStartupMessages, else
YesOrNo(config.ReceiveStartupMessages.GetValueOrDefault(true)), nl) + format = Utils.WrapInline("{0}")!;
string.Format(Messages.CurrentSettingsWelcomeMessage, config.WelcomeMessage, nl) +
string.Format(Messages.CurrentSettingsDefaultRole, defaultRole, nl) +
string.Format(Messages.CurrentSettingsMuteRole, muteRole, nl) +
string.Format(Messages.CurrentSettingsAdminLogChannel, adminLogChannel, nl) +
string.Format(Messages.CurrentSettingsBotLogChannel, botLogChannel);
await Utils.SilentSendAsync(context.Channel as ITextChannel ?? throw new ApplicationException(), toSend);
return;
} }
var setting = args[0].ToLower(); currentSettings.Append($"{Utils.GetMessage($"Settings{setting.Key}")} (`{setting.Key}`): ")
var value = ""; .AppendFormat(format, currentValue).AppendLine();
if (args.Length >= 2)
try {
value = args[1].ToLower();
} catch (IndexOutOfRangeException) {
throw new ApplicationException(Messages.InvalidSettingValue);
} }
PropertyInfo? property = null; Output(ref currentSettings);
foreach (var prop in typeof(GuildConfig).GetProperties()) currentSettings.Clear();
if (setting == prop.Name.ToLower()) return Task.CompletedTask;
property = prop; }
if (property == null || !property.CanWrite)
throw new ApplicationException(Messages.SettingDoesntExist); var selectedSetting = args[0].ToLower();
var type = property.PropertyType;
var exists = false;
foreach (var setting in Boyfriend.DefaultConfig) {
if (selectedSetting != setting.Key.ToLower()) continue;
selectedSetting = setting.Key;
exists = true;
break;
}
if (!exists) {
Error(Messages.SettingDoesntExist, false);
return Task.CompletedTask;
}
string value;
if (args.Length >= 2) {
value = Utils.JoinString(ref args, 1);
if (selectedSetting != "WelcomeMessage")
value = value.Replace(" ", "").ToLower();
if (value.StartsWith(",") || value.Count(x => x == ',') > 1) {
Error(Messages.InvalidSettingValue, false);
return Task.CompletedTask;
}
} else {
value = "reset";
}
if (IsBool(Boyfriend.DefaultConfig[selectedSetting]) && !IsBool(value)) {
value = value switch {
"y" or "yes" => "true",
"n" or "no" => "false",
_ => value
};
if (!IsBool(value)) {
Error(Messages.InvalidSettingValue, false);
return Task.CompletedTask;
}
}
var localizedSelectedSetting = Utils.GetMessage($"Settings{selectedSetting}");
var mention = Utils.ParseMention(value);
if (mention != 0) value = mention.ToString();
var formatting = Utils.WrapInline("{0}")!;
if (selectedSetting.EndsWith("Channel"))
formatting = "<#{0}>";
if (selectedSetting.EndsWith("Role"))
formatting = "<@&{0}>";
if (value is "0" or "reset" or "default")
formatting = Messages.SettingNotDefined;
var formattedValue = IsBool(value) ? YesOrNo(value == "true") : string.Format(formatting, value);
if (value is "reset" or "default") { if (value is "reset" or "default") {
property.SetValue(config, null); config[selectedSetting] = Boyfriend.DefaultConfig[selectedSetting];
} else if (type == typeof(string)) {
if (setting == "lang" && value is not ("ru" or "en"))
throw new ApplicationException(Messages.LanguageNotSupported);
property.SetValue(config, value);
} else { } else {
try { if (value == config[selectedSetting]) {
if (type == typeof(bool?)) Error(string.Format(Messages.SettingsNothingChanged, localizedSelectedSetting, formattedValue), false);
property.SetValue(config, Convert.ToBoolean(value)); return Task.CompletedTask;
if (type == typeof(ulong?)) {
var id = Convert.ToUInt64(value);
if (property.Name.EndsWith("Channel") && guild.GetTextChannel(id) == null)
throw new ApplicationException(Messages.InvalidChannel);
if (property.Name.EndsWith("Role") && guild.GetRole(id) == null)
throw new ApplicationException(Messages.InvalidRole);
property.SetValue(config, id);
} }
} catch (Exception e) when (e is FormatException or OverflowException) {
throw new ApplicationException(Messages.InvalidSettingValue);
}
}
config.Validate();
await config.Save(); if (selectedSetting == "Lang" && value is not "ru" and not "en") {
await context.Channel.SendMessageAsync(Messages.SettingsUpdated); Error(Messages.LanguageNotSupported, false);
return Task.CompletedTask;
}
if (selectedSetting.EndsWith("Channel") && guild.GetTextChannel(mention) == null) {
Error(Messages.InvalidChannel, false);
return Task.CompletedTask;
}
if (selectedSetting.EndsWith("Role") && guild.GetRole(mention) == null) {
Error(Messages.InvalidRole, false);
return Task.CompletedTask;
}
config[selectedSetting] = value;
}
if (selectedSetting == "Lang") {
Utils.SetCurrentLanguage(guild.Id);
localizedSelectedSetting = Utils.GetMessage($"Settings{selectedSetting}");
}
CommandHandler.ConfigWriteScheduled = true;
Success(string.Format(Messages.FeedbackSettingsUpdated, localizedSelectedSetting, formattedValue),
author.Mention);
return Task.CompletedTask;
} }
private static string YesOrNo(bool isYes) { private static string YesOrNo(bool isYes) {
return isYes ? Messages.Yes : Messages.No; return isYes ? Messages.Yes : Messages.No;
} }
public override List<string> GetAliases() { private static bool IsBool(string value) {
return new List<string> {"settings", "настройки", "config", "конфиг "}; return value is "true" or "false";
}
public override int GetArgumentsAmountRequired() {
return 0;
}
public override string GetSummary() {
return "Настраивает бота отдельно для этого сервера";
} }
} }

View file

@ -1,48 +1,45 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class UnbanCommand : Command { public class UnbanCommand : Command {
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(SocketCommandContext context, string[] args) {
await UnbanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), var author = (SocketGuildUser) context.User;
await Utils.ParseUser(args[0]), Utils.JoinString(args, 1));
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.BanMembers);
if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return;
} }
public static async Task UnbanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toUnban, var toUnban = Utils.ParseUser(args[0]);
string reason) {
var authorMention = author.Mention; if (toUnban == null) {
var notification = string.Format(Messages.UserUnbanned, authorMention, toUnban.Mention, Error(Messages.UserDoesntExist, false);
Utils.WrapInline(reason)); return;
var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}"); }
await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers); var reason = Utils.JoinString(ref args, 1);
if (guild.GetBanAsync(toUnban.Id) == null) await UnbanUser(context.Guild, author, toUnban, reason);
throw new ApplicationException(Messages.UserNotBanned); }
public static async Task UnbanUser(SocketGuild guild, SocketGuildUser author, SocketUser toUnban, string reason) {
if (guild.GetBanAsync(toUnban.Id) == null) {
Error(Messages.UserNotBanned, false);
return;
}
var requestOptions = Utils.GetRequestOptions($"({author}) {reason}");
await guild.RemoveBanAsync(toUnban, requestOptions); await guild.RemoveBanAsync(toUnban, requestOptions);
await Utils.SilentSendAsync(channel, string.Format(Messages.UnbanResponse, toUnban.Mention, var feedback = string.Format(Messages.FeedbackUserUnbanned, toUnban.Mention, Utils.WrapInline(reason));
Utils.WrapInline(reason))); Success(feedback, author.Mention, false, false);
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); await Utils.SendFeedback(feedback, guild.Id, author.Mention, true);
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,71 +1,78 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
// ReSharper disable UnusedType.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ClassNeverInstantiated.Global
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
public class UnmuteCommand : Command { public class UnmuteCommand : Command {
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(SocketCommandContext context, string[] args) {
await UnmuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), var author = (SocketGuildUser) context.User;
await Utils.ParseMember(context.Guild, args[0]), Utils.JoinString(args, 1));
var permissionCheckResponse = CommandHandler.HasPermission(ref author, GuildPermission.ModerateMembers);
if (permissionCheckResponse != "") {
Error(permissionCheckResponse, true);
return;
} }
public static async Task UnmuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toUnmute, 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 != "") {
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,
string reason) { string reason) {
await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles); var requestOptions = Utils.GetRequestOptions($"({author}) {reason}");
await CommandHandler.CheckInteractions(author, toUnmute); var role = Utils.GetMuteRole(ref guild);
var authorMention = author.Mention;
var config = Boyfriend.GetGuildConfig(guild);
var notification = string.Format(Messages.MemberUnmuted, authorMention, toUnmute.Mention,
Utils.WrapInline(reason));
var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}");
var role = Utils.GetMuteRole(guild);
if (role != null) { if (role != null) {
if (toUnmute.RoleIds.All(x => x != role.Id)) { var muted = false;
var rolesRemoved = config.RolesRemovedOnMute; foreach (var x in toUnmute.Roles) {
if (x != role) continue;
await toUnmute.AddRolesAsync(rolesRemoved![toUnmute.Id]); muted = true;
rolesRemoved.Remove(toUnmute.Id); break;
await config.Save();
throw new ApplicationException(Messages.RolesReturned);
} }
if (toUnmute.RoleIds.All(x => x != role.Id)) var rolesRemoved = Boyfriend.GetRemovedRoles(guild.Id);
throw new ApplicationException(Messages.MemberNotMuted);
if (rolesRemoved.ContainsKey(toUnmute.Id)) {
await toUnmute.AddRolesAsync(rolesRemoved[toUnmute.Id]);
rolesRemoved.Remove(toUnmute.Id);
CommandHandler.ConfigWriteScheduled = true;
}
if (muted) {
await toUnmute.RemoveRoleAsync(role, requestOptions); await toUnmute.RemoveRoleAsync(role, requestOptions);
if (config.RolesRemovedOnMute!.ContainsKey(toUnmute.Id)) { } else {
await toUnmute.AddRolesAsync(config.RolesRemovedOnMute[toUnmute.Id]); Error(Messages.MemberNotMuted, false);
config.RolesRemovedOnMute.Remove(toUnmute.Id); return;
await config.Save();
} }
} else { } else {
if (toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() if (toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() <
< DateTimeOffset.Now.ToUnixTimeMilliseconds()) DateTimeOffset.Now.ToUnixTimeMilliseconds()) {
throw new ApplicationException(Messages.MemberNotMuted); Error(Messages.MemberNotMuted, false);
return;
}
await toUnmute.RemoveTimeOutAsync(); await toUnmute.RemoveTimeOutAsync();
} }
await Utils.SilentSendAsync(channel, string.Format(Messages.UnmuteResponse, toUnmute.Mention, var feedback = string.Format(Messages.FeedbackMemberUnmuted, toUnmute.Mention, Utils.WrapInline(reason));
Utils.WrapInline(reason))); Success(feedback, author.Mention, false, false);
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); await Utils.SendFeedback(feedback, guild.Id, author.Mention, true);
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,5 +1,4 @@
using System.Globalization; using Boyfriend.Commands;
using Boyfriend.Commands;
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
@ -15,61 +14,75 @@ public class EventHandler {
_client.MessageReceived += MessageReceivedEvent; _client.MessageReceived += MessageReceivedEvent;
_client.MessageUpdated += MessageUpdatedEvent; _client.MessageUpdated += MessageUpdatedEvent;
_client.UserJoined += UserJoinedEvent; _client.UserJoined += UserJoinedEvent;
_client.GuildScheduledEventCreated += ScheduledEventCreatedEvent;
_client.GuildScheduledEventCancelled += ScheduledEventCancelledEvent;
_client.GuildScheduledEventStarted += ScheduledEventStartedEvent;
_client.GuildScheduledEventCompleted += ScheduledEventCompletedEvent;
} }
private static async Task ReadyEvent() { private static async Task ReadyEvent() {
await Boyfriend.SetupGuildConfigs(); var i = Utils.Random.Next(3);
var i = new Random().Next(3);
foreach (var guild in Boyfriend.Client.Guilds) { foreach (var guild in Boyfriend.Client.Guilds) {
var config = Boyfriend.GetGuildConfig(guild); var config = Boyfriend.GetGuildConfig(guild.Id);
var channel = guild.GetTextChannel(config.BotLogChannel.GetValueOrDefault(0)); var channel = guild.GetTextChannel(Convert.ToUInt64(config["BotLogChannel"]));
Messages.Culture = new CultureInfo(config.Lang!); Utils.SetCurrentLanguage(guild.Id);
if (!config.ReceiveStartupMessages.GetValueOrDefault(true) || channel == null) continue; if (config["ReceiveStartupMessages"] != "true" || channel == null) continue;
await channel.SendMessageAsync(string.Format(Messages.Ready, Utils.GetBeep(config.Lang!, i))); await channel.SendMessageAsync(string.Format(Messages.Ready, Utils.GetBeep(i)));
} }
} }
private static async Task MessageDeletedEvent(Cacheable<IMessage, ulong> message, private static async Task MessageDeletedEvent(Cacheable<IMessage, ulong> message,
Cacheable<IMessageChannel, ulong> channel) { Cacheable<IMessageChannel, ulong> channel) {
var msg = message.Value; var msg = message.Value;
if (msg is null or ISystemMessage || msg.Author.IsBot) return;
var toSend = msg == null ? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id)) var guild = Boyfriend.FindGuild(channel.Value.Id);
: string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) +
$"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}"; Utils.SetCurrentLanguage(guild.Id);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel.Value)), toSend);
var auditLogEntry = (await guild.GetAuditLogsAsync(1).FlattenAsync()).First();
var mention = auditLogEntry.User.Mention;
if (auditLogEntry.Action != ActionType.MessageDeleted ||
DateTimeOffset.Now.Subtract(auditLogEntry.CreatedAt).TotalMilliseconds > 500 ||
auditLogEntry.User.IsBot) mention = msg.Author.Mention;
await Utils.SendFeedback(
string.Format(Messages.CachedMessageDeleted, msg.Author.Mention, Utils.MentionChannel(channel.Id),
Utils.WrapAsNeeded(msg.CleanContent)), guild.Id, mention);
} }
private static async Task MessageReceivedEvent(SocketMessage messageParam) { private static async Task MessageReceivedEvent(SocketMessage messageParam) {
if (messageParam is not SocketUserMessage message) return; if (messageParam is not SocketUserMessage message) return;
var argPos = 0; var user = (SocketGuildUser) message.Author;
var user = (IGuildUser) message.Author;
var guild = user.Guild; var guild = user.Guild;
var guildConfig = Boyfriend.GetGuildConfig(guild); var guildConfig = Boyfriend.GetGuildConfig(guild.Id);
Utils.SetCurrentLanguage(guild.Id);
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) &&
!user.GuildPermissions.MentionEveryone) {
await BanCommand.BanUser(guild, guild.CurrentUser, user, TimeSpan.FromMilliseconds(-1),
Messages.AutobanReason);
return;
}
var argPos = 0;
var prev = ""; var prev = "";
var prevFailsafe = ""; var prevFailsafe = "";
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();
Messages.Culture = new CultureInfo(guildConfig.Lang!); if (prevsArray.Length >= 3) {
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) &&
!user.GuildPermissions.MentionEveryone)
await BanCommand.BanUser(guild, null, await guild.GetCurrentUserAsync(), user,
TimeSpan.FromMilliseconds(-1), Messages.AutobanReason);
try {
prev = prevsArray[1].Content; prev = prevsArray[1].Content;
prevFailsafe = prevsArray[2].Content; prevFailsafe = prevsArray[2].Content;
} catch (IndexOutOfRangeException) {} }
if (!(message.HasStringPrefix(guildConfig.Prefix, ref argPos) || if (!(message.HasStringPrefix(guildConfig["Prefix"], ref argPos) ||
message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos)) || message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos)) || user == guild.CurrentUser ||
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); await CommandHandler.HandleCommand(message);
@ -78,27 +91,89 @@ public class EventHandler {
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;
if (msg != null && msg.Content == messageSocket.Content) return; if (msg is null or ISystemMessage || msg.CleanContent == messageSocket.CleanContent || msg.Author.IsBot) return;
var toSend = msg == null var guildId = Boyfriend.FindGuild(channel.Id).Id;
? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention,
Utils.MentionChannel(channel.Id)) + Utils.Wrap(messageSocket.Content) : string.Format( Utils.SetCurrentLanguage(guildId);
Messages.CachedMessageEdited, msg.Author.Mention, Utils.MentionChannel(channel.Id), nl, nl,
Utils.Wrap(msg.Content), nl, nl, Utils.Wrap(messageSocket.Content)); await Utils.SendFeedback(
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel)), toSend); string.Format(Messages.CachedMessageEdited, Utils.MentionChannel(channel.Id),
Utils.WrapAsNeeded(msg.CleanContent), Utils.WrapAsNeeded(messageSocket.Content)), guildId,
msg.Author.Mention);
} }
private static async Task UserJoinedEvent(SocketGuildUser user) { private static async Task UserJoinedEvent(SocketGuildUser user) {
var guild = user.Guild; var guild = user.Guild;
var config = Boyfriend.GetGuildConfig(guild); var config = Boyfriend.GetGuildConfig(guild.Id);
if (config.SendWelcomeMessages.GetValueOrDefault(true)) if (config["SendWelcomeMessages"] == "true")
await Utils.SilentSendAsync(guild.SystemChannel, await Utils.SilentSendAsync(guild.SystemChannel,
string.Format(config.WelcomeMessage!, user.Mention, guild.Name)); string.Format(config["WelcomeMessage"], user.Mention, guild.Name));
if (config.DefaultRole != 0) if (config["StarterRole"] != "0")
await user.AddRoleAsync(Utils.ParseRole(guild, config.DefaultRole.ToString()!)); await user.AddRoleAsync(ulong.Parse(config["StarterRole"]));
}
private static async Task ScheduledEventCreatedEvent(SocketGuildEvent scheduledEvent) {
var guild = scheduledEvent.Guild;
var eventConfig = Boyfriend.GetGuildConfig(guild.Id);
var channel = guild.GetTextChannel(Convert.ToUInt64(eventConfig["EventCreatedChannel"]));
if (channel != null) {
var roleMention = "";
var role = guild.GetRole(Convert.ToUInt64(eventConfig["EventNotifyReceiverRole"]));
if (role != null)
roleMention = $"{role.Mention} ";
var location = Utils.WrapInline(scheduledEvent.Location) ?? Utils.MentionChannel(scheduledEvent.Channel.Id);
await Utils.SilentSendAsync(channel,
string.Format(Messages.EventCreated, "\n", roleMention, scheduledEvent.Creator.Mention,
Utils.WrapInline(scheduledEvent.Name), location,
scheduledEvent.StartTime.ToUnixTimeSeconds().ToString(), Utils.Wrap(scheduledEvent.Description)),
true);
}
}
private static async Task ScheduledEventCancelledEvent(SocketGuildEvent scheduledEvent) {
var guild = scheduledEvent.Guild;
var eventConfig = Boyfriend.GetGuildConfig(guild.Id);
var channel = guild.GetTextChannel(Convert.ToUInt64(eventConfig["EventCancelledChannel"]));
if (channel != null)
await channel.SendMessageAsync(string.Format(Messages.EventCancelled, Utils.WrapInline(scheduledEvent.Name),
eventConfig["FrowningFace"] == "true" ? $" {Messages.SettingsFrowningFace}" : ""));
}
private static async Task ScheduledEventStartedEvent(SocketGuildEvent scheduledEvent) {
var guild = scheduledEvent.Guild;
var eventConfig = Boyfriend.GetGuildConfig(guild.Id);
var channel = guild.GetTextChannel(Convert.ToUInt64(eventConfig["EventStartedChannel"]));
if (channel != null) {
var receivers = eventConfig["EventStartedReceivers"];
var role = guild.GetRole(Convert.ToUInt64(eventConfig["EventNotifyReceiverRole"]));
var mentions = Boyfriend.StringBuilder;
if (receivers.Contains("role") && role != null) mentions.Append($"{role.Mention} ");
if (receivers.Contains("users") || receivers.Contains("interested"))
foreach (var user in await scheduledEvent.GetUsersAsync(15))
mentions = mentions.Append($"{user.Mention} ");
await channel.SendMessageAsync(string.Format(Messages.EventStarted, mentions,
Utils.WrapInline(scheduledEvent.Name),
Utils.WrapInline(scheduledEvent.Location) ?? Utils.MentionChannel(scheduledEvent.Channel.Id)));
mentions.Clear();
}
}
private static async Task ScheduledEventCompletedEvent(SocketGuildEvent scheduledEvent) {
var guild = scheduledEvent.Guild;
var eventConfig = Boyfriend.GetGuildConfig(guild.Id);
var channel = guild.GetTextChannel(Convert.ToUInt64(eventConfig["EventCompletedChannel"]));
if (channel != null)
await channel.SendMessageAsync(string.Format(Messages.EventCompleted, Utils.WrapInline(scheduledEvent.Name),
Utils.WrapInline(scheduledEvent.StartTime.Subtract(DateTimeOffset.Now).Negate().ToString())));
} }
} }

View file

@ -1,55 +0,0 @@
using System.Globalization;
using Newtonsoft.Json;
// ReSharper disable MemberCanBePrivate.Global
namespace Boyfriend;
public class GuildConfig {
public GuildConfig(ulong id) {
Id = id;
Validate();
}
public ulong? Id { get; }
public string? Lang { get; set; }
public string? Prefix { get; set; }
public bool? RemoveRolesOnMute { get; set; }
public bool? UseSystemChannel { get; set; }
public bool? SendWelcomeMessages { get; set; }
public bool? ReceiveStartupMessages { get; set; }
public string? WelcomeMessage { get; set; }
public ulong? DefaultRole { get; set; }
public ulong? MuteRole { get; set; }
public ulong? AdminLogChannel { get; set; }
public ulong? BotLogChannel { get; set; }
public Dictionary<ulong, List<ulong>>? RolesRemovedOnMute { get; private set; }
public void Validate() {
if (Id == null) throw new Exception("Something went horribly, horribly wrong");
Lang ??= "ru";
Messages.Culture = new CultureInfo(Lang);
Prefix ??= "!";
RemoveRolesOnMute ??= false;
UseSystemChannel ??= true;
SendWelcomeMessages ??= true;
ReceiveStartupMessages ??= true;
WelcomeMessage ??= Messages.DefaultWelcomeMessage;
DefaultRole ??= 0;
MuteRole ??= 0;
AdminLogChannel ??= 0;
BotLogChannel ??= 0;
RolesRemovedOnMute ??= new Dictionary<ulong, List<ulong>>();
}
public async Task Save() {
Validate();
RolesRemovedOnMute!.TrimExcess();
await File.WriteAllTextAsync("config_" + Id + ".json", JsonConvert.SerializeObject(this));
}
}

View file

@ -69,15 +69,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully banned {0} for {1}.
/// </summary>
internal static string BanResponse {
get {
return ResourceManager.GetString("BanResponse", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Bah! . /// Looks up a localized string similar to Bah! .
/// </summary> /// </summary>
@ -106,7 +97,7 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Deleted message from {0} in channel . /// Looks up a localized string similar to Deleted message from {0} in channel {1}: {2}.
/// </summary> /// </summary>
internal static string CachedMessageDeleted { internal static string CachedMessageDeleted {
get { get {
@ -115,7 +106,7 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7}. /// Looks up a localized string similar to Edited message in channel {0}: {1} -&gt; {2}.
/// </summary> /// </summary>
internal static string CachedMessageEdited { internal static string CachedMessageEdited {
get { get {
@ -169,7 +160,88 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Command help:{0}. /// Looks up a localized string similar to Bans a user.
/// </summary>
internal static string CommandDescriptionBan {
get {
return ResourceManager.GetString("CommandDescriptionBan", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deletes a specified amount of messages in this channel.
/// </summary>
internal static string CommandDescriptionClear {
get {
return ResourceManager.GetString("CommandDescriptionClear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows this message.
/// </summary>
internal static string CommandDescriptionHelp {
get {
return ResourceManager.GetString("CommandDescriptionHelp", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Kicks a member.
/// </summary>
internal static string CommandDescriptionKick {
get {
return ResourceManager.GetString("CommandDescriptionKick", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Mutes a member.
/// </summary>
internal static string CommandDescriptionMute {
get {
return ResourceManager.GetString("CommandDescriptionMute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows latency to Discord servers (not counting local processing time).
/// </summary>
internal static string CommandDescriptionPing {
get {
return ResourceManager.GetString("CommandDescriptionPing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Allows you to change certain preferences for this guild.
/// </summary>
internal static string CommandDescriptionSettings {
get {
return ResourceManager.GetString("CommandDescriptionSettings", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unbans a user.
/// </summary>
internal static string CommandDescriptionUnban {
get {
return ResourceManager.GetString("CommandDescriptionUnban", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unmutes a member.
/// </summary>
internal static string CommandDescriptionUnmute {
get {
return ResourceManager.GetString("CommandDescriptionUnmute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Command help:.
/// </summary> /// </summary>
internal static string CommandHelp { internal static string CommandHelp {
get { get {
@ -205,7 +277,7 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Current settings:{0}. /// Looks up a localized string similar to Current settings:.
/// </summary> /// </summary>
internal static string CurrentSettings { internal static string CurrentSettings {
get { get {
@ -213,105 +285,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Admin log channel (`adminLogChannel`): {0}{1}.
/// </summary>
internal static string CurrentSettingsAdminLogChannel {
get {
return ResourceManager.GetString("CurrentSettingsAdminLogChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bot log channel (`botLogChannel`): {0}.
/// </summary>
internal static string CurrentSettingsBotLogChannel {
get {
return ResourceManager.GetString("CurrentSettingsBotLogChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Default role (`defaultRole`): {0}{1}.
/// </summary>
internal static string CurrentSettingsDefaultRole {
get {
return ResourceManager.GetString("CurrentSettingsDefaultRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Language (`lang`): `{0}`{1}.
/// </summary>
internal static string CurrentSettingsLang {
get {
return ResourceManager.GetString("CurrentSettingsLang", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Mute role (`muteRole`): {0}{1}.
/// </summary>
internal static string CurrentSettingsMuteRole {
get {
return ResourceManager.GetString("CurrentSettingsMuteRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prefix (`prefix`): `{0}`{1}.
/// </summary>
internal static string CurrentSettingsPrefix {
get {
return ResourceManager.GetString("CurrentSettingsPrefix", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Receive startup messages (`receiveStartupMessages`): {0}{1}.
/// </summary>
internal static string CurrentSettingsReceiveStartupMessages {
get {
return ResourceManager.GetString("CurrentSettingsReceiveStartupMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove roles on mute (`removeRolesOnMute`): {0}{1}.
/// </summary>
internal static string CurrentSettingsRemoveRoles {
get {
return ResourceManager.GetString("CurrentSettingsRemoveRoles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Send welcome messages (`sendWelcomeMessages`): {0}{1}.
/// </summary>
internal static string CurrentSettingsSendWelcomeMessages {
get {
return ResourceManager.GetString("CurrentSettingsSendWelcomeMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use system channel for notifications (`useSystemChannel`): {0}{1}.
/// </summary>
internal static string CurrentSettingsUseSystemChannel {
get {
return ResourceManager.GetString("CurrentSettingsUseSystemChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Welcome message: `{0}`{1}.
/// </summary>
internal static string CurrentSettingsWelcomeMessage {
get {
return ResourceManager.GetString("CurrentSettingsWelcomeMessage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to {0}, welcome to {1}. /// Looks up a localized string similar to {0}, welcome to {1}.
/// </summary> /// </summary>
@ -339,6 +312,123 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Event {0} is cancelled!{1}.
/// </summary>
internal static string EventCancelled {
get {
return ResourceManager.GetString("EventCancelled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Event {0} has completed! Duration: {1}.
/// </summary>
internal static string EventCompleted {
get {
return ResourceManager.GetString("EventCompleted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {1}{2} created event {3}! It will take place in {4} and will start &lt;t:{5}:R&gt;!{0}{6}.
/// </summary>
internal static string EventCreated {
get {
return ResourceManager.GetString("EventCreated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0}Event {1} is starting at {2}!.
/// </summary>
internal static string EventStarted {
get {
return ResourceManager.GetString("EventStarted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ever.
/// </summary>
internal static string Ever {
get {
return ResourceManager.GetString("Ever", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to *[{0}: {1}]*.
/// </summary>
internal static string FeedbackFormat {
get {
return ResourceManager.GetString("FeedbackFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Kicked {0}: {1}.
/// </summary>
internal static string FeedbackMemberKicked {
get {
return ResourceManager.GetString("FeedbackMemberKicked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Muted {0} for{1}: {2}.
/// </summary>
internal static string FeedbackMemberMuted {
get {
return ResourceManager.GetString("FeedbackMemberMuted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unmuted {0}: {1}.
/// </summary>
internal static string FeedbackMemberUnmuted {
get {
return ResourceManager.GetString("FeedbackMemberUnmuted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deleted {0} messages in {1}.
/// </summary>
internal static string FeedbackMessagesCleared {
get {
return ResourceManager.GetString("FeedbackMessagesCleared", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Value of setting `{0}` is now set to {1}.
/// </summary>
internal static string FeedbackSettingsUpdated {
get {
return ResourceManager.GetString("FeedbackSettingsUpdated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Banned {0} for{1}: {2}.
/// </summary>
internal static string FeedbackUserBanned {
get {
return ResourceManager.GetString("FeedbackUserBanned", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unbanned {0}: {1}.
/// </summary>
internal static string FeedbackUserUnbanned {
get {
return ResourceManager.GetString("FeedbackUserUnbanned", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Members are in different guilds!. /// Looks up a localized string similar to Members are in different guilds!.
/// </summary> /// </summary>
@ -420,15 +510,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully kicked {0} for {1}.
/// </summary>
internal static string KickResponse {
get {
return ResourceManager.GetString("KickResponse", 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>
@ -447,24 +528,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0} kicked {1} for {2}.
/// </summary>
internal static string MemberKicked {
get {
return ResourceManager.GetString("MemberKicked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} muted {1} for {2}{3}.
/// </summary>
internal static string MemberMuted {
get {
return ResourceManager.GetString("MemberMuted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Member not muted!. /// Looks up a localized string similar to Member not muted!.
/// </summary> /// </summary>
@ -483,15 +546,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0} deleted {1} messages in channel {2}.
/// </summary>
internal static string MessagesDeleted {
get {
return ResourceManager.GetString("MessagesDeleted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to ms. /// Looks up a localized string similar to ms.
/// </summary> /// </summary>
@ -501,15 +555,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully muted {0} for {1}.
/// </summary>
internal static string MuteResponse {
get {
return ResourceManager.GetString("MuteResponse", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to No. /// Looks up a localized string similar to No.
/// </summary> /// </summary>
@ -537,15 +582,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0}This punishment will expire &lt;t:{1}:R&gt;.
/// </summary>
internal static string PunishmentExpiresIn {
get {
return ResourceManager.GetString("PunishmentExpiresIn", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to {0}I&apos;m ready! (C#). /// Looks up a localized string similar to {0}I&apos;m ready! (C#).
/// </summary> /// </summary>
@ -592,25 +628,178 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Settings successfully updated. /// Looks up a localized string similar to Not specified.
/// </summary> /// </summary>
internal static string SettingsUpdated { internal static string SettingNotDefined {
get { get {
return ResourceManager.GetString("SettingsUpdated", resourceCulture); return ResourceManager.GetString("SettingNotDefined", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully unbanned {0} for {1}. /// Looks up a localized string similar to Admin log channel.
/// </summary> /// </summary>
internal static string UnbanResponse { internal static string SettingsAdminLogChannel {
get { get {
return ResourceManager.GetString("UnbanResponse", resourceCulture); return ResourceManager.GetString("SettingsAdminLogChannel", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Deleted message in {0}, but I forgot what was there. /// Looks up a localized string similar to Bot log channel.
/// </summary>
internal static string SettingsBotLogChannel {
get {
return ResourceManager.GetString("SettingsBotLogChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Channel for event cancellation notifications.
/// </summary>
internal static string SettingsEventCancelledChannel {
get {
return ResourceManager.GetString("SettingsEventCancelledChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Channel for event completion notifications.
/// </summary>
internal static string SettingsEventCompletedChannel {
get {
return ResourceManager.GetString("SettingsEventCompletedChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Channel for event creation notifications.
/// </summary>
internal static string SettingsEventCreatedChannel {
get {
return ResourceManager.GetString("SettingsEventCreatedChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Role for event creation notifications.
/// </summary>
internal static string SettingsEventNotifyReceiverRole {
get {
return ResourceManager.GetString("SettingsEventNotifyReceiverRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Channel for event start notifications.
/// </summary>
internal static string SettingsEventStartedChannel {
get {
return ResourceManager.GetString("SettingsEventStartedChannel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Event start notifications receivers.
/// </summary>
internal static string SettingsEventStartedReceivers {
get {
return ResourceManager.GetString("SettingsEventStartedReceivers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to :(.
/// </summary>
internal static string SettingsFrowningFace {
get {
return ResourceManager.GetString("SettingsFrowningFace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Language.
/// </summary>
internal static string SettingsLang {
get {
return ResourceManager.GetString("SettingsLang", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Mute role.
/// </summary>
internal static string SettingsMuteRole {
get {
return ResourceManager.GetString("SettingsMuteRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Nothing changed! `{0}` is already set to {1}.
/// </summary>
internal static string SettingsNothingChanged {
get {
return ResourceManager.GetString("SettingsNothingChanged", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prefix.
/// </summary>
internal static string SettingsPrefix {
get {
return ResourceManager.GetString("SettingsPrefix", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Receive startup messages.
/// </summary>
internal static string SettingsReceiveStartupMessages {
get {
return ResourceManager.GetString("SettingsReceiveStartupMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove roles on mute.
/// </summary>
internal static string SettingsRemoveRolesOnMute {
get {
return ResourceManager.GetString("SettingsRemoveRolesOnMute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Send welcome messages.
/// </summary>
internal static string SettingsSendWelcomeMessages {
get {
return ResourceManager.GetString("SettingsSendWelcomeMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Starter role.
/// </summary>
internal static string SettingsStarterRole {
get {
return ResourceManager.GetString("SettingsStarterRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Welcome message.
/// </summary>
internal static string SettingsWelcomeMessage {
get {
return ResourceManager.GetString("SettingsWelcomeMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Message deleted in {0}, but I forgot what was there.
/// </summary> /// </summary>
internal static string UncachedMessageDeleted { internal static string UncachedMessageDeleted {
get { get {
@ -619,7 +808,7 @@ namespace Boyfriend {
} }
/// <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: . /// Looks up a localized string similar to Message edited from {0} in channel {1}, but I forgot what was there before the edit: {2}.
/// </summary> /// </summary>
internal static string UncachedMessageEdited { internal static string UncachedMessageEdited {
get { get {
@ -628,20 +817,11 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully unmuted {0} for {1}. /// Looks up a localized string similar to That user doesn&apos;t exist!.
/// </summary> /// </summary>
internal static string UnmuteResponse { internal static string UserDoesntExist {
get { get {
return ResourceManager.GetString("UnmuteResponse", resourceCulture); return ResourceManager.GetString("UserDoesntExist", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} banned {1} for {2}{3}.
/// </summary>
internal static string UserBanned {
get {
return ResourceManager.GetString("UserBanned", resourceCulture);
} }
} }

View file

@ -31,19 +31,19 @@
<value>{0}I'm ready! (C#)</value> <value>{0}I'm ready! (C#)</value>
</data> </data>
<data name="UncachedMessageDeleted" xml:space="preserve"> <data name="UncachedMessageDeleted" xml:space="preserve">
<value>Deleted message in {0}, but I forgot what was there</value> <value>Message deleted in {0}, but I forgot what was there</value>
</data> </data>
<data name="CachedMessageDeleted" xml:space="preserve"> <data name="CachedMessageDeleted" xml:space="preserve">
<value>Deleted message from {0} in channel </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"> <data name="UncachedMessageEdited" xml:space="preserve">
<value>Message edited from {0} in channel {1}, but I forgot what was there before the edit: </value> <value>Message edited from {0} in channel {1}, but I forgot what was there before the edit: {2}</value>
</data> </data>
<data name="CachedMessageEdited" xml:space="preserve"> <data name="CachedMessageEdited" xml:space="preserve">
<value>Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7}</value> <value>Edited message in channel {0}: {1} -&gt; {2}</value>
</data> </data>
<data name="DefaultWelcomeMessage" xml:space="preserve"> <data name="DefaultWelcomeMessage" xml:space="preserve">
<value>{0}, welcome to {1}</value> <value>{0}, welcome to {1}</value>
@ -84,9 +84,6 @@
<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="UserBanned" xml:space="preserve">
<value>{0} banned {1} for {2}{3}</value>
</data>
<data name="PunishmentExpired" xml:space="preserve"> <data name="PunishmentExpired" xml:space="preserve">
<value>Punishment expired</value> <value>Punishment expired</value>
</data> </data>
@ -96,21 +93,12 @@
<data name="ClearAmountTooLarge" xml:space="preserve"> <data name="ClearAmountTooLarge" xml:space="preserve">
<value>Too many messages specified!</value> <value>Too many messages specified!</value>
</data> </data>
<data name="MessagesDeleted" xml:space="preserve">
<value>{0} deleted {1} messages in channel {2}</value>
</data>
<data name="CommandHelp" xml:space="preserve"> <data name="CommandHelp" xml:space="preserve">
<value>Command help:{0}</value> <value>Command help:</value>
</data> </data>
<data name="YouWereKicked" xml:space="preserve"> <data name="YouWereKicked" xml:space="preserve">
<value>You were kicked by {0} in guild {1} for {2}</value> <value>You were kicked by {0} in guild {1} for {2}</value>
</data> </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}{3}</value>
</data>
<data name="Milliseconds" xml:space="preserve"> <data name="Milliseconds" xml:space="preserve">
<value>ms</value> <value>ms</value>
</data> </data>
@ -124,41 +112,35 @@
<value>Not specified</value> <value>Not specified</value>
</data> </data>
<data name="CurrentSettings" xml:space="preserve"> <data name="CurrentSettings" xml:space="preserve">
<value>Current settings:{0}</value> <value>Current settings:</value>
</data> </data>
<data name="CurrentSettingsLang" xml:space="preserve"> <data name="SettingsLang" xml:space="preserve">
<value>Language (`lang`): `{0}`{1}</value> <value>Language</value>
</data> </data>
<data name="CurrentSettingsPrefix" xml:space="preserve"> <data name="SettingsPrefix" xml:space="preserve">
<value>Prefix (`prefix`): `{0}`{1}</value> <value>Prefix</value>
</data> </data>
<data name="CurrentSettingsRemoveRoles" xml:space="preserve"> <data name="SettingsRemoveRolesOnMute" xml:space="preserve">
<value>Remove roles on mute (`removeRolesOnMute`): {0}{1}</value> <value>Remove roles on mute</value>
</data> </data>
<data name="CurrentSettingsUseSystemChannel" xml:space="preserve"> <data name="SettingsSendWelcomeMessages" xml:space="preserve">
<value>Use system channel for notifications (`useSystemChannel`): {0}{1}</value> <value>Send welcome messages</value>
</data> </data>
<data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve"> <data name="SettingsStarterRole" xml:space="preserve">
<value>Send welcome messages (`sendWelcomeMessages`): {0}{1}</value> <value>Starter role</value>
</data> </data>
<data name="CurrentSettingsDefaultRole" xml:space="preserve"> <data name="SettingsMuteRole" xml:space="preserve">
<value>Default role (`defaultRole`): {0}{1}</value> <value>Mute role</value>
</data> </data>
<data name="CurrentSettingsMuteRole" xml:space="preserve"> <data name="SettingsAdminLogChannel" xml:space="preserve">
<value>Mute role (`muteRole`): {0}{1}</value> <value>Admin log channel</value>
</data> </data>
<data name="CurrentSettingsAdminLogChannel" xml:space="preserve"> <data name="SettingsBotLogChannel" xml:space="preserve">
<value>Admin log channel (`adminLogChannel`): {0}{1}</value> <value>Bot log channel</value>
</data>
<data name="CurrentSettingsBotLogChannel" xml:space="preserve">
<value>Bot log channel (`botLogChannel`): {0}</value>
</data> </data>
<data name="LanguageNotSupported" xml:space="preserve"> <data name="LanguageNotSupported" xml:space="preserve">
<value>Language not supported!</value> <value>Language not supported!</value>
</data> </data>
<data name="SettingsUpdated" xml:space="preserve">
<value>Settings successfully updated</value>
</data>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>Yes</value> <value>Yes</value>
</data> </data>
@ -180,8 +162,8 @@
<data name="UserUnbanned" xml:space="preserve"> <data name="UserUnbanned" xml:space="preserve">
<value>{0} unbanned {1} for {2}</value> <value>{0} unbanned {1} for {2}</value>
</data> </data>
<data name="CurrentSettingsWelcomeMessage" xml:space="preserve"> <data name="SettingsWelcomeMessage" xml:space="preserve">
<value>Welcome message: `{0}`{1}</value> <value>Welcome message</value>
</data> </data>
<data name="NotEnoughArguments" xml:space="preserve"> <data name="NotEnoughArguments" xml:space="preserve">
<value>Not enough arguments! Needed: {0}, provided: {1}</value> <value>Not enough arguments! Needed: {0}, provided: {1}</value>
@ -189,32 +171,17 @@
<data name="ClearInvalidAmountSpecified" xml:space="preserve"> <data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Invalid message amount specified!</value> <value>Invalid message amount specified!</value>
</data> </data>
<data name="BanResponse" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>:white_check_mark: Successfully banned {0} for {1}</value> <value>Banned {0} for{1}: {2}</value>
</data>
<data name="KickResponse" xml:space="preserve">
<value>:white_check_mark: Successfully kicked {0} for {1}</value>
</data> </data>
<data name="UserNotInGuild" xml:space="preserve"> <data name="UserNotInGuild" xml:space="preserve">
<value>The specified user is not a member of this server!</value> <value>The specified user is not a member of this server!</value>
</data> </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>
<data name="SettingDoesntExist" xml:space="preserve"> <data name="SettingDoesntExist" xml:space="preserve">
<value>That setting doesn't exist!</value> <value>That setting doesn't exist!</value>
</data> </data>
<data name="CurrentSettingsReceiveStartupMessages" xml:space="preserve"> <data name="SettingsReceiveStartupMessages" xml:space="preserve">
<value>Receive startup messages (`receiveStartupMessages`): {0}{1}</value> <value>Receive startup messages</value>
</data>
<data name="PunishmentExpiresIn" xml:space="preserve">
<value>{0}This punishment will expire &lt;t:{1}:R&gt;</value>
</data> </data>
<data name="InvalidSettingValue" xml:space="preserve"> <data name="InvalidSettingValue" xml:space="preserve">
<value>Invalid setting value specified!</value> <value>Invalid setting value specified!</value>
@ -237,4 +204,97 @@
<data name="CannotTimeOutBot" xml:space="preserve"> <data name="CannotTimeOutBot" xml:space="preserve">
<value>I cannot use time-outs on other bots! Try to set a mute role in settings</value> <value>I cannot use time-outs on other bots! Try to set a mute role in settings</value>
</data> </data>
<data name="EventCreated" xml:space="preserve">
<value>{1}{2} created event {3}! It will take place in {4} and will start &lt;t:{5}:R&gt;!{0}{6}</value>
</data>
<data name="SettingsEventNotifyReceiverRole" xml:space="preserve">
<value>Role for event creation notifications</value>
</data>
<data name="SettingsEventCreatedChannel" xml:space="preserve">
<value>Channel for event creation notifications</value>
</data>
<data name="SettingsEventStartedChannel" xml:space="preserve">
<value>Channel for event start notifications</value>
</data>
<data name="SettingsEventStartedReceivers" xml:space="preserve">
<value>Event start notifications receivers</value>
</data>
<data name="EventStarted" xml:space="preserve">
<value>{0}Event {1} is starting at {2}!</value>
</data>
<data name="SettingsFrowningFace" xml:space="preserve">
<value>:(</value>
</data>
<data name="EventCancelled" xml:space="preserve">
<value>Event {0} is cancelled!{1}</value>
</data>
<data name="SettingsEventCancelledChannel" xml:space="preserve">
<value>Channel for event cancellation notifications</value>
</data>
<data name="SettingsEventCompletedChannel" xml:space="preserve">
<value>Channel for event completion notifications</value>
</data>
<data name="EventCompleted" xml:space="preserve">
<value>Event {0} has completed! Duration: {1}</value>
</data>
<data name="UserDoesntExist" xml:space="preserve">
<value>That user doesn't exist!</value>
</data>
<data name="FeedbackFormat" xml:space="preserve">
<value>*[{0}: {1}]*</value>
</data>
<data name="Ever" xml:space="preserve">
<value>ever</value>
</data>
<data name="FeedbackMessagesCleared" xml:space="preserve">
<value>Deleted {0} messages in {1}</value>
</data>
<data name="FeedbackMemberKicked" xml:space="preserve">
<value>Kicked {0}: {1}</value>
</data>
<data name="FeedbackMemberMuted" xml:space="preserve">
<value>Muted {0} for{1}: {2}</value>
</data>
<data name="FeedbackUserUnbanned" xml:space="preserve">
<value>Unbanned {0}: {1}</value>
</data>
<data name="FeedbackMemberUnmuted" xml:space="preserve">
<value>Unmuted {0}: {1}</value>
</data>
<data name="SettingsNothingChanged" xml:space="preserve">
<value>Nothing changed! `{0}` is already set to {1}</value>
</data>
<data name="SettingNotDefined" xml:space="preserve">
<value>Not specified</value>
</data>
<data name="FeedbackSettingsUpdated" xml:space="preserve">
<value>Value of setting `{0}` is now set to {1}</value>
</data>
<data name="CommandDescriptionBan" xml:space="preserve">
<value>Bans a user</value>
</data>
<data name="CommandDescriptionClear" xml:space="preserve">
<value>Deletes a specified amount of messages in this channel</value>
</data>
<data name="CommandDescriptionHelp" xml:space="preserve">
<value>Shows this message</value>
</data>
<data name="CommandDescriptionKick" xml:space="preserve">
<value>Kicks a member</value>
</data>
<data name="CommandDescriptionMute" xml:space="preserve">
<value>Mutes a member</value>
</data>
<data name="CommandDescriptionPing" xml:space="preserve">
<value>Shows latency to Discord servers (not counting local processing time)</value>
</data>
<data name="CommandDescriptionSettings" xml:space="preserve">
<value>Allows you to change certain preferences for this guild</value>
</data>
<data name="CommandDescriptionUnban" xml:space="preserve">
<value>Unbans a user</value>
</data>
<data name="CommandDescriptionUnmute" xml:space="preserve">
<value>Unmutes a member</value>
</data>
</root> </root>

View file

@ -25,16 +25,16 @@
<value>Удалено сообщение в канале {0}, но я забыл что там было</value> <value>Удалено сообщение в канале {0}, но я забыл что там было</value>
</data> </data>
<data name="CachedMessageDeleted" xml:space="preserve"> <data name="CachedMessageDeleted" xml:space="preserve">
<value>Удалено сообщение от {0} в канале </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"> <data name="UncachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования: </value> <value>Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования: {2}</value>
</data> </data>
<data name="CachedMessageEdited" xml:space="preserve"> <data name="CachedMessageEdited" xml:space="preserve">
<value>Отредактировано сообщение от {0} в канале {1}.{2}До:{3}{4}{5}После:{6}{7}</value> <value>Отредактировано сообщение в канале {0}: {1} -&gt; {2}</value>
</data> </data>
<data name="DefaultWelcomeMessage" xml:space="preserve"> <data name="DefaultWelcomeMessage" xml:space="preserve">
<value>{0}, добро пожаловать на сервер {1}</value> <value>{0}, добро пожаловать на сервер {1}</value>
@ -75,9 +75,6 @@
<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="UserBanned" xml:space="preserve">
<value>{0} банит {1} за {2}{3}</value>
</data>
<data name="PunishmentExpired" xml:space="preserve"> <data name="PunishmentExpired" xml:space="preserve">
<value>Время наказания истекло</value> <value>Время наказания истекло</value>
</data> </data>
@ -87,21 +84,12 @@
<data name="ClearAmountTooLarge" xml:space="preserve"> <data name="ClearAmountTooLarge" xml:space="preserve">
<value>Указано слишком много сообщений!</value> <value>Указано слишком много сообщений!</value>
</data> </data>
<data name="MessagesDeleted" xml:space="preserve">
<value>{0} удаляет {1} сообщений в канале {2}</value>
</data>
<data name="CommandHelp" xml:space="preserve"> <data name="CommandHelp" xml:space="preserve">
<value>Справка по командам:{0}</value> <value>Справка по командам:</value>
</data> </data>
<data name="YouWereKicked" xml:space="preserve"> <data name="YouWereKicked" xml:space="preserve">
<value>Тебя кикнул {0} на сервере {1} за {2}</value> <value>Тебя кикнул {0} на сервере {1} за {2}</value>
</data> </data>
<data name="MemberKicked" xml:space="preserve">
<value>{0} выгоняет {1} за {2}</value>
</data>
<data name="MemberMuted" xml:space="preserve">
<value>{0} глушит {1} за {2}{3}</value>
</data>
<data name="Milliseconds" xml:space="preserve"> <data name="Milliseconds" xml:space="preserve">
<value>мс</value> <value>мс</value>
</data> </data>
@ -115,41 +103,32 @@
<value>Не указана</value> <value>Не указана</value>
</data> </data>
<data name="CurrentSettings" xml:space="preserve"> <data name="CurrentSettings" xml:space="preserve">
<value>Текущие настройки:{0}</value> <value>Текущие настройки:</value>
</data> </data>
<data name="CurrentSettingsLang" xml:space="preserve"> <data name="SettingsLang" xml:space="preserve">
<value>Язык (`lang`): `{0}`{1}</value> <value>Язык</value>
</data> </data>
<data name="CurrentSettingsPrefix" xml:space="preserve"> <data name="SettingsPrefix" xml:space="preserve">
<value>Префикс (`prefix`): `{0}`{1}</value> <value>Префикс</value>
</data> </data>
<data name="CurrentSettingsRemoveRoles" xml:space="preserve"> <data name="SettingsRemoveRolesOnMute" xml:space="preserve">
<value>Удалять роли при муте (`removeRolesOnMute`): {0}{1}</value> <value>Удалять роли при муте</value>
</data> </data>
<data name="CurrentSettingsUseSystemChannel" xml:space="preserve"> <data name="SettingsSendWelcomeMessages" xml:space="preserve">
<value>Использовать канал системных сообщений для уведомлений (`useSystemChannel`): {0}{1}</value> <value>Отправлять приветствия</value>
</data> </data>
<data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve"> <data name="SettingsMuteRole" xml:space="preserve">
<value>Отправлять приветствия (`sendWelcomeMessages`): {0}{1}</value> <value>Роль мута</value>
</data> </data>
<data name="CurrentSettingsDefaultRole" xml:space="preserve"> <data name="SettingsAdminLogChannel" xml:space="preserve">
<value>Стандартная роль (`defaultRole`): {0}{1}</value> <value>Канал админ-уведомлений</value>
</data> </data>
<data name="CurrentSettingsMuteRole" xml:space="preserve"> <data name="SettingsBotLogChannel" xml:space="preserve">
<value>Роль мута (`muteRole`): {0}{1}</value> <value>Канал бот-уведомлений</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>
<data name="LanguageNotSupported" xml:space="preserve"> <data name="LanguageNotSupported" xml:space="preserve">
<value>Язык не поддерживается!</value> <value>Язык не поддерживается!</value>
</data> </data>
<data name="SettingsUpdated" xml:space="preserve">
<value>Настройки успешно обновлены!</value>
</data>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>Да</value> <value>Да</value>
</data> </data>
@ -171,8 +150,8 @@
<data name="UserUnbanned" xml:space="preserve"> <data name="UserUnbanned" xml:space="preserve">
<value>{0} возвращает из бана {1} за {2}</value> <value>{0} возвращает из бана {1} за {2}</value>
</data> </data>
<data name="CurrentSettingsWelcomeMessage" xml:space="preserve"> <data name="SettingsWelcomeMessage" xml:space="preserve">
<value>Приветствие: `{0}`{1}</value> <value>Приветствие</value>
</data> </data>
<data name="NotEnoughArguments" xml:space="preserve"> <data name="NotEnoughArguments" xml:space="preserve">
<value>Недостаточно аргументов! Требуется: {0}, указано: {1}</value> <value>Недостаточно аргументов! Требуется: {0}, указано: {1}</value>
@ -180,32 +159,17 @@
<data name="ClearInvalidAmountSpecified" xml:space="preserve"> <data name="ClearInvalidAmountSpecified" xml:space="preserve">
<value>Указано неверное количество сообщений!</value> <value>Указано неверное количество сообщений!</value>
</data> </data>
<data name="BanResponse" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>:white_check_mark: Успешно забанен {0} за {1}</value> <value>Забанен {0} на{1}: {2}</value>
</data>
<data name="KickResponse" xml:space="preserve">
<value>:white_check_mark: Успешно выгнан {0} за {1}</value>
</data> </data>
<data name="UserNotInGuild" xml:space="preserve"> <data name="UserNotInGuild" xml:space="preserve">
<value>Указанный пользователь не является участником этого сервера!</value> <value>Указанный пользователь не является участником этого сервера!</value>
</data> </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>
<data name="SettingDoesntExist" xml:space="preserve"> <data name="SettingDoesntExist" xml:space="preserve">
<value>Такая настройка не существует!</value> <value>Такая настройка не существует!</value>
</data> </data>
<data name="CurrentSettingsReceiveStartupMessages" xml:space="preserve"> <data name="SettingsReceiveStartupMessages" xml:space="preserve">
<value>Получать сообщения о запуске (`receiveStartupMessages`): {0}{1}</value> <value>Получать сообщения о запуске</value>
</data>
<data name="PunishmentExpiresIn" xml:space="preserve">
<value>{0}Это наказание истечёт &lt;t:{1}:R&gt;</value>
</data> </data>
<data name="InvalidSettingValue" xml:space="preserve"> <data name="InvalidSettingValue" xml:space="preserve">
<value>Указано недействительное значение для настройки!</value> <value>Указано недействительное значение для настройки!</value>
@ -228,4 +192,100 @@
<data name="CannotTimeOutBot" xml:space="preserve"> <data name="CannotTimeOutBot" xml:space="preserve">
<value>Я не могу использовать тайм-ауты на других ботах! Попробуй указать роль мута в настройках</value> <value>Я не могу использовать тайм-ауты на других ботах! Попробуй указать роль мута в настройках</value>
</data> </data>
<data name="SettingsStarterRole" xml:space="preserve">
<value>Начальная роль</value>
</data>
<data name="EventCreated" xml:space="preserve">
<value>{1}{2} создал событие {3}! Оно пройдёт в {4} и начнётся &lt;t:{5}:R&gt;!{0}{6}</value>
</data>
<data name="SettingsEventNotifyReceiverRole" xml:space="preserve">
<value>Роль для уведомлений о создании событий</value>
</data>
<data name="SettingsEventCreatedChannel" xml:space="preserve">
<value>Канал для уведомлений о создании событий</value>
</data>
<data name="SettingsEventStartedChannel" xml:space="preserve">
<value>Канал для уведомлений о начале событий</value>
</data>
<data name="SettingsEventStartedReceivers" xml:space="preserve">
<value>Получатели уведомлений о начале событий</value>
</data>
<data name="EventStarted" xml:space="preserve">
<value>{0}Событие {1} начинается в {2}!</value>
</data>
<data name="SettingsFrowningFace" xml:space="preserve">
<value>:( </value>
</data>
<data name="EventCancelled" xml:space="preserve">
<value>Событие {0} отменено!{1}</value>
</data>
<data name="SettingsEventCancelledChannel" xml:space="preserve">
<value>Канал для уведомлений о отмене событий</value>
</data>
<data name="SettingsEventCompletedChannel" xml:space="preserve">
<value>Канал для уведомлений о завершении событий</value>
</data>
<data name="EventCompleted" xml:space="preserve">
<value>Событие {0} завершено! Продолжительность: {1}</value>
</data>
<data name="UserDoesntExist" xml:space="preserve">
<value>Такого пользователя не существует!</value>
</data>
<data name="FeedbackFormat" xml:space="preserve">
<value>*[{0}: {1}]* </value>
</data>
<data name="Ever" xml:space="preserve">
<value>всегда</value>
</data>
<data name="FeedbackMessagesCleared" xml:space="preserve">
<value>Удалено {0} сообщений в {1}</value>
</data>
<data name="FeedbackMemberKicked" xml:space="preserve">
<value>Выгнан {0}: {1}</value>
</data>
<data name="FeedbackMemberMuted" xml:space="preserve">
<value>Заглушен {0} на{1}: {2}</value>
</data>
<data name="FeedbackUserUnbanned" xml:space="preserve">
<value>Возвращён из бана {0}: {1}</value>
</data>
<data name="FeedbackMemberUnmuted" xml:space="preserve">
<value>Разглушен {0}: {1}</value>
</data>
<data name="SettingsNothingChanged" xml:space="preserve">
<value>Ничего не изменилось! Значение настройки `{0}` уже {1}</value>
</data>
<data name="SettingNotDefined" xml:space="preserve">
<value>Не указано</value>
</data>
<data name="FeedbackSettingsUpdated" xml:space="preserve">
<value>Значение настройки `{0}` теперь установлено на {1}</value>
</data>
<data name="CommandDescriptionBan" xml:space="preserve">
<value>Банит пользователя</value>
</data>
<data name="CommandDescriptionClear" xml:space="preserve">
<value>Удаляет указанное количество сообщений в этом канале</value>
</data>
<data name="CommandDescriptionHelp" xml:space="preserve">
<value>Показывает эту справку</value>
</data>
<data name="CommandDescriptionKick" xml:space="preserve">
<value>Выгоняет участника</value>
</data>
<data name="CommandDescriptionMute" xml:space="preserve">
<value>Глушит участника</value>
</data>
<data name="CommandDescriptionPing" xml:space="preserve">
<value>Показывает задержку до серверов Discord (не считая времени на локальные вычисления)</value>
</data>
<data name="CommandDescriptionSettings" xml:space="preserve">
<value>Позволяет менять некоторые настройки под этот сервер</value>
</data>
<data name="CommandDescriptionUnban" xml:space="preserve">
<value>Возвращает пользователя из бана</value>
</data>
<data name="CommandDescriptionUnmute" xml:space="preserve">
<value>Разглушает участника</value>
</data>
</root> </root>

View file

@ -1,11 +1,16 @@
using System.Globalization; using System.Globalization;
using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Discord; using Discord;
using Discord.Net; using Discord.Net;
using Discord.WebSocket;
using Humanizer;
using Humanizer.Localisation;
namespace Boyfriend; namespace Boyfriend;
public static class Utils { public static class Utils {
private static readonly string[] Formats = { 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'%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'", "%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'",
@ -14,65 +19,60 @@ public static class Utils {
"%d'д'%s'с'", "%d'д'", "%h'ч'%m'м'%s'с'", "%h'ч'%m'м'", "%h'ч'%s'с'", "%h'ч'", "%m'м'%s'с'", "%m'м'", "%s'с'" "%d'д'%s'с'", "%d'д'", "%h'ч'%m'м'%s'с'", "%h'ч'%m'м'", "%h'ч'%s'с'", "%h'ч'", "%m'м'%s'с'", "%m'м'", "%s'с'"
}; };
public static string GetBeep(string cultureInfo, int i = -1) { public static readonly Random Random = new();
Messages.Culture = new CultureInfo(cultureInfo); private static readonly Dictionary<string, string> ReflectionMessageCache = new();
private static readonly Dictionary<string, CultureInfo> CultureInfoCache = new() {
{"ru", new CultureInfo("ru-RU")},
{"en", new CultureInfo("en-US")}
};
private static readonly Dictionary<ulong, SocketRole> MuteRoleCache = new();
var beeps = new[] {Messages.Beep1, Messages.Beep2, Messages.Beep3}; private static readonly AllowedMentions AllowRoles = new() {
return beeps[i < 0 ? new Random().Next(3) : i]; AllowedTypes = AllowedMentionTypes.Roles
};
public static string GetBeep(int i = -1) {
return GetMessage($"Beep{(i < 0 ? Random.Next(3) + 1 : ++i)}");
} }
public static async Task<ITextChannel?> GetAdminLogChannel(IGuild guild) { public static SocketTextChannel? GetAdminLogChannel(ulong id) {
var adminLogChannel = await ParseChannelNullable(Boyfriend.GetGuildConfig(guild).AdminLogChannel.ToString()!); return Boyfriend.Client.GetGuild(id)
return adminLogChannel as ITextChannel; .GetTextChannel(ParseMention(Boyfriend.GetGuildConfig(id)["AdminLogChannel"]));
} }
public static string Wrap(string original) { public static string Wrap(string? original) {
if (original == null) return "";
var toReturn = original.Replace("```", "ˋˋˋ"); var toReturn = original.Replace("```", "ˋˋˋ");
return $"```{toReturn}{(toReturn.EndsWith("`") || toReturn.Trim().Equals("") ? " " : "")}```"; return $"```{toReturn}{(toReturn.EndsWith("`") || toReturn.Trim().Equals("") ? " " : "")}```";
} }
public static string WrapInline(string original) { public static string? WrapInline(string? original) {
return $"`{original.Replace("`", "ˋ")}`"; return original == null ? null : $"`{original.Replace("`", "ˋ")}`";
}
public static string? WrapAsNeeded(string? original) {
if (original == null) return null;
return original.Contains('\n') ? Wrap(original) : WrapInline(original);
} }
public static string MentionChannel(ulong id) { public static string MentionChannel(ulong id) {
return $"<#{id}>"; return $"<#{id}>";
} }
private static ulong ParseMention(string mention) { public static ulong ParseMention(string mention) {
return Convert.ToUInt64(Regex.Replace(mention, "[^0-9]", "")); return ulong.TryParse(Regex.Replace(mention, "[^0-9]", ""), out var id) ? id : 0;
} }
private static ulong? ParseMentionNullable(string mention) { public static SocketUser? ParseUser(string mention) {
try { var user = Boyfriend.Client.GetUser(ParseMention(mention));
return ParseMention(mention) == 0 ? throw new FormatException() : ParseMention(mention); return user;
} catch (FormatException) {
return null;
}
} }
public static async Task<IUser> ParseUser(string mention) { public static SocketGuildUser? ParseMember(SocketGuild guild, string mention) {
var user = Boyfriend.Client.GetUserAsync(ParseMention(mention)); return guild.GetUser(ParseMention(mention));
return await user;
} }
public static async Task<IGuildUser> ParseMember(IGuild guild, string mention) { public static async Task SendDirectMessage(SocketUser user, string toSend) {
return await guild.GetUserAsync(ParseMention(mention));
}
private static async Task<IChannel> ParseChannel(string mention) {
return await Boyfriend.Client.GetChannelAsync(ParseMention(mention));
}
private 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));
}
public static async Task SendDirectMessage(IUser user, string toSend) {
try { try {
await user.SendMessageAsync(toSend); await user.SendMessageAsync(toSend);
} catch (HttpException e) { } catch (HttpException e) {
@ -81,33 +81,74 @@ public static class Utils {
} }
} }
public static IRole? GetMuteRole(IGuild guild) { public static SocketRole? GetMuteRole(ref SocketGuild guild) {
var role = guild.Roles.FirstOrDefault(x => x.Id == Boyfriend.GetGuildConfig(guild).MuteRole); var id = ulong.Parse(Boyfriend.GetGuildConfig(guild.Id)["MuteRole"]);
if (MuteRoleCache.ContainsKey(id)) return MuteRoleCache[id];
SocketRole? role = null;
foreach (var x in guild.Roles) {
if (x.Id != id) continue;
role = x;
MuteRoleCache.Add(id, role);
break;
}
return role; return role;
} }
public static async Task SilentSendAsync(ITextChannel? channel, string text) { public static async Task SilentSendAsync(SocketTextChannel? channel, string text, bool allowRoles = false) {
if (channel == null) return; if (channel == null || text.Length is 0 or > 2000) return;
try { await channel.SendMessageAsync(text, false, null, null, allowRoles ? AllowRoles : AllowedMentions.None);
await channel.SendMessageAsync(text, false, null, null, AllowedMentions.None);
} catch (ArgumentException) {}
} }
public static TimeSpan GetTimeSpan(string from) { public static TimeSpan? GetTimeSpan(ref string from) {
return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture); if (TimeSpan.TryParseExact(from.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan))
return timeSpan;
return null;
} }
public static string JoinString(string[] args, int startIndex) { public static string JoinString(ref string[] args, int startIndex) {
return string.Join(" ", args, startIndex, args.Length - startIndex); return string.Join(" ", args, startIndex, args.Length - startIndex);
} }
public static string GetNameAndDiscrim(IUser user) {
return $"{user.Username}#{user.Discriminator}";
}
public static RequestOptions GetRequestOptions(string reason) { public static RequestOptions GetRequestOptions(string reason) {
var options = RequestOptions.Default; var options = RequestOptions.Default;
options.AuditLogReason = reason; options.AuditLogReason = reason;
return options; return options;
} }
public static string GetMessage(string name) {
var propertyName = name;
name = $"{Messages.Culture}/{name}";
if (ReflectionMessageCache.ContainsKey(name)) return ReflectionMessageCache[name];
var toReturn =
typeof(Messages).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null)
?.ToString()! ?? throw new Exception($"Could not find localized property: {name}");
ReflectionMessageCache.Add(name, toReturn);
return toReturn;
}
public static async Task SendFeedback(string feedback, ulong guildId, string mention, bool sendPublic = false) {
var adminChannel = GetAdminLogChannel(guildId);
var systemChannel = Boyfriend.Client.GetGuild(guildId).SystemChannel;
var toSend = string.Format(Messages.FeedbackFormat, mention, feedback);
if (adminChannel != null)
await SilentSendAsync(adminChannel, toSend);
if (sendPublic && systemChannel != null)
await SilentSendAsync(systemChannel, toSend);
}
public static void StackFeedback(ref string feedback, ref string mention, bool isPublic) {
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 ? $" {span.Humanize(minUnit: TimeUnit.Second, culture: Messages.Culture)}"
: Messages.Ever;
}
public static void SetCurrentLanguage(ulong guildId) {
Messages.Culture = CultureInfoCache[Boyfriend.GetGuildConfig(guildId)["Lang"]];
}
} }

BIN
UpgradeLog.htm Normal file

Binary file not shown.