mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-19 16:33:36 +03:00
Begin adapting code to new guild data storage
This commit is contained in:
parent
f5a81fba35
commit
163e3ac46b
8 changed files with 56 additions and 167 deletions
|
@ -1,3 +1,4 @@
|
|||
using Boyfriend.Data;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
|
@ -32,7 +33,7 @@ public sealed class BanCommand : ICommand {
|
|||
cmd.Reply(feedback, ReplyEmojis.Banned);
|
||||
cmd.Audit(feedback);
|
||||
|
||||
if (duration.TotalSeconds > 0)
|
||||
await Task.FromResult(Utils.DelayedUnbanAsync(cmd, toBan.Id, Messages.PunishmentExpired, duration));
|
||||
GuildData.FromSocketGuild(guild).MemberData[toBan.Id].BannedUntil
|
||||
= DateTimeOffset.Now.Add(duration).ToUnixTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Boyfriend.Data;
|
||||
using Humanizer;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -6,7 +7,7 @@ public sealed class HelpCommand : ICommand {
|
|||
public string[] Aliases { get; } = { "help", "помощь", "справка" };
|
||||
|
||||
public Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
||||
var prefix = Boyfriend.GetGuildConfig(cmd.Context.Guild.Id)["Prefix"];
|
||||
var prefix = GuildData.FromSocketGuild(cmd.Context.Guild).Preferences["Prefix"];
|
||||
var toSend = Boyfriend.StringBuilder.Append(Messages.CommandHelp);
|
||||
|
||||
foreach (var command in CommandProcessor.Commands)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Boyfriend.Data;
|
||||
using Discord;
|
||||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace Boyfriend.Commands;
|
||||
|
@ -14,7 +14,8 @@ public sealed class MuteCommand : ICommand {
|
|||
var duration = CommandProcessor.GetTimeSpan(args, 1);
|
||||
var reason = cmd.GetRemaining(args, duration.TotalSeconds < 1 ? 1 : 2, "MuteReason");
|
||||
if (reason is null) return;
|
||||
var role = Utils.GetMuteRole(cmd.Context.Guild);
|
||||
var guildData = GuildData.FromSocketGuild(cmd.Context.Guild);
|
||||
var role = guildData.MuteRole;
|
||||
|
||||
if ((role is not null && toMute.Roles.Contains(role))
|
||||
|| (toMute.TimedOutUntil is not null
|
||||
|
@ -24,48 +25,22 @@ public sealed class MuteCommand : ICommand {
|
|||
return;
|
||||
}
|
||||
|
||||
var rolesRemoved = Boyfriend.GetRemovedRoles(cmd.Context.Guild.Id);
|
||||
|
||||
if (rolesRemoved.TryGetValue(toMute.Id, out var mutedRemovedRoles)) {
|
||||
foreach (var roleId in mutedRemovedRoles) await toMute.AddRoleAsync(roleId);
|
||||
rolesRemoved.Remove(toMute.Id);
|
||||
cmd.ConfigWriteScheduled = true;
|
||||
cmd.Reply(Messages.RolesReturned, ReplyEmojis.Warning);
|
||||
}
|
||||
|
||||
if (cmd.HasPermission(GuildPermission.ModerateMembers) && cmd.CanInteractWith(toMute, "Mute"))
|
||||
await MuteMemberAsync(cmd, toMute, duration, reason);
|
||||
await MuteMemberAsync(cmd, toMute, duration, guildData, reason);
|
||||
}
|
||||
|
||||
private static async Task MuteMemberAsync(CommandProcessor cmd, SocketGuildUser toMute,
|
||||
TimeSpan duration, string reason) {
|
||||
var guild = cmd.Context.Guild;
|
||||
var config = Boyfriend.GetGuildConfig(guild.Id);
|
||||
TimeSpan duration, GuildData data, string reason) {
|
||||
var requestOptions = Utils.GetRequestOptions($"({cmd.Context.User}) {reason}");
|
||||
var role = Utils.GetMuteRole(guild);
|
||||
var role = data.MuteRole;
|
||||
var hasDuration = duration.TotalSeconds > 0;
|
||||
|
||||
if (role is not null) {
|
||||
if (config["RemoveRolesOnMute"] is "true") {
|
||||
var rolesRemoved = new List<ulong>();
|
||||
foreach (var userRole in toMute.Roles)
|
||||
try {
|
||||
if (userRole == guild.EveryoneRole || userRole == role) continue;
|
||||
await toMute.RemoveRoleAsync(role);
|
||||
rolesRemoved.Add(userRole.Id);
|
||||
} catch (HttpException e) {
|
||||
cmd.Reply(string.Format(Messages.RoleRemovalFailed, $"<@&{userRole}>", Utils.Wrap(e.Reason)),
|
||||
ReplyEmojis.Warning);
|
||||
}
|
||||
|
||||
Boyfriend.GetRemovedRoles(guild.Id).Add(toMute.Id, rolesRemoved.AsReadOnly());
|
||||
cmd.ConfigWriteScheduled = true;
|
||||
}
|
||||
if (data.Preferences["RemoveRolesOnMute"] is "true") await toMute.RemoveRolesAsync(toMute.Roles);
|
||||
|
||||
await toMute.AddRoleAsync(role, requestOptions);
|
||||
|
||||
if (hasDuration)
|
||||
await Task.FromResult(Utils.DelayedUnmuteAsync(cmd, toMute, Messages.PunishmentExpired, duration));
|
||||
data.MemberData[toMute.Id].MutedUntil = DateTimeOffset.Now.Add(duration).ToUnixTimeSeconds();
|
||||
} else {
|
||||
if (!hasDuration || duration.TotalDays > 28) {
|
||||
cmd.Reply(Messages.DurationRequiredForTimeOuts, ReplyEmojis.Error);
|
||||
|
|
|
@ -10,7 +10,8 @@ public sealed class SettingsCommand : ICommand {
|
|||
if (!cmd.HasPermission(GuildPermission.ManageGuild)) return Task.CompletedTask;
|
||||
|
||||
var guild = cmd.Context.Guild;
|
||||
var config = Boyfriend.GetGuildConfig(guild.Id);
|
||||
var data = GuildData.FromSocketGuild(guild);
|
||||
var config = data.Preferences;
|
||||
|
||||
if (args.Length is 0) {
|
||||
var currentSettings = Boyfriend.StringBuilder.AppendLine(Messages.CurrentSettings);
|
||||
|
@ -132,7 +133,7 @@ public sealed class SettingsCommand : ICommand {
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (selectedSetting is "MuteRole") Utils.RemoveMuteRoleFromCache(ulong.Parse(config[selectedSetting]));
|
||||
if (selectedSetting is "MuteRole") data.MuteRole = guild.GetRole(mention);
|
||||
|
||||
config[selectedSetting] = value;
|
||||
}
|
||||
|
@ -158,4 +159,3 @@ public sealed class SettingsCommand : ICommand {
|
|||
return value is "true" or "false";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Boyfriend.Data;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
|
@ -19,18 +20,10 @@ public sealed class UnmuteCommand : ICommand {
|
|||
public static async Task UnmuteMemberAsync(CommandProcessor cmd, SocketGuildUser toUnmute,
|
||||
string reason) {
|
||||
var requestOptions = Utils.GetRequestOptions($"({cmd.Context.User}) {reason}");
|
||||
var role = Utils.GetMuteRole(cmd.Context.Guild);
|
||||
var role = GuildData.FromSocketGuild(cmd.Context.Guild).MuteRole;
|
||||
|
||||
if (role is not null && toUnmute.Roles.Contains(role)) {
|
||||
var rolesRemoved = Boyfriend.GetRemovedRoles(cmd.Context.Guild.Id);
|
||||
|
||||
if (rolesRemoved.TryGetValue(toUnmute.Id, out var unmutedRemovedRoles)) {
|
||||
await toUnmute.AddRolesAsync(unmutedRemovedRoles);
|
||||
rolesRemoved.Remove(toUnmute.Id);
|
||||
cmd.ConfigWriteScheduled = true;
|
||||
}
|
||||
|
||||
await toUnmute.RemoveRoleAsync(role, requestOptions);
|
||||
// TODO: Return roles
|
||||
} else {
|
||||
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value.ToUnixTimeSeconds() <
|
||||
DateTimeOffset.Now.ToUnixTimeSeconds()) {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Boyfriend.Data;
|
||||
|
||||
public struct GuildData {
|
||||
public record GuildData {
|
||||
public static readonly Dictionary<string, string> DefaultConfiguration = new() {
|
||||
{ "Prefix", "!" },
|
||||
{ "Lang", "en" },
|
||||
|
@ -26,81 +25,46 @@ public struct GuildData {
|
|||
|
||||
private static readonly Dictionary<ulong, GuildData> GuildDataDictionary = new();
|
||||
|
||||
public readonly Dictionary<string, string> GuildConfiguration;
|
||||
|
||||
public readonly Dictionary<ulong, MemberData> MemberData;
|
||||
|
||||
/*public static Dictionary<string, string> GetGuildConfig(ulong id) {
|
||||
if (GuildConfigDictionary.TryGetValue(id, out var cfg)) return cfg;
|
||||
public readonly Dictionary<string, string> Preferences;
|
||||
|
||||
var path = $"config_{id}.json";
|
||||
private SocketRole? _cachedMuteRole;
|
||||
|
||||
if (!File.Exists(path)) File.Create(path).Dispose();
|
||||
private ulong _id;
|
||||
|
||||
var json = File.ReadAllText(path);
|
||||
var config = JsonConvert.DeserializeObject<Dictionary<string, string>>(json)
|
||||
?? new Dictionary<string, string>();
|
||||
|
||||
if (config.Keys.Count < GuildData.DefaultConfiguration.Keys.Count) {
|
||||
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
// Conversion will result in a lot of memory allocations
|
||||
foreach (var key in GuildData.DefaultConfiguration.Keys)
|
||||
if (!config.ContainsKey(key))
|
||||
config.Add(key, GuildData.DefaultConfiguration[key]);
|
||||
} else if (config.Keys.Count > GuildData.DefaultConfiguration.Keys.Count) {
|
||||
foreach (var key in config.Keys.Where(key => !GuildData.DefaultConfiguration.ContainsKey(key))) config.Remove(key);
|
||||
}
|
||||
|
||||
GuildConfigDictionary.Add(id, config);
|
||||
|
||||
return config;
|
||||
}*/
|
||||
|
||||
/*public static async Task WriteGuildConfigAsync(ulong id) {
|
||||
await File.WriteAllTextAsync($"config_{id}.json",
|
||||
JsonConvert.SerializeObject(GuildConfigDictionary[id], Formatting.Indented));
|
||||
|
||||
if (RemovedRolesDictionary.TryGetValue(id, out var removedRoles))
|
||||
await File.WriteAllTextAsync($"removedroles_{id}.json",
|
||||
JsonConvert.SerializeObject(removedRoles, Formatting.Indented));
|
||||
}*/
|
||||
[SuppressMessage("Performance", "CA1853:Unnecessary call to \'Dictionary.ContainsKey(key)\'")]
|
||||
// https://github.com/dotnet/roslyn-analyzers/issues/6377
|
||||
public GuildData(SocketGuild guild) {
|
||||
private GuildData(SocketGuild guild) {
|
||||
var id = guild.Id;
|
||||
if (GuildDataDictionary.TryGetValue(id, out var stored)) {
|
||||
this = stored;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists($"{id}")) Directory.CreateDirectory($"{id}");
|
||||
if (!Directory.Exists($"{id}/MemberData")) Directory.CreateDirectory($"{id}/MemberData");
|
||||
if (!File.Exists($"{id}/Configuration.json")) File.Create($"{id}/Configuration.json").Dispose();
|
||||
GuildConfiguration = JsonConvert.DeserializeObject<Dictionary<string, string>>($"{id}/Configuration.json") ??
|
||||
new Dictionary<string, string>();
|
||||
Preferences
|
||||
= JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText($"{id}/Configuration.json")) ??
|
||||
new Dictionary<string, string>();
|
||||
|
||||
// ReSharper disable twice ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
if (GuildConfiguration.Keys.Count < DefaultConfiguration.Keys.Count)
|
||||
if (Preferences.Keys.Count < DefaultConfiguration.Keys.Count)
|
||||
foreach (var key in DefaultConfiguration.Keys)
|
||||
if (!GuildConfiguration.ContainsKey(key))
|
||||
GuildConfiguration.Add(key, DefaultConfiguration[key]);
|
||||
if (GuildConfiguration.Keys.Count > DefaultConfiguration.Keys.Count)
|
||||
foreach (var key in GuildConfiguration.Keys)
|
||||
if (!Preferences.ContainsKey(key))
|
||||
Preferences.Add(key, DefaultConfiguration[key]);
|
||||
if (Preferences.Keys.Count > DefaultConfiguration.Keys.Count)
|
||||
foreach (var key in Preferences.Keys)
|
||||
if (!DefaultConfiguration.ContainsKey(key))
|
||||
GuildConfiguration.Remove(key);
|
||||
GuildConfiguration.TrimExcess();
|
||||
Preferences.Remove(key);
|
||||
Preferences.TrimExcess();
|
||||
|
||||
MemberData = new Dictionary<ulong, MemberData>();
|
||||
foreach (var data in Directory.GetFiles($"{id}/MemberData")) {
|
||||
var deserialised = JsonConvert.DeserializeObject<MemberData>($"{id}/MemberData/{data}.json") ??
|
||||
throw new UnreachableException();
|
||||
MemberData.Add(deserialised.Id, deserialised);
|
||||
var deserialised = JsonSerializer.Deserialize<MemberData>(File.ReadAllText($"{id}/MemberData/{data}.json"));
|
||||
MemberData.Add(deserialised!.Id, deserialised);
|
||||
}
|
||||
|
||||
if (guild.MemberCount > MemberData.Count)
|
||||
foreach (var member in guild.Users) {
|
||||
if (MemberData.TryGetValue(member.Id, out var memberData)) {
|
||||
if (memberData is { IsInGuild: false } &&
|
||||
if (!memberData.IsInGuild &&
|
||||
DateTimeOffset.Now.ToUnixTimeSeconds() -
|
||||
Math.Max(memberData.LeftAt.Last(), memberData.BannedUntil) >
|
||||
60 * 60 * 24 * 30) {
|
||||
|
@ -114,9 +78,24 @@ public struct GuildData {
|
|||
var data = new MemberData(member);
|
||||
MemberData.Add(member.Id, data);
|
||||
File.WriteAllText($"{id}/MemberData/{data.Id}.json",
|
||||
JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
JsonSerializer.Serialize(data));
|
||||
}
|
||||
|
||||
GuildDataDictionary.Add(id, this);
|
||||
}
|
||||
|
||||
public SocketRole? MuteRole {
|
||||
get => _cachedMuteRole ??= Boyfriend.Client.GetGuild(_id).Roles
|
||||
.Single(x => x.Id == ulong.Parse(Preferences["MuteRole"]));
|
||||
set => _cachedMuteRole = value;
|
||||
}
|
||||
|
||||
public static GuildData FromSocketGuild(SocketGuild guild) {
|
||||
if (GuildDataDictionary.TryGetValue(guild.Id, out var stored)) return stored;
|
||||
var newData = new GuildData(guild) {
|
||||
_id = guild.Id
|
||||
};
|
||||
GuildDataDictionary.Add(guild.Id, newData);
|
||||
return newData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Boyfriend.Data;
|
||||
|
||||
public struct Reminder {
|
||||
public DateTimeOffset RemindAt;
|
||||
public long RemindAt;
|
||||
public string ReminderText;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Globalization;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Boyfriend.Commands;
|
||||
using Discord;
|
||||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
|
@ -21,8 +20,6 @@ public static partial class Utils {
|
|||
|
||||
private static readonly Dictionary<string, string> ReflectionMessageCache = new();
|
||||
|
||||
private static readonly Dictionary<ulong, SocketRole> MuteRoleCache = new();
|
||||
|
||||
private static readonly AllowedMentions AllowRoles = new() {
|
||||
AllowedTypes = AllowedMentionTypes.Roles
|
||||
};
|
||||
|
@ -58,22 +55,6 @@ public static partial class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
public static SocketRole? GetMuteRole(SocketGuild guild) {
|
||||
var id = ulong.Parse(Boyfriend.GetGuildConfig(guild.Id)["MuteRole"]);
|
||||
if (MuteRoleCache.TryGetValue(id, out var cachedMuteRole)) return cachedMuteRole;
|
||||
foreach (var x in guild.Roles) {
|
||||
if (x.Id != id) continue;
|
||||
MuteRoleCache.Add(id, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RemoveMuteRoleFromCache(ulong id) {
|
||||
MuteRoleCache.Remove(id);
|
||||
}
|
||||
|
||||
public static async Task SilentSendAsync(SocketTextChannel? channel, string text, bool allowRoles = false) {
|
||||
try {
|
||||
if (channel is null || text.Length is 0 or > 2000)
|
||||
|
@ -147,46 +128,6 @@ public static partial class Utils {
|
|||
appendTo.AppendLine(appendWhat);
|
||||
}
|
||||
|
||||
public static async Task DelayedUnbanAsync(CommandProcessor cmd, ulong banned, string reason, TimeSpan duration) {
|
||||
await Task.Delay(duration);
|
||||
SetCurrentLanguage(cmd.Context.Guild.Id);
|
||||
await UnbanCommand.UnbanUserAsync(cmd, banned, reason);
|
||||
}
|
||||
|
||||
public static async Task DelayedUnmuteAsync(CommandProcessor cmd, SocketGuildUser muted, string reason,
|
||||
TimeSpan duration) {
|
||||
await Task.Delay(duration);
|
||||
SetCurrentLanguage(cmd.Context.Guild.Id);
|
||||
await UnmuteCommand.UnmuteMemberAsync(cmd, muted, reason);
|
||||
}
|
||||
|
||||
public static async Task SendEarlyEventStartNotificationAsync(SocketTextChannel? channel,
|
||||
SocketGuildEvent scheduledEvent, int minuteOffset) {
|
||||
try {
|
||||
await Task.Delay(scheduledEvent.StartTime.Subtract(DateTimeOffset.Now)
|
||||
.Subtract(TimeSpan.FromMinutes(minuteOffset)));
|
||||
var guild = scheduledEvent.Guild;
|
||||
if (guild.GetEvent(scheduledEvent.Id) is null) return;
|
||||
var eventConfig = Boyfriend.GetGuildConfig(guild.Id);
|
||||
SetCurrentLanguage(guild.Id);
|
||||
|
||||
var receivers = eventConfig["EventStartedReceivers"];
|
||||
var role = guild.GetRole(ulong.Parse(eventConfig["EventNotificationRole"]));
|
||||
var mentions = Boyfriend.StringBuilder;
|
||||
|
||||
if (receivers.Contains("role") && role is not null) mentions.Append($"{role.Mention} ");
|
||||
if (receivers.Contains("users") || receivers.Contains("interested"))
|
||||
mentions = (await scheduledEvent.GetUsersAsync(15)).Aggregate(mentions,
|
||||
(current, user) => current.Append($"{user.Mention} "));
|
||||
await channel?.SendMessageAsync(string.Format(Messages.EventEarlyNotification, mentions,
|
||||
Wrap(scheduledEvent.Name), scheduledEvent.StartTime.ToUnixTimeSeconds().ToString()))!;
|
||||
mentions.Clear();
|
||||
} catch (Exception e) {
|
||||
await Boyfriend.Log(new LogMessage(LogSeverity.Error, nameof(Utils),
|
||||
"Exception while sending early event start notification", e));
|
||||
}
|
||||
}
|
||||
|
||||
public static SocketTextChannel? GetEventNotificationChannel(SocketGuild guild) {
|
||||
return guild.GetTextChannel(ParseMention(Boyfriend.GetGuildConfig(guild.Id)["EventNotificationChannel"]));
|
||||
}
|
||||
|
@ -194,4 +135,3 @@ public static partial class Utils {
|
|||
[GeneratedRegex("[^0-9]")]
|
||||
private static partial Regex NumbersOnlyRegex();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue