diff --git a/Boyfriend/Boyfriend.cs b/Boyfriend/Boyfriend.cs index bc44f26..f22da61 100644 --- a/Boyfriend/Boyfriend.cs +++ b/Boyfriend/Boyfriend.cs @@ -16,6 +16,7 @@ public static class Boyfriend { GatewayIntents = (GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent | GatewayIntents.GuildMembers) & ~GatewayIntents.GuildInvites, + AlwaysDownloadUsers = true, AlwaysResolveStickers = false, AlwaysDownloadDefaultStickers = false, LargeThreshold = 500 @@ -74,8 +75,8 @@ public static class Boyfriend { try { Task.WaitAll(GuildTickTasks.ToArray()); } catch (AggregateException ex) { foreach (var exc in ex.InnerExceptions) - await Log(new LogMessage(LogSeverity.Error, nameof(CommandProcessor), - "Exception while executing commands", exc)); + await Log(new LogMessage(LogSeverity.Error, nameof(Boyfriend), + "Exception while ticking guilds", exc)); } GuildTickTasks.Clear(); @@ -114,10 +115,11 @@ public static class Boyfriend { var saveData = false; _ = int.TryParse(config["EventEarlyNotificationOffset"], out var offset); foreach (var schEvent in guild.Events) - if (config["AutoStartEvents"] is "true" && DateTimeOffset.Now >= schEvent.StartTime) { - await schEvent.StartAsync(); - } else if (!data.EarlyNotifications.Contains(schEvent.Id) && - DateTimeOffset.Now >= schEvent.StartTime.Subtract(new TimeSpan(0, offset, 0))) { + if (schEvent.Status is GuildScheduledEventStatus.Scheduled && config["AutoStartEvents"] is "true" && + DateTimeOffset.Now >= schEvent.StartTime) { await schEvent.StartAsync(); } else if + (!data.EarlyNotifications.Contains(schEvent.Id) && + DateTimeOffset.Now >= schEvent.StartTime.Subtract(new TimeSpan(0, offset, 0))) { + data.EarlyNotifications.Add(schEvent.Id); var receivers = config["EventStartedReceivers"]; var role = guild.GetRole(ulong.Parse(config["EventNotificationRole"])); var mentions = StringBuilder; @@ -128,12 +130,12 @@ public static class Boyfriend { .Where(user => role is null || !((RestGuildUser)user).RoleIds.Contains(role.Id)) .Aggregate(mentions, (current, user) => current.Append($"{user.Mention} ")); - await Utils.GetEventNotificationChannel(guild)?.SendMessageAsync(string.Format(Messages.EventStarted, + await Utils.GetEventNotificationChannel(guild)?.SendMessageAsync(string.Format( + Messages.EventEarlyNotification, mentions, Utils.Wrap(schEvent.Name), - Utils.Wrap(schEvent.Location) ?? Utils.MentionChannel(schEvent.Channel.Id)))!; + schEvent.StartTime.ToUnixTimeSeconds().ToString()))!; mentions.Clear(); - data.EarlyNotifications.Add(schEvent.Id); } foreach (var mData in data.MemberData.Values) { diff --git a/Boyfriend/CommandProcessor.cs b/Boyfriend/CommandProcessor.cs index 09a49ea..184d6d8 100644 --- a/Boyfriend/CommandProcessor.cs +++ b/Boyfriend/CommandProcessor.cs @@ -36,7 +36,7 @@ public sealed class CommandProcessor { Utils.SetCurrentLanguage(guild); if (GetMember().Roles.Contains(data.MuteRole)) { - _ = Context.Message.ReplyAsync(Messages.UserCannotUnmuteThemselves); + _ = Context.Message.DeleteAsync(); return; } @@ -175,7 +175,7 @@ public sealed class CommandProcessor { } var member = Context.Guild.GetUser(Utils.ParseMention(args[index])); - if (member is null && argument is not null) + if (member is null) Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.InvalidArgument} {Messages.InvalidMember}", Context.Message); diff --git a/Boyfriend/Commands/BanCommand.cs b/Boyfriend/Commands/BanCommand.cs index b16e91b..9c00656 100644 --- a/Boyfriend/Commands/BanCommand.cs +++ b/Boyfriend/Commands/BanCommand.cs @@ -38,7 +38,7 @@ public sealed class BanCommand : ICommand { cmd.ConfigWriteScheduled = true; var feedback = string.Format(Messages.FeedbackUserBanned, $"<@{toBan.Item1.ToString()}>", - Utils.GetHumanizedTimeOffset(duration), Utils.Wrap(reason)); + Utils.GetHumanizedTimeSpan(duration), Utils.Wrap(reason)); cmd.Reply(feedback, ReplyEmojis.Banned); cmd.Audit(feedback); } diff --git a/Boyfriend/Commands/MuteCommand.cs b/Boyfriend/Commands/MuteCommand.cs index 98c7e98..a713420 100644 --- a/Boyfriend/Commands/MuteCommand.cs +++ b/Boyfriend/Commands/MuteCommand.cs @@ -58,7 +58,7 @@ public sealed class MuteCommand : ICommand { cmd.ConfigWriteScheduled = true; var feedback = string.Format(Messages.FeedbackMemberMuted, toMute.Mention, - Utils.GetHumanizedTimeOffset(duration), + Utils.GetHumanizedTimeSpan(duration), Utils.Wrap(reason)); cmd.Reply(feedback, ReplyEmojis.Muted); cmd.Audit(feedback); diff --git a/Boyfriend/Commands/PingCommand.cs b/Boyfriend/Commands/PingCommand.cs index 88034a5..67e0861 100644 --- a/Boyfriend/Commands/PingCommand.cs +++ b/Boyfriend/Commands/PingCommand.cs @@ -7,7 +7,7 @@ public sealed class PingCommand : ICommand { var builder = Boyfriend.StringBuilder; builder.Append(Utils.GetBeep()) - .Append(Math.Abs(DateTimeOffset.Now.Subtract(cmd.Context.Message.Timestamp).TotalMilliseconds)) + .Append(Math.Round(Math.Abs(DateTimeOffset.Now.Subtract(cmd.Context.Message.Timestamp).TotalMilliseconds))) .Append(Messages.Milliseconds); cmd.Reply(builder.ToString(), ReplyEmojis.Ping); diff --git a/Boyfriend/Data/MemberData.cs b/Boyfriend/Data/MemberData.cs index 8bf8391..137375a 100644 --- a/Boyfriend/Data/MemberData.cs +++ b/Boyfriend/Data/MemberData.cs @@ -1,4 +1,5 @@ -using Discord; +using System.Text.Json.Serialization; +using Discord; namespace Boyfriend.Data; @@ -12,12 +13,26 @@ public record MemberData { public List Reminders; public List Roles; + [JsonConstructor] + public MemberData(DateTimeOffset? bannedUntil, ulong id, bool isInGuild, List joinedAt, + List leftAt, DateTimeOffset? mutedUntil, List reminders, List roles) { + BannedUntil = bannedUntil; + Id = id; + IsInGuild = isInGuild; + JoinedAt = joinedAt; + LeftAt = leftAt; + MutedUntil = mutedUntil; + Reminders = reminders; + Roles = roles; + } + public MemberData(IGuildUser user) { Id = user.Id; IsInGuild = true; JoinedAt = new List { user.JoinedAt!.Value }; LeftAt = new List(); Roles = user.RoleIds.ToList(); + Roles.Remove(user.Guild.Id); Reminders = new List(); } } diff --git a/Boyfriend/EventHandler.cs b/Boyfriend/EventHandler.cs index 0a5ca2a..3e3bcfd 100644 --- a/Boyfriend/EventHandler.cs +++ b/Boyfriend/EventHandler.cs @@ -25,7 +25,9 @@ public static class EventHandler { } private static Task RolesUpdatedEvent(Cacheable oldUser, SocketGuildUser newUser) { - GuildData.Get(newUser.Guild).MemberData[newUser.Id].Roles = ((IGuildUser)newUser).RoleIds.ToList(); + var data = GuildData.Get(newUser.Guild).MemberData[newUser.Id]; + data.Roles = ((IGuildUser)newUser).RoleIds.ToList(); + data.Roles.Remove(newUser.Guild.Id); return Task.CompletedTask; } @@ -205,6 +207,6 @@ public static class EventHandler { Utils.SetCurrentLanguage(guild); if (channel is not null) await channel.SendMessageAsync(string.Format(Messages.EventCompleted, Utils.Wrap(scheduledEvent.Name), - Utils.GetHumanizedTimeOffset(DateTimeOffset.Now.Subtract(scheduledEvent.StartTime)))); + Utils.GetHumanizedTimeSpan(DateTimeOffset.Now.Subtract(scheduledEvent.StartTime)))); } } diff --git a/Boyfriend/Utils.cs b/Boyfriend/Utils.cs index dba9573..094a09c 100644 --- a/Boyfriend/Utils.cs +++ b/Boyfriend/Utils.cs @@ -96,7 +96,7 @@ public static partial class Utils { if (sendPublic && systemChannel is not null) await SilentSendAsync(systemChannel, toSend); } - public static string GetHumanizedTimeOffset(TimeSpan span) { + public static string GetHumanizedTimeSpan(TimeSpan span) { return span.TotalSeconds < 1 ? Messages.Ever : $" {span.Humanize(2, minUnit: TimeUnit.Second, maxUnit: TimeUnit.Month, culture: Messages.Culture.Name.Contains("RU") ? CultureInfoCache["ru"] : Messages.Culture)}"; @@ -145,7 +145,8 @@ public static partial class Utils { if (role is not null) { if (!toUnmute.Roles.Contains(role)) return false; - await toUnmute.AddRolesAsync(data.MemberData[toUnmute.Id].Roles, requestOptions); + if (data.Preferences["RemoveRolesOnMute"] is "true") + await toUnmute.AddRolesAsync(data.MemberData[toUnmute.Id].Roles, requestOptions); await toUnmute.RemoveRoleAsync(role, requestOptions); data.MemberData[toUnmute.Id].MutedUntil = null; } else { @@ -157,6 +158,13 @@ public static partial class Utils { return true; } + public static async Task ReturnRolesAsync(SocketGuildUser user, List roles) { + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator + foreach (var role in roles) + if (role != user.Guild.Id) + await user.AddRoleAsync(role); + } + [GeneratedRegex("[^0-9]")] private static partial Regex NumbersOnlyRegex(); }