1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-04-20 00:43:36 +03:00

Keep adapting code to new guild data storage...

Fix #15, update InspectCode, make Dependabot use the correct label
This commit is contained in:
Octol1ttle 2023-01-12 22:00:52 +05:00
parent fe2cfb3b3c
commit 4b2d98c440
Signed by: Octol1ttle
GPG key ID: B77C34313AEE1FFF
16 changed files with 152 additions and 131 deletions

View file

@ -13,7 +13,9 @@ updates:
# Allow both direct and indirect updates for all packages # Allow both direct and indirect updates for all packages
- dependency-type: "all" - dependency-type: "all"
assignees: assignees:
- "l1ttleO" - "Octol1ttle"
labels:
- "type: dependencies"
- package-ecosystem: "nuget" # See documentation for possible values - package-ecosystem: "nuget" # See documentation for possible values
directory: "/Boyfriend" # Location of package manifests directory: "/Boyfriend" # Location of package manifests
@ -24,4 +26,6 @@ updates:
- dependency-type: "all" - dependency-type: "all"
# Add assignees # Add assignees
assignees: assignees:
- "l1ttleO" - "Octol1ttle"
labels:
- "type: dependencies"

View file

@ -29,7 +29,7 @@ jobs:
run: dotnet restore run: dotnet restore
- name: ReSharper CLI InspectCode - name: ReSharper CLI InspectCode
uses: muno92/resharper_inspectcode@1.6.0 uses: muno92/resharper_inspectcode@1.6.6
with: with:
solutionPath: ./Boyfriend-CSharp.sln solutionPath: ./Boyfriend-CSharp.sln
ignoreIssueType: InvertIf ignoreIssueType: InvertIf

View file

@ -112,19 +112,30 @@ public sealed class CommandProcessor {
return null; return null;
} }
public SocketUser? GetUser(string[] args, string[] cleanArgs, int index, string? argument) { public Tuple<ulong, SocketUser?>? GetUser(string[] args, string[] cleanArgs, int index) {
if (index >= args.Length) { if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}", Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",
Context.Message); Context.Message);
return null; return null;
} }
var user = Boyfriend.Client.GetUser(Utils.ParseMention(args[index])); var mention = Utils.ParseMention(args[index]);
if (user is null && argument is not null) if (mention is 0) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{ReplyEmojis.InvalidArgument} {string.Format(Messages.InvalidUser, Utils.Wrap(cleanArgs[index]))}", $"{ReplyEmojis.InvalidArgument} {string.Format(Messages.InvalidUser, Utils.Wrap(cleanArgs[index]))}",
Context.Message); Context.Message);
return user; return null;
}
var exists = Utils.UserExists(mention);
if (!exists) {
Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{ReplyEmojis.Error} {string.Format(Messages.UserNotFound, Utils.Wrap(cleanArgs[index]))}",
Context.Message);
return null;
}
return Tuple.Create(mention, (SocketUser?)Boyfriend.Client.GetUser(mention));
} }
public bool HasPermission(GuildPermission permission) { public bool HasPermission(GuildPermission permission) {
@ -135,7 +146,7 @@ public sealed class CommandProcessor {
return false; return false;
} }
if (!Context.Guild.GetUser(Context.User.Id).GuildPermissions.Has(permission) if (!GetMember().GuildPermissions.Has(permission)
&& Context.Guild.OwnerId != Context.User.Id) { && Context.Guild.OwnerId != Context.User.Id) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, Utils.SafeAppendToBuilder(_stackedReplyMessage,
$"{ReplyEmojis.NoPermission} {Utils.GetMessage($"UserCannot{permission}")}", $"{ReplyEmojis.NoPermission} {Utils.GetMessage($"UserCannot{permission}")}",
@ -146,8 +157,12 @@ public sealed class CommandProcessor {
return true; return true;
} }
public SocketGuildUser? GetMember(SocketUser user) { private SocketGuildUser GetMember() {
return Context.Guild.GetUser(user.Id); return GetMember(Context.User.Id)!;
}
public SocketGuildUser? GetMember(ulong id) {
return Context.Guild.GetUser(id);
} }
public SocketGuildUser? GetMember(string[] args, string[] cleanArgs, int index, string? argument) { public SocketGuildUser? GetMember(string[] args, string[] cleanArgs, int index, string? argument) {
@ -165,10 +180,6 @@ public sealed class CommandProcessor {
return member; return member;
} }
private SocketGuildUser GetMember() {
return Context.Guild.GetUser(Context.User.Id);
}
public ulong? GetBan(string[] args, int index) { public ulong? GetBan(string[] args, int index) {
if (index >= args.Length) { if (index >= args.Length) {
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}", Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",

View file

@ -8,10 +8,10 @@ public sealed class BanCommand : ICommand {
public string[] Aliases { get; } = { "ban", "бан" }; public string[] Aliases { get; } = { "ban", "бан" };
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) { public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
var toBan = cmd.GetUser(args, cleanArgs, 0, "ToBan"); var toBan = cmd.GetUser(args, cleanArgs, 0);
if (toBan is null || !cmd.HasPermission(GuildPermission.BanMembers)) return; if (toBan is null || !cmd.HasPermission(GuildPermission.BanMembers)) return;
var memberToBan = cmd.GetMember(toBan); var memberToBan = cmd.GetMember(toBan.Item1);
if (memberToBan is not null && !cmd.CanInteractWith(memberToBan, "Ban")) return; if (memberToBan is not null && !cmd.CanInteractWith(memberToBan, "Ban")) return;
var duration = CommandProcessor.GetTimeSpan(args, 1); var duration = CommandProcessor.GetTimeSpan(args, 1);
@ -19,21 +19,23 @@ public sealed class BanCommand : ICommand {
if (reason is not null) await BanUserAsync(cmd, toBan, duration, reason); if (reason is not null) await BanUserAsync(cmd, toBan, duration, reason);
} }
private static async Task BanUserAsync(CommandProcessor cmd, SocketUser toBan, TimeSpan duration, string reason) { private static async Task BanUserAsync(CommandProcessor cmd, Tuple<ulong, SocketUser?> toBan, TimeSpan duration,
string reason) {
var author = cmd.Context.User; var author = cmd.Context.User;
var guild = cmd.Context.Guild; var guild = cmd.Context.Guild;
await Utils.SendDirectMessage(toBan, if (toBan.Item2 is not null)
await Utils.SendDirectMessage(toBan.Item2,
string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.Wrap(reason))); string.Format(Messages.YouWereBanned, author.Mention, guild.Name, Utils.Wrap(reason)));
var guildBanMessage = $"({author}) {reason}"; var guildBanMessage = $"({author}) {reason}";
await guild.AddBanAsync(toBan, 0, guildBanMessage); await guild.AddBanAsync(toBan.Item1, 0, guildBanMessage);
var memberData = GuildData.FromSocketGuild(guild).MemberData[toBan.Id]; var memberData = GuildData.FromSocketGuild(guild).MemberData[toBan.Item1];
memberData.BannedUntil memberData.BannedUntil
= duration.TotalSeconds < 1 ? -1 : DateTimeOffset.Now.Add(duration).ToUnixTimeSeconds(); = duration.TotalSeconds < 1 ? DateTimeOffset.MaxValue : DateTimeOffset.Now.Add(duration);
memberData.Roles.Clear(); memberData.Roles.Clear();
var feedback = string.Format(Messages.FeedbackUserBanned, toBan.Mention, var feedback = string.Format(Messages.FeedbackUserBanned, $"<@{toBan.Item1.ToString()}>",
Utils.GetHumanizedTimeOffset(duration), Utils.Wrap(reason)); Utils.GetHumanizedTimeOffset(duration), Utils.Wrap(reason));
cmd.Reply(feedback, ReplyEmojis.Banned); cmd.Reply(feedback, ReplyEmojis.Banned);
cmd.Audit(feedback); cmd.Audit(feedback);

View file

@ -19,8 +19,8 @@ public sealed class MuteCommand : ICommand {
if ((role is not null && toMute.Roles.Contains(role)) if ((role is not null && toMute.Roles.Contains(role))
|| (toMute.TimedOutUntil is not null || (toMute.TimedOutUntil is not null
&& toMute.TimedOutUntil.Value.ToUnixTimeSeconds() && toMute.TimedOutUntil.Value
> DateTimeOffset.Now.ToUnixTimeSeconds())) { > DateTimeOffset.Now)) {
cmd.Reply(Messages.MemberAlreadyMuted, ReplyEmojis.Error); cmd.Reply(Messages.MemberAlreadyMuted, ReplyEmojis.Error);
return; return;
} }
@ -41,7 +41,7 @@ public sealed class MuteCommand : ICommand {
await toMute.AddRoleAsync(role, requestOptions); await toMute.AddRoleAsync(role, requestOptions);
data.MemberData[toMute.Id].MutedUntil = DateTimeOffset.Now.Add(duration).ToUnixTimeSeconds(); data.MemberData[toMute.Id].MutedUntil = DateTimeOffset.Now.Add(duration);
} else { } else {
if (!hasDuration || duration.TotalDays > 28) { if (!hasDuration || duration.TotalDays > 28) {
cmd.Reply(Messages.DurationRequiredForTimeOuts, ReplyEmojis.Error); cmd.Reply(Messages.DurationRequiredForTimeOuts, ReplyEmojis.Error);

View file

@ -10,7 +10,7 @@ public sealed class RemindCommand : ICommand {
var reminderText = cmd.GetRemaining(args, 1, "ReminderText"); var reminderText = cmd.GetRemaining(args, 1, "ReminderText");
if (reminderText is not null) if (reminderText is not null)
GuildData.FromSocketGuild(cmd.Context.Guild).MemberData[cmd.Context.User.Id].Reminders.Add(new Reminder { GuildData.FromSocketGuild(cmd.Context.Guild).MemberData[cmd.Context.User.Id].Reminders.Add(new Reminder {
RemindAt = DateTimeOffset.Now.Add(remindIn).ToUnixTimeSeconds(), RemindAt = DateTimeOffset.Now.Add(remindIn),
ReminderText = reminderText ReminderText = reminderText
}); });

View file

@ -27,8 +27,7 @@ public sealed class UnmuteCommand : ICommand {
await toUnmute.AddRolesAsync(data.MemberData[toUnmute.Id].Roles, requestOptions); await toUnmute.AddRolesAsync(data.MemberData[toUnmute.Id].Roles, requestOptions);
await toUnmute.RemoveRoleAsync(role, requestOptions); await toUnmute.RemoveRoleAsync(role, requestOptions);
} else { } else {
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value.ToUnixTimeSeconds() < if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value < DateTimeOffset.Now) {
DateTimeOffset.Now.ToUnixTimeSeconds()) {
cmd.Reply(Messages.MemberNotMuted, ReplyEmojis.Error); cmd.Reply(Messages.MemberNotMuted, ReplyEmojis.Error);
return; return;
} }

View file

@ -24,7 +24,7 @@ public record GuildData {
// TODO: { "AutoStartEvents", "false" } // TODO: { "AutoStartEvents", "false" }
}; };
private static readonly Dictionary<ulong, GuildData> GuildDataDictionary = new(); public static readonly Dictionary<ulong, GuildData> GuildDataDictionary = new();
public readonly Dictionary<ulong, MemberData> MemberData; public readonly Dictionary<ulong, MemberData> MemberData;
@ -44,14 +44,11 @@ public record GuildData {
= JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText($"{_id}/Configuration.json")) ?? = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText($"{_id}/Configuration.json")) ??
new Dictionary<string, string>(); new Dictionary<string, string>();
// ReSharper disable twice ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
if (Preferences.Keys.Count < DefaultPreferences.Keys.Count) if (Preferences.Keys.Count < DefaultPreferences.Keys.Count)
foreach (var key in DefaultPreferences.Keys) foreach (var key in DefaultPreferences.Keys.Where(key => !Preferences.ContainsKey(key)))
if (!Preferences.ContainsKey(key))
Preferences.Add(key, DefaultPreferences[key]); Preferences.Add(key, DefaultPreferences[key]);
if (Preferences.Keys.Count > DefaultPreferences.Keys.Count) if (Preferences.Keys.Count > DefaultPreferences.Keys.Count)
foreach (var key in Preferences.Keys) foreach (var key in Preferences.Keys.Where(key => !DefaultPreferences.ContainsKey(key)))
if (!DefaultPreferences.ContainsKey(key))
Preferences.Remove(key); Preferences.Remove(key);
Preferences.TrimExcess(); Preferences.TrimExcess();
@ -67,7 +64,7 @@ public record GuildData {
if (MemberData.TryGetValue(member.Id, out var memberData)) { if (MemberData.TryGetValue(member.Id, out var memberData)) {
if (!memberData.IsInGuild && if (!memberData.IsInGuild &&
DateTimeOffset.Now.ToUnixTimeSeconds() - DateTimeOffset.Now.ToUnixTimeSeconds() -
Math.Max(memberData.LeftAt.Last(), memberData.BannedUntil) > Math.Max(memberData.LeftAt.Last().ToUnixTimeSeconds(), memberData.BannedUntil.ToUnixTimeSeconds()) >
60 * 60 * 24 * 30) { 60 * 60 * 24 * 30) {
File.Delete($"{_id}/MemberData/{memberData.Id}.json"); File.Delete($"{_id}/MemberData/{memberData.Id}.json");
MemberData.Remove(memberData.Id); MemberData.Remove(memberData.Id);
@ -83,8 +80,11 @@ public record GuildData {
} }
public SocketRole? MuteRole { public SocketRole? MuteRole {
get => _cachedMuteRole ??= Boyfriend.Client.GetGuild(_id).Roles get {
if (Preferences["MuteRole"] is "0") return null;
return _cachedMuteRole ??= Boyfriend.Client.GetGuild(_id).Roles
.Single(x => x.Id == ulong.Parse(Preferences["MuteRole"])); .Single(x => x.Id == ulong.Parse(Preferences["MuteRole"]));
}
set => _cachedMuteRole = value; set => _cachedMuteRole = value;
} }

View file

@ -3,23 +3,23 @@
namespace Boyfriend.Data; namespace Boyfriend.Data;
public record MemberData { public record MemberData {
public long BannedUntil; public DateTimeOffset BannedUntil;
public ulong Id; public ulong Id;
public bool IsInGuild; public bool IsInGuild;
public List<long> JoinedAt; public List<DateTimeOffset> JoinedAt;
public List<long> LeftAt; public List<DateTimeOffset> LeftAt;
public long MutedUntil; public DateTimeOffset MutedUntil;
public List<Reminder> Reminders; public List<Reminder> Reminders;
public List<ulong> Roles; public List<ulong> Roles;
public MemberData(IGuildUser user) { public MemberData(IGuildUser user) {
Id = user.Id; Id = user.Id;
IsInGuild = true; IsInGuild = true;
JoinedAt = new List<long> { user.JoinedAt!.Value.ToUnixTimeSeconds() }; JoinedAt = new List<DateTimeOffset> { user.JoinedAt!.Value };
LeftAt = new List<long>(); LeftAt = new List<DateTimeOffset>();
Roles = user.RoleIds.ToList(); Roles = user.RoleIds.ToList();
Reminders = new List<Reminder>(); Reminders = new List<Reminder>();
MutedUntil = 0; MutedUntil = DateTimeOffset.MinValue;
BannedUntil = 0; BannedUntil = DateTimeOffset.MinValue;
} }
} }

View file

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

View file

@ -110,19 +110,18 @@ public static class EventHandler {
if (!data.MemberData.ContainsKey(user.Id)) data.MemberData.Add(user.Id, new MemberData(user)); if (!data.MemberData.ContainsKey(user.Id)) data.MemberData.Add(user.Id, new MemberData(user));
var memberData = data.MemberData[user.Id]; var memberData = data.MemberData[user.Id];
memberData.IsInGuild = true; memberData.IsInGuild = true;
memberData.BannedUntil = 0; memberData.BannedUntil = DateTimeOffset.MinValue;
if (memberData.LeftAt.Count > 0) { if (memberData.LeftAt.Count > 0) {
if (memberData.JoinedAt.Contains(user.JoinedAt!.Value.ToUnixTimeSeconds())) if (memberData.JoinedAt.Contains(user.JoinedAt!.Value))
throw new UnreachableException(); throw new UnreachableException();
memberData.JoinedAt.Add(user.JoinedAt!.Value.ToUnixTimeSeconds()); memberData.JoinedAt.Add(user.JoinedAt!.Value);
} }
if (memberData.MutedUntil < DateTimeOffset.Now.ToUnixTimeSeconds()) { if (memberData.MutedUntil < DateTimeOffset.Now) {
if (data.MuteRole is not null) if (data.MuteRole is not null)
await user.AddRoleAsync(data.MuteRole); await user.AddRoleAsync(data.MuteRole);
else else
await user.SetTimeOutAsync( await user.SetTimeOutAsync(DateTimeOffset.Now - memberData.MutedUntil);
TimeSpan.FromSeconds(DateTimeOffset.Now.ToUnixTimeSeconds() - memberData.MutedUntil));
if (config["RemoveRolesOnMute"] is "false" && config["ReturnRolesOnRejoin"] is "true") if (config["RemoveRolesOnMute"] is "false" && config["ReturnRolesOnRejoin"] is "true")
await user.AddRolesAsync(memberData.Roles); await user.AddRolesAsync(memberData.Roles);
@ -132,11 +131,10 @@ public static class EventHandler {
private static Task UserLeftEvent(SocketGuild guild, SocketUser user) { private static Task UserLeftEvent(SocketGuild guild, SocketUser user) {
var data = GuildData.FromSocketGuild(guild).MemberData[user.Id]; var data = GuildData.FromSocketGuild(guild).MemberData[user.Id];
data.IsInGuild = false; data.IsInGuild = false;
data.LeftAt.Add(DateTimeOffset.Now.ToUnixTimeSeconds()); data.LeftAt.Add(DateTimeOffset.Now);
return Task.CompletedTask; return Task.CompletedTask;
} }
// TODO: store data about event (early) notifications
private static async Task ScheduledEventCreatedEvent(SocketGuildEvent scheduledEvent) { private static async Task ScheduledEventCreatedEvent(SocketGuildEvent scheduledEvent) {
var guild = scheduledEvent.Guild; var guild = scheduledEvent.Guild;
var eventConfig = GuildData.FromSocketGuild(guild).Preferences; var eventConfig = GuildData.FromSocketGuild(guild).Preferences;

View file

@ -1050,11 +1050,11 @@ namespace Boyfriend {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to The specified user is not a member of this server!. /// Looks up a localized string similar to .
/// </summary> /// </summary>
internal static string UserNotInGuild { internal static string UserNotFound {
get { get {
return ResourceManager.GetString("UserNotInGuild", resourceCulture); return ResourceManager.GetString("UserNotFound", resourceCulture);
} }
} }

View file

@ -225,9 +225,6 @@
<data name="FeedbackUserBanned" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>Banned {0} for{1}: {2}</value> <value>Banned {0} for{1}: {2}</value>
</data> </data>
<data name="UserNotInGuild" xml:space="preserve">
<value>The specified user is not a member of this server!</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>
@ -459,4 +456,7 @@
<data name="SettingsEventEarlyNotificationOffset" xml:space="preserve"> <data name="SettingsEventEarlyNotificationOffset" xml:space="preserve">
<value>Early event start notification offset</value> <value>Early event start notification offset</value>
</data> </data>
<data name="UserNotFound" xml:space="preserve">
<value/>
</data>
</root> </root>

View file

@ -222,9 +222,6 @@
<data name="FeedbackUserBanned" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>Забанен {0} на{1}: {2}</value> <value>Забанен {0} на{1}: {2}</value>
</data> </data>
<data name="UserNotInGuild" xml:space="preserve">
<value>Указанный пользователь не является участником этого сервера!</value>
</data>
<data name="SettingDoesntExist" xml:space="preserve"> <data name="SettingDoesntExist" xml:space="preserve">
<value>Такая настройка не существует!</value> <value>Такая настройка не существует!</value>
</data> </data>

View file

@ -222,9 +222,6 @@
<data name="FeedbackUserBanned" xml:space="preserve"> <data name="FeedbackUserBanned" xml:space="preserve">
<value>забанен {0} на{1}: {2}</value> <value>забанен {0} на{1}: {2}</value>
</data> </data>
<data name="UserNotInGuild" xml:space="preserve">
<value>шизик не на этом сервере</value>
</data>
<data name="SettingDoesntExist" xml:space="preserve"> <data name="SettingDoesntExist" xml:space="preserve">
<value>такой прикол не существует</value> <value>такой прикол не существует</value>
</data> </data>

View file

@ -49,6 +49,11 @@ public static partial class Utils {
return ulong.TryParse(NumbersOnlyRegex().Replace(mention, ""), out var id) ? id : 0; return ulong.TryParse(NumbersOnlyRegex().Replace(mention, ""), out var id) ? id : 0;
} }
public static async Task SendDirectMessage(ulong id, string toSend) {
if (await Boyfriend.Client.GetUserAsync(id) is SocketUser user)
await SendDirectMessage(user, toSend);
}
public static async Task SendDirectMessage(SocketUser user, string toSend) { public static async Task SendDirectMessage(SocketUser user, string toSend) {
try { await user.SendMessageAsync(toSend); } catch (HttpException e) { try { await user.SendMessageAsync(toSend); } catch (HttpException e) {
if (e.DiscordCode is not DiscordErrorCode.CannotSendMessageToUser) throw; if (e.DiscordCode is not DiscordErrorCode.CannotSendMessageToUser) throw;
@ -133,6 +138,14 @@ public static partial class Utils {
.Preferences["EventNotificationChannel"])); .Preferences["EventNotificationChannel"]));
} }
public static bool UserExists(ulong id) {
return Boyfriend.Client.GetUser(id) is not null || UserInMemberData(id);
}
private static bool UserInMemberData(ulong id) {
return GuildData.GuildDataDictionary.Values.Any(gData => gData.MemberData.Values.Any(mData => mData.Id == id));
}
[GeneratedRegex("[^0-9]")] [GeneratedRegex("[^0-9]")]
private static partial Regex NumbersOnlyRegex(); private static partial Regex NumbersOnlyRegex();
} }