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:
parent
fe2cfb3b3c
commit
4b2d98c440
16 changed files with 152 additions and 131 deletions
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
|
@ -13,7 +13,9 @@ updates:
|
|||
# Allow both direct and indirect updates for all packages
|
||||
- dependency-type: "all"
|
||||
assignees:
|
||||
- "l1ttleO"
|
||||
- "Octol1ttle"
|
||||
labels:
|
||||
- "type: dependencies"
|
||||
|
||||
- package-ecosystem: "nuget" # See documentation for possible values
|
||||
directory: "/Boyfriend" # Location of package manifests
|
||||
|
@ -24,4 +26,6 @@ updates:
|
|||
- dependency-type: "all"
|
||||
# Add assignees
|
||||
assignees:
|
||||
- "l1ttleO"
|
||||
- "Octol1ttle"
|
||||
labels:
|
||||
- "type: dependencies"
|
||||
|
|
2
.github/workflows/resharper.yml
vendored
2
.github/workflows/resharper.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
|||
run: dotnet restore
|
||||
|
||||
- name: ReSharper CLI InspectCode
|
||||
uses: muno92/resharper_inspectcode@1.6.0
|
||||
uses: muno92/resharper_inspectcode@1.6.6
|
||||
with:
|
||||
solutionPath: ./Boyfriend-CSharp.sln
|
||||
ignoreIssueType: InvertIf
|
||||
|
|
|
@ -112,19 +112,30 @@ public sealed class CommandProcessor {
|
|||
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) {
|
||||
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",
|
||||
Context.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
var user = Boyfriend.Client.GetUser(Utils.ParseMention(args[index]));
|
||||
if (user is null && argument is not null)
|
||||
var mention = Utils.ParseMention(args[index]);
|
||||
if (mention is 0) {
|
||||
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
||||
$"{ReplyEmojis.InvalidArgument} {string.Format(Messages.InvalidUser, Utils.Wrap(cleanArgs[index]))}",
|
||||
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) {
|
||||
|
@ -135,7 +146,7 @@ public sealed class CommandProcessor {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!Context.Guild.GetUser(Context.User.Id).GuildPermissions.Has(permission)
|
||||
if (!GetMember().GuildPermissions.Has(permission)
|
||||
&& Context.Guild.OwnerId != Context.User.Id) {
|
||||
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
||||
$"{ReplyEmojis.NoPermission} {Utils.GetMessage($"UserCannot{permission}")}",
|
||||
|
@ -146,8 +157,12 @@ public sealed class CommandProcessor {
|
|||
return true;
|
||||
}
|
||||
|
||||
public SocketGuildUser? GetMember(SocketUser user) {
|
||||
return Context.Guild.GetUser(user.Id);
|
||||
private SocketGuildUser GetMember() {
|
||||
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) {
|
||||
|
@ -165,10 +180,6 @@ public sealed class CommandProcessor {
|
|||
return member;
|
||||
}
|
||||
|
||||
private SocketGuildUser GetMember() {
|
||||
return Context.Guild.GetUser(Context.User.Id);
|
||||
}
|
||||
|
||||
public ulong? GetBan(string[] args, int index) {
|
||||
if (index >= args.Length) {
|
||||
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingUser}",
|
||||
|
|
|
@ -8,10 +8,10 @@ public sealed class BanCommand : ICommand {
|
|||
public string[] Aliases { get; } = { "ban", "бан" };
|
||||
|
||||
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;
|
||||
|
||||
var memberToBan = cmd.GetMember(toBan);
|
||||
var memberToBan = cmd.GetMember(toBan.Item1);
|
||||
if (memberToBan is not null && !cmd.CanInteractWith(memberToBan, "Ban")) return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 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)));
|
||||
|
||||
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
|
||||
= duration.TotalSeconds < 1 ? -1 : DateTimeOffset.Now.Add(duration).ToUnixTimeSeconds();
|
||||
= duration.TotalSeconds < 1 ? DateTimeOffset.MaxValue : DateTimeOffset.Now.Add(duration);
|
||||
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));
|
||||
cmd.Reply(feedback, ReplyEmojis.Banned);
|
||||
cmd.Audit(feedback);
|
||||
|
|
|
@ -19,8 +19,8 @@ public sealed class MuteCommand : ICommand {
|
|||
|
||||
if ((role is not null && toMute.Roles.Contains(role))
|
||||
|| (toMute.TimedOutUntil is not null
|
||||
&& toMute.TimedOutUntil.Value.ToUnixTimeSeconds()
|
||||
> DateTimeOffset.Now.ToUnixTimeSeconds())) {
|
||||
&& toMute.TimedOutUntil.Value
|
||||
> DateTimeOffset.Now)) {
|
||||
cmd.Reply(Messages.MemberAlreadyMuted, ReplyEmojis.Error);
|
||||
return;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public sealed class MuteCommand : ICommand {
|
|||
|
||||
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 {
|
||||
if (!hasDuration || duration.TotalDays > 28) {
|
||||
cmd.Reply(Messages.DurationRequiredForTimeOuts, ReplyEmojis.Error);
|
||||
|
|
|
@ -10,7 +10,7 @@ public sealed class RemindCommand : ICommand {
|
|||
var reminderText = cmd.GetRemaining(args, 1, "ReminderText");
|
||||
if (reminderText is not null)
|
||||
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
|
||||
});
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ public sealed class UnmuteCommand : ICommand {
|
|||
await toUnmute.AddRolesAsync(data.MemberData[toUnmute.Id].Roles, requestOptions);
|
||||
await toUnmute.RemoveRoleAsync(role, requestOptions);
|
||||
} else {
|
||||
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value.ToUnixTimeSeconds() <
|
||||
DateTimeOffset.Now.ToUnixTimeSeconds()) {
|
||||
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value < DateTimeOffset.Now) {
|
||||
cmd.Reply(Messages.MemberNotMuted, ReplyEmojis.Error);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public record GuildData {
|
|||
// 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;
|
||||
|
||||
|
@ -44,14 +44,11 @@ public record GuildData {
|
|||
= JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText($"{_id}/Configuration.json")) ??
|
||||
new Dictionary<string, string>();
|
||||
|
||||
// ReSharper disable twice ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
if (Preferences.Keys.Count < DefaultPreferences.Keys.Count)
|
||||
foreach (var key in DefaultPreferences.Keys)
|
||||
if (!Preferences.ContainsKey(key))
|
||||
foreach (var key in DefaultPreferences.Keys.Where(key => !Preferences.ContainsKey(key)))
|
||||
Preferences.Add(key, DefaultPreferences[key]);
|
||||
if (Preferences.Keys.Count > DefaultPreferences.Keys.Count)
|
||||
foreach (var key in Preferences.Keys)
|
||||
if (!DefaultPreferences.ContainsKey(key))
|
||||
foreach (var key in Preferences.Keys.Where(key => !DefaultPreferences.ContainsKey(key)))
|
||||
Preferences.Remove(key);
|
||||
Preferences.TrimExcess();
|
||||
|
||||
|
@ -67,7 +64,7 @@ public record GuildData {
|
|||
if (MemberData.TryGetValue(member.Id, out var memberData)) {
|
||||
if (!memberData.IsInGuild &&
|
||||
DateTimeOffset.Now.ToUnixTimeSeconds() -
|
||||
Math.Max(memberData.LeftAt.Last(), memberData.BannedUntil) >
|
||||
Math.Max(memberData.LeftAt.Last().ToUnixTimeSeconds(), memberData.BannedUntil.ToUnixTimeSeconds()) >
|
||||
60 * 60 * 24 * 30) {
|
||||
File.Delete($"{_id}/MemberData/{memberData.Id}.json");
|
||||
MemberData.Remove(memberData.Id);
|
||||
|
@ -83,8 +80,11 @@ public record GuildData {
|
|||
}
|
||||
|
||||
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"]));
|
||||
}
|
||||
set => _cachedMuteRole = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,23 +3,23 @@
|
|||
namespace Boyfriend.Data;
|
||||
|
||||
public record MemberData {
|
||||
public long BannedUntil;
|
||||
public DateTimeOffset BannedUntil;
|
||||
public ulong Id;
|
||||
public bool IsInGuild;
|
||||
public List<long> JoinedAt;
|
||||
public List<long> LeftAt;
|
||||
public long MutedUntil;
|
||||
public List<DateTimeOffset> JoinedAt;
|
||||
public List<DateTimeOffset> LeftAt;
|
||||
public DateTimeOffset MutedUntil;
|
||||
public List<Reminder> Reminders;
|
||||
public List<ulong> Roles;
|
||||
|
||||
public MemberData(IGuildUser user) {
|
||||
Id = user.Id;
|
||||
IsInGuild = true;
|
||||
JoinedAt = new List<long> { user.JoinedAt!.Value.ToUnixTimeSeconds() };
|
||||
LeftAt = new List<long>();
|
||||
JoinedAt = new List<DateTimeOffset> { user.JoinedAt!.Value };
|
||||
LeftAt = new List<DateTimeOffset>();
|
||||
Roles = user.RoleIds.ToList();
|
||||
Reminders = new List<Reminder>();
|
||||
MutedUntil = 0;
|
||||
BannedUntil = 0;
|
||||
MutedUntil = DateTimeOffset.MinValue;
|
||||
BannedUntil = DateTimeOffset.MinValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Boyfriend.Data;
|
||||
|
||||
public struct Reminder {
|
||||
public long RemindAt;
|
||||
public DateTimeOffset RemindAt;
|
||||
public string ReminderText;
|
||||
}
|
||||
|
|
|
@ -110,19 +110,18 @@ public static class EventHandler {
|
|||
if (!data.MemberData.ContainsKey(user.Id)) data.MemberData.Add(user.Id, new MemberData(user));
|
||||
var memberData = data.MemberData[user.Id];
|
||||
memberData.IsInGuild = true;
|
||||
memberData.BannedUntil = 0;
|
||||
memberData.BannedUntil = DateTimeOffset.MinValue;
|
||||
if (memberData.LeftAt.Count > 0) {
|
||||
if (memberData.JoinedAt.Contains(user.JoinedAt!.Value.ToUnixTimeSeconds()))
|
||||
if (memberData.JoinedAt.Contains(user.JoinedAt!.Value))
|
||||
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)
|
||||
await user.AddRoleAsync(data.MuteRole);
|
||||
else
|
||||
await user.SetTimeOutAsync(
|
||||
TimeSpan.FromSeconds(DateTimeOffset.Now.ToUnixTimeSeconds() - memberData.MutedUntil));
|
||||
await user.SetTimeOutAsync(DateTimeOffset.Now - memberData.MutedUntil);
|
||||
|
||||
if (config["RemoveRolesOnMute"] is "false" && config["ReturnRolesOnRejoin"] is "true")
|
||||
await user.AddRolesAsync(memberData.Roles);
|
||||
|
@ -132,11 +131,10 @@ public static class EventHandler {
|
|||
private static Task UserLeftEvent(SocketGuild guild, SocketUser user) {
|
||||
var data = GuildData.FromSocketGuild(guild).MemberData[user.Id];
|
||||
data.IsInGuild = false;
|
||||
data.LeftAt.Add(DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||
data.LeftAt.Add(DateTimeOffset.Now);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// TODO: store data about event (early) notifications
|
||||
private static async Task ScheduledEventCreatedEvent(SocketGuildEvent scheduledEvent) {
|
||||
var guild = scheduledEvent.Guild;
|
||||
var eventConfig = GuildData.FromSocketGuild(guild).Preferences;
|
||||
|
|
6
Boyfriend/Messages.Designer.cs
generated
6
Boyfriend/Messages.Designer.cs
generated
|
@ -1050,11 +1050,11 @@ namespace Boyfriend {
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string UserNotInGuild {
|
||||
internal static string UserNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("UserNotInGuild", resourceCulture);
|
||||
return ResourceManager.GetString("UserNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -225,9 +225,6 @@
|
|||
<data name="FeedbackUserBanned" xml:space="preserve">
|
||||
<value>Banned {0} for{1}: {2}</value>
|
||||
</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">
|
||||
<value>That setting doesn't exist!</value>
|
||||
</data>
|
||||
|
@ -459,4 +456,7 @@
|
|||
<data name="SettingsEventEarlyNotificationOffset" xml:space="preserve">
|
||||
<value>Early event start notification offset</value>
|
||||
</data>
|
||||
<data name="UserNotFound" xml:space="preserve">
|
||||
<value/>
|
||||
</data>
|
||||
</root>
|
|
@ -222,9 +222,6 @@
|
|||
<data name="FeedbackUserBanned" xml:space="preserve">
|
||||
<value>Забанен {0} на{1}: {2}</value>
|
||||
</data>
|
||||
<data name="UserNotInGuild" xml:space="preserve">
|
||||
<value>Указанный пользователь не является участником этого сервера!</value>
|
||||
</data>
|
||||
<data name="SettingDoesntExist" xml:space="preserve">
|
||||
<value>Такая настройка не существует!</value>
|
||||
</data>
|
||||
|
|
|
@ -222,9 +222,6 @@
|
|||
<data name="FeedbackUserBanned" xml:space="preserve">
|
||||
<value>забанен {0} на{1}: {2}</value>
|
||||
</data>
|
||||
<data name="UserNotInGuild" xml:space="preserve">
|
||||
<value>шизик не на этом сервере</value>
|
||||
</data>
|
||||
<data name="SettingDoesntExist" xml:space="preserve">
|
||||
<value>такой прикол не существует</value>
|
||||
</data>
|
||||
|
|
|
@ -49,6 +49,11 @@ public static partial class Utils {
|
|||
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) {
|
||||
try { await user.SendMessageAsync(toSend); } catch (HttpException e) {
|
||||
if (e.DiscordCode is not DiscordErrorCode.CannotSendMessageToUser) throw;
|
||||
|
@ -133,6 +138,14 @@ public static partial class Utils {
|
|||
.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]")]
|
||||
private static partial Regex NumbersOnlyRegex();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue