1
0
Fork 1
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:
Octol1ttle 2022-12-30 00:47:02 +05:00
parent f5a81fba35
commit 163e3ac46b
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
8 changed files with 56 additions and 167 deletions

View file

@ -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();
}
}

View file

@ -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)

View file

@ -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);

View file

@ -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";
}
}

View file

@ -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()) {

View file

@ -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;
}
}

View file

@ -1,6 +1,6 @@
namespace Boyfriend.Data;
public struct Reminder {
public DateTimeOffset RemindAt;
public long RemindAt;
public string ReminderText;
}

View file

@ -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();
}