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

time-out failsafes and new warnings

rewrote setting values in SettingsCommand.cs
fixed a bug with message edited notification on mobile
fixed an exploit with WrapInline where you could escape the code block by simply using `
moved a few things in MuteCommand.cs
cleaned up code
updated library to 3.3.2
This commit is contained in:
l1ttleO 2022-02-21 22:08:55 +05:00
parent e41a459f6f
commit 868b6bcaa7
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
12 changed files with 220 additions and 345 deletions

View file

@ -1,7 +1,7 @@
using System.Globalization; using System.Globalization;
using Newtonsoft.Json;
using Discord; using Discord;
using Discord.WebSocket; using Discord.WebSocket;
using Newtonsoft.Json;
namespace Boyfriend; namespace Boyfriend;
@ -56,20 +56,11 @@ public static class Boyfriend {
} }
} }
public static void ResetGuildConfig(IGuild guild) {
GuildConfigDictionary.Remove(guild.Id);
var config = new GuildConfig(guild.Id);
config.Validate();
GuildConfigDictionary.Add(guild.Id, config);
}
public static GuildConfig GetGuildConfig(IGuild guild) { public static GuildConfig GetGuildConfig(IGuild guild) {
Messages.Culture = new CultureInfo("ru"); Messages.Culture = new CultureInfo("ru");
var config = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id] : var config = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id]
new GuildConfig(guild.Id); : new GuildConfig(guild.Id);
config.Validate(); config.Validate();
return config; return config;
@ -82,4 +73,4 @@ public static class Boyfriend {
throw new Exception(Messages.CouldntFindGuildByChannel); throw new Exception(Messages.CouldntFindGuildByChannel);
} }
} }

View file

@ -15,7 +15,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.2.1" /> <PackageReference Include="Discord.Net" Version="3.3.2"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>

View file

@ -16,6 +16,7 @@ public class BanCommand : Command {
duration = Utils.GetTimeSpan(args[1]); duration = Utils.GetTimeSpan(args[1]);
reason = Utils.JoinString(args, 2); reason = Utils.JoinString(args, 2);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) { } catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
await Warn(context.Channel as ITextChannel, Messages.DurationParseFailed);
duration = TimeSpan.FromMilliseconds(-1); duration = TimeSpan.FromMilliseconds(-1);
} }
@ -28,10 +29,8 @@ public class BanCommand : Command {
var authorMention = author.Mention; var authorMention = author.Mention;
var guildBanMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}"; var guildBanMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}";
var memberToBan = await guild.GetUserAsync(toBan.Id); var memberToBan = await guild.GetUserAsync(toBan.Id);
var expiresIn = duration.TotalSeconds > 0 var expiresIn = duration.TotalSeconds > 0 ? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine,
? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine, DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds) : "";
DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds)
: "";
var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason), var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason),
expiresIn); expiresIn);
@ -39,25 +38,24 @@ public class BanCommand : Command {
if (memberToBan != null) if (memberToBan != null)
await CommandHandler.CheckInteractions(author, memberToBan); await CommandHandler.CheckInteractions(author, memberToBan);
await Utils.SendDirectMessage(toBan, string.Format(Messages.YouWereBanned, author.Mention, guild.Name, await Utils.SendDirectMessage(toBan,
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, string.Format(Messages.BanResponse, toBan.Mention, await Utils.SilentSendAsync(channel,
Utils.WrapInline(reason))); string.Format(Messages.BanResponse, toBan.Mention, Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification); await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
if (duration.TotalSeconds > 0) { if (duration.TotalSeconds > 0) {
var task = new Task(async () => { await Task.Run(async () => {
await Task.Delay(duration); await Task.Delay(duration);
try { try {
await UnbanCommand.UnbanUser(guild, null, await guild.GetCurrentUserAsync(), toBan, await UnbanCommand.UnbanUser(guild, null, await guild.GetCurrentUserAsync(), toBan,
Messages.PunishmentExpired); Messages.PunishmentExpired);
} catch (ApplicationException) {} } catch (ApplicationException) {}
}); });
task.Start();
} }
} }
@ -72,4 +70,4 @@ public class BanCommand : Command {
public override string GetSummary() { public override string GetSummary() {
return "Банит пользователя"; return "Банит пользователя";
} }
} }

View file

@ -1,6 +1,5 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
namespace Boyfriend.Commands; namespace Boyfriend.Commands;
@ -13,7 +12,7 @@ public abstract class Command {
public abstract string GetSummary(); public abstract string GetSummary();
protected static async Task Warn(ISocketMessageChannel channel, string warning) { protected static async Task Warn(ITextChannel? channel, string warning) {
await Utils.SilentSendAsync(channel as ITextChannel, ":warning: " + warning); await Utils.SilentSendAsync(channel, ":warning: " + warning);
} }
} }

View file

@ -22,23 +22,22 @@ public class MuteCommand : Command {
duration = Utils.GetTimeSpan(args[1]); duration = Utils.GetTimeSpan(args[1]);
reason = Utils.JoinString(args, 2); reason = Utils.JoinString(args, 2);
} catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) { } catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
await Warn(context.Channel as ITextChannel, Messages.DurationParseFailed);
duration = TimeSpan.FromMilliseconds(-1); duration = TimeSpan.FromMilliseconds(-1);
} }
if (toMute == null) if (toMute == null)
throw new ApplicationException(Messages.UserNotInGuild); throw new ApplicationException(Messages.UserNotInGuild);
if (role != null && toMute.RoleIds.Any(x => x == role.Id) || if (role != null && toMute.RoleIds.Any(x => x == role.Id) || toMute.TimedOutUntil != null &&
toMute.TimedOutUntil != null && toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds() > DateTimeOffset.Now.ToUnixTimeMilliseconds())
> DateTimeOffset.Now.ToUnixTimeMilliseconds())
throw new ApplicationException(Messages.MemberAlreadyMuted); throw new ApplicationException(Messages.MemberAlreadyMuted);
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(); await config.Save();
await Warn(context.Channel, Messages.RolesReturned); throw new ApplicationException(Messages.RolesReturned);
return;
} }
await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles); await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles);
@ -55,10 +54,9 @@ public class MuteCommand : Command {
var config = Boyfriend.GetGuildConfig(guild); var config = Boyfriend.GetGuildConfig(guild);
var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}"); var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}");
var role = Utils.GetMuteRole(guild); var role = Utils.GetMuteRole(guild);
var expiresIn = duration.TotalSeconds > 0 var hasDuration = duration.TotalSeconds > 0;
? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine, var expiresIn = hasDuration ? string.Format(Messages.PunishmentExpiresIn, Environment.NewLine,
DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds) DateTimeOffset.Now.ToUnixTimeSeconds() + duration.TotalSeconds) : "";
: "";
var notification = string.Format(Messages.MemberMuted, authorMention, toMute.Mention, Utils.WrapInline(reason), var notification = string.Format(Messages.MemberMuted, authorMention, toMute.Mention, Utils.WrapInline(reason),
expiresIn); expiresIn);
@ -71,31 +69,38 @@ public class MuteCommand : Command {
if (roleId == role.Id) continue; if (roleId == role.Id) continue;
await toMute.RemoveRoleAsync(roleId); await toMute.RemoveRoleAsync(roleId);
rolesRemoved.Add(roleId); rolesRemoved.Add(roleId);
} catch (HttpException) {} } catch (HttpException e) {
await Warn(channel,
string.Format(Messages.RoleRemovalFailed, $"<@&{roleId}>", Utils.WrapInline(e.Reason)));
}
} }
config.RolesRemovedOnMute!.Add(toMute.Id, rolesRemoved); config.RolesRemovedOnMute!.Add(toMute.Id, rolesRemoved);
await config.Save(); await config.Save();
if (hasDuration)
await Task.Run(async () => {
await Task.Delay(duration);
try {
await UnmuteCommand.UnmuteMember(guild, null, await guild.GetCurrentUserAsync(), toMute,
Messages.PunishmentExpired);
} catch (ApplicationException) {}
});
} }
await toMute.AddRoleAsync(role, requestOptions); await toMute.AddRoleAsync(role, requestOptions);
} else } else {
if (!hasDuration)
throw new ApplicationException(Messages.DurationRequiredForTimeOuts);
if (toMute.IsBot)
throw new ApplicationException(Messages.CannotTimeOutBot);
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(channel,
string.Format(Messages.MuteResponse, toMute.Mention, Utils.WrapInline(reason)));
await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification); await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
if (role != null && duration.TotalSeconds > 0) {
var task = new Task(async () => {
await Task.Delay(duration);
try {
await UnmuteCommand.UnmuteMember(guild, null, await guild.GetCurrentUserAsync(), toMute,
Messages.PunishmentExpired);
} catch (ApplicationException) {}
});
task.Start();
}
} }
public override List<string> GetAliases() { public override List<string> GetAliases() {
@ -109,4 +114,4 @@ public class MuteCommand : Command {
public override string GetSummary() { public override string GetSummary() {
return "Глушит участника"; return "Глушит участника";
} }
} }

View file

@ -1,4 +1,4 @@
using System.Globalization; using System.Reflection;
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
@ -16,117 +16,83 @@ public class SettingsCommand : Command {
if (args.Length == 0) { if (args.Length == 0) {
var nl = Environment.NewLine; var nl = Environment.NewLine;
var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel.GetValueOrDefault(0)); dynamic forCheck;
var admin = adminLogChannel == null ? Messages.ChannelNotSpecified : adminLogChannel.Mention; var adminLogChannel = (forCheck = guild.GetTextChannel(config.AdminLogChannel.GetValueOrDefault(0))) == null
var botLogChannel = guild.GetTextChannel(config.BotLogChannel.GetValueOrDefault(0)); ? Messages.ChannelNotSpecified : forCheck.Mention;
var bot = botLogChannel == null ? Messages.ChannelNotSpecified : botLogChannel.Mention; var botLogChannel = (forCheck = guild.GetTextChannel(config.BotLogChannel.GetValueOrDefault(0))) == null
var muteRole = guild.GetRole(config.MuteRole.GetValueOrDefault(0)); ? Messages.ChannelNotSpecified : forCheck.Mention;
var mute = muteRole == null ? Messages.RoleNotSpecified : muteRole.Mention; var muteRole = (forCheck = guild.GetRole(config.MuteRole.GetValueOrDefault(0))) == null
var defaultRole = guild.GetRole(config.DefaultRole.GetValueOrDefault(0)); ? Messages.RoleNotSpecified : forCheck.Mention;
var defaultr = defaultRole == null ? Messages.RoleNotSpecified : defaultRole.Mention; var defaultRole = (forCheck = guild.GetRole(config.DefaultRole.GetValueOrDefault(0))) == null
? Messages.RoleNotSpecified : forCheck.Mention;
var toSend = string.Format(Messages.CurrentSettings, nl) + var toSend = string.Format(Messages.CurrentSettings, nl) +
string.Format(Messages.CurrentSettingsLang, config.Lang, nl) + string.Format(Messages.CurrentSettingsLang, config.Lang, nl) +
string.Format(Messages.CurrentSettingsPrefix, config.Prefix, nl) + string.Format(Messages.CurrentSettingsPrefix, config.Prefix, nl) +
string.Format(Messages.CurrentSettingsRemoveRoles, YesOrNo( string.Format(Messages.CurrentSettingsRemoveRoles,
config.RemoveRolesOnMute.GetValueOrDefault(false)), nl) + YesOrNo(config.RemoveRolesOnMute.GetValueOrDefault(false)), nl) +
string.Format(Messages.CurrentSettingsUseSystemChannel, YesOrNo( string.Format(Messages.CurrentSettingsUseSystemChannel,
config.UseSystemChannel.GetValueOrDefault(true)), nl) + YesOrNo(config.UseSystemChannel.GetValueOrDefault(true)), nl) +
string.Format(Messages.CurrentSettingsSendWelcomeMessages, YesOrNo( string.Format(Messages.CurrentSettingsSendWelcomeMessages,
config.SendWelcomeMessages.GetValueOrDefault(true)), nl) + YesOrNo(config.SendWelcomeMessages.GetValueOrDefault(true)), nl) +
string.Format(Messages.CurrentSettingsReceiveStartupMessages, YesOrNo( string.Format(Messages.CurrentSettingsReceiveStartupMessages,
config.ReceiveStartupMessages.GetValueOrDefault(true)), nl) + YesOrNo(config.ReceiveStartupMessages.GetValueOrDefault(true)), nl) +
string.Format(Messages.CurrentSettingsWelcomeMessage, config.WelcomeMessage, nl) + string.Format(Messages.CurrentSettingsWelcomeMessage, config.WelcomeMessage, nl) +
string.Format(Messages.CurrentSettingsDefaultRole, defaultr, nl) + string.Format(Messages.CurrentSettingsDefaultRole, defaultRole, nl) +
string.Format(Messages.CurrentSettingsMuteRole, mute, nl) + string.Format(Messages.CurrentSettingsMuteRole, muteRole, nl) +
string.Format(Messages.CurrentSettingsAdminLogChannel, admin, nl) + string.Format(Messages.CurrentSettingsAdminLogChannel, adminLogChannel, nl) +
string.Format(Messages.CurrentSettingsBotLogChannel, bot); string.Format(Messages.CurrentSettingsBotLogChannel, botLogChannel);
await Utils.SilentSendAsync(context.Channel as ITextChannel ?? throw new ApplicationException(), toSend); await Utils.SilentSendAsync(context.Channel as ITextChannel ?? throw new ApplicationException(), toSend);
return; return;
} }
var setting = args[0].ToLower(); var setting = args[0].ToLower();
var shouldDefault = false;
var value = ""; var value = "";
if (args.Length >= 2) if (args.Length >= 2)
value = args[1].ToLower(); try {
else value = args[1].ToLower();
shouldDefault = true; } catch (IndexOutOfRangeException) {
throw new ApplicationException(Messages.InvalidSettingValue);
}
var boolValue = ParseBool(value); PropertyInfo? property = null;
var channel = await Utils.ParseChannelNullable(value) as IGuildChannel; foreach (var prop in typeof(GuildConfig).GetProperties())
var role = Utils.ParseRoleNullable(guild, value); if (setting == prop.Name.ToLower())
property = prop;
if (property == null || !property.CanWrite)
throw new ApplicationException(Messages.SettingDoesntExist);
var type = property.PropertyType;
switch (setting) { if (value is "reset" or "default") {
case "reset": property.SetValue(config, null);
Boyfriend.ResetGuildConfig(guild); } else if (type == typeof(string)) {
break; if (setting == "lang" && value is not ("ru" or "en"))
case "lang" when value is not ("ru" or "en"):
throw new ApplicationException(Messages.LanguageNotSupported); throw new ApplicationException(Messages.LanguageNotSupported);
case "lang": property.SetValue(config, value);
config.Lang = shouldDefault ? "ru" : value; } else {
Messages.Culture = new CultureInfo(shouldDefault ? "ru" : value); try {
break; if (type == typeof(bool?))
case "prefix": property.SetValue(config, Convert.ToBoolean(value));
config.Prefix = shouldDefault ? "!" : value;
break; if (type == typeof(ulong?)) {
case "removerolesonmute": var id = Convert.ToUInt64(value);
config.RemoveRolesOnMute = !shouldDefault && GetBoolValue(boolValue); if (property.Name.EndsWith("Channel") && guild.GetTextChannel(id) == null)
break; throw new ApplicationException(Messages.InvalidChannel);
case "usesystemchannel": if (property.Name.EndsWith("Role") && guild.GetRole(id) == null)
config.UseSystemChannel = shouldDefault || GetBoolValue(boolValue); throw new ApplicationException(Messages.InvalidRole);
break;
case "sendwelcomemessages": property.SetValue(config, id);
config.SendWelcomeMessages = shouldDefault || GetBoolValue(boolValue); }
break; } catch (Exception e) when (e is FormatException or OverflowException) {
case "receivestartupmessages": throw new ApplicationException(Messages.InvalidSettingValue);
config.ReceiveStartupMessages = shouldDefault || GetBoolValue(boolValue); }
break;
case "welcomemessage":
config.WelcomeMessage = shouldDefault ? Messages.DefaultWelcomeMessage : value;
break;
case "defaultrole":
config.DefaultRole = shouldDefault ? 0 : GetRoleId(role);
break;
case "muterole":
config.MuteRole = shouldDefault ? 0 : GetRoleId(role);
break;
case "adminlogchannel":
config.AdminLogChannel = shouldDefault ? 0 : GetChannelId(channel);
break;
case "botlogchannel":
config.BotLogChannel = shouldDefault ? 0 : GetChannelId(channel);
break;
default:
await context.Channel.SendMessageAsync(Messages.SettingDoesntExist);
return;
} }
config.Validate();
await config.Save(); await config.Save();
await context.Channel.SendMessageAsync(Messages.SettingsUpdated); await context.Channel.SendMessageAsync(Messages.SettingsUpdated);
} }
private static bool? ParseBool(string toParse) {
try {
return bool.Parse(toParse.ToLower());
} catch (FormatException) {
return null;
}
}
private static bool GetBoolValue(bool? from) {
return from ?? throw new ApplicationException(Messages.InvalidBoolean);
}
private static ulong GetRoleId(IRole? role) {
return (role ?? throw new ApplicationException(Messages.InvalidRoleSpecified)).Id;
}
private static ulong GetChannelId(IGuildChannel? channel) {
return (channel ?? throw new ApplicationException(Messages.InvalidChannelSpecified)).Id;
}
private static string YesOrNo(bool isYes) { private static string YesOrNo(bool isYes) {
return isYes ? Messages.Yes : Messages.No; return isYes ? Messages.Yes : Messages.No;
} }
@ -142,4 +108,4 @@ public class SettingsCommand : Command {
public override string GetSummary() { public override string GetSummary() {
return "Настраивает бота отдельно для этого сервера"; return "Настраивает бота отдельно для этого сервера";
} }
} }

View file

@ -36,12 +36,10 @@ public class EventHandler {
Cacheable<IMessageChannel, ulong> channel) { Cacheable<IMessageChannel, ulong> channel) {
var msg = message.Value; var msg = message.Value;
var toSend = msg == null var toSend = msg == null ? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id))
? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id))
: string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) + : string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) +
$"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}"; $"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}";
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel( await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel.Value)), toSend);
Boyfriend.FindGuild(channel.Value)), toSend);
} }
private static async Task MessageReceivedEvent(SocketMessage messageParam) { private static async Task MessageReceivedEvent(SocketMessage messageParam) {
@ -58,21 +56,20 @@ public class EventHandler {
Messages.Culture = new CultureInfo(guildConfig.Lang!); Messages.Culture = new CultureInfo(guildConfig.Lang!);
if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) &&
&& !user.GuildPermissions.MentionEveryone) !user.GuildPermissions.MentionEveryone)
await BanCommand.BanUser(guild, null, await guild.GetCurrentUserAsync(), user, await BanCommand.BanUser(guild, null, await guild.GetCurrentUserAsync(), user,
TimeSpan.FromMilliseconds(-1), Messages.AutobanReason); TimeSpan.FromMilliseconds(-1), Messages.AutobanReason);
try { try {
prev = prevsArray[1].Content; prev = prevsArray[1].Content;
prevFailsafe = prevsArray[2].Content; prevFailsafe = prevsArray[2].Content;
} } catch (IndexOutOfRangeException) {}
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 == await guild.GetCurrentUserAsync() user == await guild.GetCurrentUserAsync() ||
|| user.IsBot && (message.Content.Contains(prev) || message.Content.Contains(prevFailsafe))) user.IsBot && (message.Content.Contains(prev) || message.Content.Contains(prevFailsafe)))
return; return;
await CommandHandler.HandleCommand(message); await CommandHandler.HandleCommand(message);
@ -87,12 +84,10 @@ public class EventHandler {
var toSend = msg == null var toSend = msg == null
? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention, ? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention,
Utils.MentionChannel(channel.Id)) + Utils.MentionChannel(channel.Id)) + Utils.Wrap(messageSocket.Content) : string.Format(
Utils.Wrap(messageSocket.Content) Messages.CachedMessageEdited, msg.Author.Mention, Utils.MentionChannel(channel.Id), nl, nl,
: string.Format(Messages.CachedMessageEdited, msg.Author.Mention, Utils.MentionChannel(channel.Id), nl, nl,
Utils.Wrap(msg.Content), nl, nl, Utils.Wrap(messageSocket.Content)); Utils.Wrap(msg.Content), nl, nl, Utils.Wrap(messageSocket.Content));
await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel)), await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel)), toSend);
toSend);
} }
private static async Task UserJoinedEvent(SocketGuildUser user) { private static async Task UserJoinedEvent(SocketGuildUser user) {
@ -100,10 +95,10 @@ public class EventHandler {
var config = Boyfriend.GetGuildConfig(guild); var config = Boyfriend.GetGuildConfig(guild);
if (config.SendWelcomeMessages.GetValueOrDefault(true)) if (config.SendWelcomeMessages.GetValueOrDefault(true))
await Utils.SilentSendAsync(guild.SystemChannel, string.Format(config.WelcomeMessage!, user.Mention, await Utils.SilentSendAsync(guild.SystemChannel,
guild.Name)); string.Format(config.WelcomeMessage!, user.Mention, guild.Name));
if (config.DefaultRole != 0) if (config.DefaultRole != 0)
await user.AddRoleAsync(Utils.ParseRole(guild, config.DefaultRole.ToString()!)); await user.AddRoleAsync(Utils.ParseRole(guild, config.DefaultRole.ToString()!));
} }
} }

View file

@ -1,9 +1,15 @@
using System.Globalization; using System.Globalization;
using Newtonsoft.Json; using Newtonsoft.Json;
// ReSharper disable MemberCanBePrivate.Global
namespace Boyfriend; namespace Boyfriend;
public class GuildConfig { public class GuildConfig {
public GuildConfig(ulong id) {
Id = id;
Validate();
}
public ulong? Id { get; } public ulong? Id { get; }
public string? Lang { get; set; } public string? Lang { get; set; }
public string? Prefix { get; set; } public string? Prefix { get; set; }
@ -22,11 +28,6 @@ public class GuildConfig {
public Dictionary<ulong, List<ulong>>? RolesRemovedOnMute { get; private set; } public Dictionary<ulong, List<ulong>>? RolesRemovedOnMute { get; private set; }
public GuildConfig(ulong id) {
Id = id;
Validate();
}
public void Validate() { public void Validate() {
if (Id == null) throw new Exception("Something went horribly, horribly wrong"); if (Id == null) throw new Exception("Something went horribly, horribly wrong");

View file

@ -60,15 +60,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Arguments not present! {0}.
/// </summary>
internal static string ArgumentNotPresent {
get {
return ResourceManager.GetString("ArgumentNotPresent", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Too many mentions in 1 message. /// Looks up a localized string similar to Too many mentions in 1 message.
/// </summary> /// </summary>
@ -78,15 +69,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Invalid argument count! {0}.
/// </summary>
internal static string BadArgumentCount {
get {
return ResourceManager.GetString("BadArgumentCount", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully banned {0} for {1}. /// Looks up a localized string similar to :white_check_mark: Successfully banned {0} for {1}.
/// </summary> /// </summary>
@ -141,6 +123,15 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to I cannot use time-outs on other bots! Try to set a mute role in settings.
/// </summary>
internal static string CannotTimeOutBot {
get {
return ResourceManager.GetString("CannotTimeOutBot", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Not specified. /// Looks up a localized string similar to Not specified.
/// </summary> /// </summary>
@ -177,15 +168,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Command execution was unsuccessful: {0}.
/// </summary>
internal static string CommandExecutionUnsuccessful {
get {
return ResourceManager.GetString("CommandExecutionUnsuccessful", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Command help:{0}. /// Looks up a localized string similar to Command help:{0}.
/// </summary> /// </summary>
@ -213,15 +195,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Command parsing failed: {0}.
/// </summary>
internal static string CommandParseFailed {
get {
return ResourceManager.GetString("CommandParseFailed", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Couldn&apos;t find guild by message!. /// Looks up a localized string similar to Couldn&apos;t find guild by message!.
/// </summary> /// </summary>
@ -348,6 +321,24 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to I couldn&apos;t parse the specified duration! One of the components could be outside it&apos;s valid range (e.g. `24h` or `60m`).
/// </summary>
internal static string DurationParseFailed {
get {
return ResourceManager.GetString("DurationParseFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I cannot mute someone forever using timeouts! Either specify a proper duration, or set a mute role in settings.
/// </summary>
internal static string DurationRequiredForTimeOuts {
get {
return ResourceManager.GetString("DurationRequiredForTimeOuts", 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>
@ -403,38 +394,29 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid admin log channel for guild. /// Looks up a localized string similar to This channel does not exist!.
/// </summary> /// </summary>
internal static string InvalidAdminLogChannel { internal static string InvalidChannel {
get { get {
return ResourceManager.GetString("InvalidAdminLogChannel", resourceCulture); return ResourceManager.GetString("InvalidChannel", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid argument! &apos;true&apos; or &apos;false&apos; required!. /// Looks up a localized string similar to This role does not exist!.
/// </summary> /// </summary>
internal static string InvalidBoolean { internal static string InvalidRole {
get { get {
return ResourceManager.GetString("InvalidBoolean", resourceCulture); return ResourceManager.GetString("InvalidRole", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid channel specified!. /// Looks up a localized string similar to Invalid setting value specified!.
/// </summary> /// </summary>
internal static string InvalidChannelSpecified { internal static string InvalidSettingValue {
get { get {
return ResourceManager.GetString("InvalidChannelSpecified", resourceCulture); return ResourceManager.GetString("InvalidSettingValue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid role specified!.
/// </summary>
internal static string InvalidRoleSpecified {
get {
return ResourceManager.GetString("InvalidRoleSpecified", resourceCulture);
} }
} }
@ -528,24 +510,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Someone removed the mute role manually!.
/// </summary>
internal static string MuteRoleManuallyRemoved {
get {
return ResourceManager.GetString("MuteRoleManuallyRemoved", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You must set up a mute role in settings!.
/// </summary>
internal static string MuteRoleRequired {
get {
return ResourceManager.GetString("MuteRoleRequired", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to No. /// Looks up a localized string similar to No.
/// </summary> /// </summary>
@ -591,15 +555,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Repeated arguments detected! {0}.
/// </summary>
internal static string RepeatedArgumentsDetected {
get {
return ResourceManager.GetString("RepeatedArgumentsDetected", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Not specified. /// Looks up a localized string similar to Not specified.
/// </summary> /// </summary>
@ -609,6 +564,15 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to I couldn&apos;t remove role {0} because of an error! {1}.
/// </summary>
internal static string RoleRemovalFailed {
get {
return ResourceManager.GetString("RoleRemovalFailed", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Someone removed the mute role manually! I added back all roles that I removed during the mute. /// Looks up a localized string similar to Someone removed the mute role manually! I added back all roles that I removed during the mute.
/// </summary> /// </summary>
@ -655,7 +619,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: .
/// </summary> /// </summary>
internal static string UncachedMessageEdited { internal static string UncachedMessageEdited {
get { get {
@ -663,15 +627,6 @@ namespace Boyfriend {
} }
} }
/// <summary>
/// Looks up a localized string similar to Unknown command! {0}.
/// </summary>
internal static string UnknownCommand {
get {
return ResourceManager.GetString("UnknownCommand", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to :white_check_mark: Successfully unmuted {0} for {1}. /// Looks up a localized string similar to :white_check_mark: Successfully unmuted {0} for {1}.
/// </summary> /// </summary>

View file

@ -40,7 +40,7 @@
<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: </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>Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7}</value>
@ -57,30 +57,6 @@
<data name="Beep3" xml:space="preserve"> <data name="Beep3" xml:space="preserve">
<value>Beep! </value> <value>Beep! </value>
</data> </data>
<data name="InvalidAdminLogChannel" xml:space="preserve">
<value>Invalid admin log channel for guild</value>
</data>
<data name="MuteRoleRequired" xml:space="preserve">
<value>You must set up a mute role in settings!</value>
</data>
<data name="CommandExecutionUnsuccessful" xml:space="preserve">
<value>Command execution was unsuccessful: {0}</value>
</data>
<data name="RepeatedArgumentsDetected" xml:space="preserve">
<value>Repeated arguments detected! {0}</value>
</data>
<data name="CommandParseFailed" xml:space="preserve">
<value>Command parsing failed: {0}</value>
</data>
<data name="UnknownCommand" xml:space="preserve">
<value>Unknown command! {0}</value>
</data>
<data name="BadArgumentCount" xml:space="preserve">
<value>Invalid argument count! {0}</value>
</data>
<data name="ArgumentNotPresent" xml:space="preserve">
<value>Arguments not present! {0}</value>
</data>
<data name="CommandNoPermissionBot" xml:space="preserve"> <data name="CommandNoPermissionBot" xml:space="preserve">
<value>I do not have permission to execute this command!</value> <value>I do not have permission to execute this command!</value>
</data> </data>
@ -141,9 +117,6 @@
<data name="MemberAlreadyMuted" xml:space="preserve"> <data name="MemberAlreadyMuted" xml:space="preserve">
<value>Member is already muted!</value> <value>Member is already muted!</value>
</data> </data>
<data name="MuteRoleManuallyRemoved" xml:space="preserve">
<value>Someone removed the mute role manually!</value>
</data>
<data name="ChannelNotSpecified" xml:space="preserve"> <data name="ChannelNotSpecified" xml:space="preserve">
<value>Not specified</value> <value>Not specified</value>
</data> </data>
@ -186,15 +159,6 @@
<data name="SettingsUpdated" xml:space="preserve"> <data name="SettingsUpdated" xml:space="preserve">
<value>Settings successfully updated</value> <value>Settings successfully updated</value>
</data> </data>
<data name="InvalidBoolean" xml:space="preserve">
<value>Invalid argument! 'true' or 'false' required!</value>
</data>
<data name="InvalidRoleSpecified" xml:space="preserve">
<value>Invalid role specified!</value>
</data>
<data name="InvalidChannelSpecified" xml:space="preserve">
<value>Invalid channel specified!</value>
</data>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>Yes</value> <value>Yes</value>
</data> </data>
@ -252,4 +216,25 @@
<data name="PunishmentExpiresIn" xml:space="preserve"> <data name="PunishmentExpiresIn" xml:space="preserve">
<value>{0}This punishment will expire &lt;t:{1}:R&gt;</value> <value>{0}This punishment will expire &lt;t:{1}:R&gt;</value>
</data> </data>
<data name="InvalidSettingValue" xml:space="preserve">
<value>Invalid setting value specified!</value>
</data>
<data name="InvalidRole" xml:space="preserve">
<value>This role does not exist!</value>
</data>
<data name="InvalidChannel" xml:space="preserve">
<value>This channel does not exist!</value>
</data>
<data name="DurationParseFailed" xml:space="preserve">
<value>I couldn't parse the specified duration! One of the components could be outside it's valid range (e.g. `24h` or `60m`)</value>
</data>
<data name="RoleRemovalFailed" xml:space="preserve">
<value>I couldn't remove role {0} because of an error! {1}</value>
</data>
<data name="DurationRequiredForTimeOuts" xml:space="preserve">
<value>I cannot mute someone forever using timeouts! Either specify a proper duration, or set a mute role in settings</value>
</data>
<data name="CannotTimeOutBot" xml:space="preserve">
<value>I cannot use time-outs on other bots! Try to set a mute role in settings</value>
</data>
</root> </root>

View file

@ -31,7 +31,7 @@
<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}, но я забыл что там было до редактирования: </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}.{2}До:{3}{4}{5}После:{6}{7}</value>
@ -48,30 +48,6 @@
<data name="Beep3" xml:space="preserve"> <data name="Beep3" xml:space="preserve">
<value>Бип! </value> <value>Бип! </value>
</data> </data>
<data name="MuteRoleRequired" xml:space="preserve">
<value>Требуется указать роль мута в настройках!</value>
</data>
<data name="InvalidAdminLogChannel" xml:space="preserve">
<value>Неверный канал админ-логов для гильдии </value>
</data>
<data name="CommandExecutionUnsuccessful" xml:space="preserve">
<value>Выполнение команды завершилось неудачей: {0}</value>
</data>
<data name="RepeatedArgumentsDetected" xml:space="preserve">
<value>Обнаружены повторяющиеся типы аргументов! {0}</value>
</data>
<data name="CommandParseFailed" xml:space="preserve">
<value>Не удалось обработать команду: {0}</value>
</data>
<data name="UnknownCommand" xml:space="preserve">
<value>Неизвестная команда! {0}</value>
</data>
<data name="BadArgumentCount" xml:space="preserve">
<value>Неверное количество аргументов! {0}</value>
</data>
<data name="ArgumentNotPresent" xml:space="preserve">
<value>Нету нужных аргументов! {0}</value>
</data>
<data name="CommandNoPermissionBot" xml:space="preserve"> <data name="CommandNoPermissionBot" xml:space="preserve">
<value>У меня недостаточно прав для выполнения этой команды!</value> <value>У меня недостаточно прав для выполнения этой команды!</value>
</data> </data>
@ -132,9 +108,6 @@
<data name="MemberAlreadyMuted" xml:space="preserve"> <data name="MemberAlreadyMuted" xml:space="preserve">
<value>Участник уже заглушен!</value> <value>Участник уже заглушен!</value>
</data> </data>
<data name="MuteRoleManuallyRemoved" xml:space="preserve">
<value>Кто-то убрал роль мута самостоятельно!</value>
</data>
<data name="ChannelNotSpecified" xml:space="preserve"> <data name="ChannelNotSpecified" xml:space="preserve">
<value>Не указан</value> <value>Не указан</value>
</data> </data>
@ -177,15 +150,6 @@
<data name="SettingsUpdated" xml:space="preserve"> <data name="SettingsUpdated" xml:space="preserve">
<value>Настройки успешно обновлены!</value> <value>Настройки успешно обновлены!</value>
</data> </data>
<data name="InvalidBoolean" xml:space="preserve">
<value>Неверный параметр! Требуется 'true' или 'false'</value>
</data>
<data name="InvalidRoleSpecified" xml:space="preserve">
<value>Указана недействительная роль!</value>
</data>
<data name="InvalidChannelSpecified" xml:space="preserve">
<value>Указан недействильный канал!</value>
</data>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>Да</value> <value>Да</value>
</data> </data>
@ -243,4 +207,25 @@
<data name="PunishmentExpiresIn" xml:space="preserve"> <data name="PunishmentExpiresIn" xml:space="preserve">
<value>{0}Это наказание истечёт &lt;t:{1}:R&gt;</value> <value>{0}Это наказание истечёт &lt;t:{1}:R&gt;</value>
</data> </data>
<data name="InvalidSettingValue" xml:space="preserve">
<value>Указано недействительное значение для настройки!</value>
</data>
<data name="InvalidRole" xml:space="preserve">
<value>Эта роль не существует!</value>
</data>
<data name="InvalidChannel" xml:space="preserve">
<value>Этот канал не существует!</value>
</data>
<data name="DurationParseFailed" xml:space="preserve">
<value>Мне не удалось обработать продолжительность! Один из компонентов может быть за пределами допустимого диапазона (например, `24ч` или `60м`)</value>
</data>
<data name="RoleRemovalFailed" xml:space="preserve">
<value>Я не смог забрать роль {0} в связи с ошибкой! {1}</value>
</data>
<data name="DurationRequiredForTimeOuts" xml:space="preserve">
<value>Я не могу заглушить кого-то навсегда, используя тайм-ауты! Или укажи правильную продолжительность, или установи роль мута в настройках</value>
</data>
<data name="CannotTimeOutBot" xml:space="preserve">
<value>Я не могу использовать тайм-ауты на других ботах! Попробуй указать роль мута в настройках</value>
</data>
</root> </root>

View file

@ -27,12 +27,12 @@ public static class Utils {
} }
public static string Wrap(string original) { public static string Wrap(string original) {
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}`"; return $"`{original.Replace("`", "ˋ")}`";
} }
public static string MentionChannel(ulong id) { public static string MentionChannel(ulong id) {
@ -64,7 +64,7 @@ public static class Utils {
return await Boyfriend.Client.GetChannelAsync(ParseMention(mention)); return await Boyfriend.Client.GetChannelAsync(ParseMention(mention));
} }
public static async Task<IChannel?> ParseChannelNullable(string mention) { private static async Task<IChannel?> ParseChannelNullable(string mention) {
return ParseMentionNullable(mention) == null ? null : await ParseChannel(mention); return ParseMentionNullable(mention) == null ? null : await ParseChannel(mention);
} }
@ -72,10 +72,6 @@ public static class Utils {
return guild.GetRole(ParseMention(mention)); return guild.GetRole(ParseMention(mention));
} }
public static IRole? ParseRoleNullable(IGuild guild, string mention) {
return ParseMentionNullable(mention) == null ? null : ParseRole(guild, mention);
}
public static async Task SendDirectMessage(IUser user, string toSend) { public static async Task SendDirectMessage(IUser user, string toSend) {
try { try {
await user.SendMessageAsync(toSend); await user.SendMessageAsync(toSend);
@ -98,8 +94,7 @@ public static class Utils {
} catch (ArgumentException) {} } catch (ArgumentException) {}
} }
public static TimeSpan GetTimeSpan(string from) { public static TimeSpan GetTimeSpan(string from) {
return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats, return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture);
CultureInfo.InvariantCulture);
} }
public static string JoinString(string[] args, int startIndex) { public static string JoinString(string[] args, int startIndex) {
@ -115,4 +110,4 @@ public static class Utils {
options.AuditLogReason = reason; options.AuditLogReason = reason;
return options; return options;
} }
} }