mirror of
https://github.com/TeamOctolings/Octobot.git
synced 2025-04-20 00:43:36 +03:00
Fixing bugs and other problems
This commit is contained in:
parent
905cf5619c
commit
f1bd54d6f6
16 changed files with 255 additions and 144 deletions
|
@ -16,7 +16,6 @@ public static class Boyfriend {
|
||||||
GatewayIntents
|
GatewayIntents
|
||||||
= (GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent | GatewayIntents.GuildMembers) &
|
= (GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent | GatewayIntents.GuildMembers) &
|
||||||
~GatewayIntents.GuildInvites,
|
~GatewayIntents.GuildInvites,
|
||||||
AlwaysDownloadUsers = true,
|
|
||||||
AlwaysResolveStickers = false,
|
AlwaysResolveStickers = false,
|
||||||
AlwaysDownloadDefaultStickers = false,
|
AlwaysDownloadDefaultStickers = false,
|
||||||
LargeThreshold = 500
|
LargeThreshold = 500
|
||||||
|
@ -112,6 +111,7 @@ public static class Boyfriend {
|
||||||
private static async Task TickGuildAsync(SocketGuild guild) {
|
private static async Task TickGuildAsync(SocketGuild guild) {
|
||||||
var data = GuildData.Get(guild);
|
var data = GuildData.Get(guild);
|
||||||
var config = data.Preferences;
|
var config = data.Preferences;
|
||||||
|
var saveData = false;
|
||||||
_ = int.TryParse(config["EventEarlyNotificationOffset"], out var offset);
|
_ = int.TryParse(config["EventEarlyNotificationOffset"], out var offset);
|
||||||
foreach (var schEvent in guild.Events)
|
foreach (var schEvent in guild.Events)
|
||||||
if (config["AutoStartEvents"] is "true" && DateTimeOffset.Now >= schEvent.StartTime) {
|
if (config["AutoStartEvents"] is "true" && DateTimeOffset.Now >= schEvent.StartTime) {
|
||||||
|
@ -140,11 +140,15 @@ public static class Boyfriend {
|
||||||
if (DateTimeOffset.Now >= mData.BannedUntil) _ = guild.RemoveBanAsync(mData.Id);
|
if (DateTimeOffset.Now >= mData.BannedUntil) _ = guild.RemoveBanAsync(mData.Id);
|
||||||
|
|
||||||
if (mData.IsInGuild) {
|
if (mData.IsInGuild) {
|
||||||
if (DateTimeOffset.Now >= mData.MutedUntil)
|
if (DateTimeOffset.Now >= mData.MutedUntil) {
|
||||||
await Utils.UnmuteMemberAsync(data, Client.CurrentUser.ToString(), guild.GetUser(mData.Id),
|
await Utils.UnmuteMemberAsync(data, Client.CurrentUser.ToString(), guild.GetUser(mData.Id),
|
||||||
Messages.PunishmentExpired);
|
Messages.PunishmentExpired);
|
||||||
|
saveData = true;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var reminder in mData.Reminders.Where(rem => DateTimeOffset.Now >= rem.RemindAt)) {
|
for (var i = mData.Reminders.Count - 1; i >= 0; i--) {
|
||||||
|
var reminder = mData.Reminders[i];
|
||||||
|
if (DateTimeOffset.Now >= reminder.RemindAt) {
|
||||||
var channel = guild.GetTextChannel(reminder.ReminderChannel);
|
var channel = guild.GetTextChannel(reminder.ReminderChannel);
|
||||||
if (channel is null) {
|
if (channel is null) {
|
||||||
await Utils.SendDirectMessage(Client.GetUser(mData.Id), reminder.ReminderText);
|
await Utils.SendDirectMessage(Client.GetUser(mData.Id), reminder.ReminderText);
|
||||||
|
@ -152,10 +156,14 @@ public static class Boyfriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
await channel.SendMessageAsync($"<@{mData.Id}> {Utils.Wrap(reminder.ReminderText)}");
|
await channel.SendMessageAsync($"<@{mData.Id}> {Utils.Wrap(reminder.ReminderText)}");
|
||||||
|
mData.Reminders.RemoveAt(i);
|
||||||
|
|
||||||
mData.Reminders.Remove(reminder);
|
saveData = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saveData) data.Save(true).Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ public sealed class CommandProcessor {
|
||||||
return Context.Guild.GetUser(id);
|
return Context.Guild.GetUser(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketGuildUser? GetMember(string[] args, string[] cleanArgs, int index, string? argument) {
|
public SocketGuildUser? GetMember(string[] args, int index, string? argument) {
|
||||||
if (index >= args.Length) {
|
if (index >= args.Length) {
|
||||||
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingMember}",
|
Utils.SafeAppendToBuilder(_stackedReplyMessage, $"{ReplyEmojis.MissingArgument} {Messages.MissingMember}",
|
||||||
Context.Message);
|
Context.Message);
|
||||||
|
@ -177,7 +177,7 @@ public sealed class CommandProcessor {
|
||||||
var member = Context.Guild.GetUser(Utils.ParseMention(args[index]));
|
var member = Context.Guild.GetUser(Utils.ParseMention(args[index]));
|
||||||
if (member is null && argument is not null)
|
if (member is null && argument is not null)
|
||||||
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
Utils.SafeAppendToBuilder(_stackedReplyMessage,
|
||||||
$"{ReplyEmojis.InvalidArgument} {string.Format(Messages.InvalidMember, Utils.Wrap(cleanArgs[index]))}",
|
$"{ReplyEmojis.InvalidArgument} {Messages.InvalidMember}",
|
||||||
Context.Message);
|
Context.Message);
|
||||||
return member;
|
return member;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ public sealed class BanCommand : ICommand {
|
||||||
= duration.TotalSeconds < 1 ? DateTimeOffset.MaxValue : DateTimeOffset.Now.Add(duration);
|
= duration.TotalSeconds < 1 ? DateTimeOffset.MaxValue : DateTimeOffset.Now.Add(duration);
|
||||||
memberData.Roles.Clear();
|
memberData.Roles.Clear();
|
||||||
|
|
||||||
|
cmd.ConfigWriteScheduled = true;
|
||||||
|
|
||||||
var feedback = string.Format(Messages.FeedbackUserBanned, $"<@{toBan.Item1.ToString()}>",
|
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);
|
||||||
|
|
|
@ -19,7 +19,7 @@ public sealed class ClearCommand : ICommand {
|
||||||
var user = (SocketGuildUser)cmd.Context.User;
|
var user = (SocketGuildUser)cmd.Context.User;
|
||||||
await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(user.ToString()!));
|
await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(user.ToString()!));
|
||||||
|
|
||||||
cmd.Audit(string.Format(Messages.FeedbackMessagesCleared, (toDelete + 1).ToString()));
|
cmd.Audit(string.Format(Messages.FeedbackMessagesCleared, (toDelete + 1).ToString(),
|
||||||
|
Utils.MentionChannel(channel.Id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ public sealed class KickCommand : ICommand {
|
||||||
public string[] Aliases { get; } = { "kick", "кик", "выгнать" };
|
public string[] Aliases { get; } = { "kick", "кик", "выгнать" };
|
||||||
|
|
||||||
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
||||||
var toKick = cmd.GetMember(args, cleanArgs, 0, "ToKick");
|
var toKick = cmd.GetMember(args, 0, "ToKick");
|
||||||
if (toKick is null || !cmd.HasPermission(GuildPermission.KickMembers)) return;
|
if (toKick is null || !cmd.HasPermission(GuildPermission.KickMembers)) return;
|
||||||
|
|
||||||
if (cmd.CanInteractWith(toKick, "Kick"))
|
if (cmd.CanInteractWith(toKick, "Kick"))
|
||||||
|
@ -24,6 +24,7 @@ public sealed class KickCommand : ICommand {
|
||||||
Utils.Wrap(reason)));
|
Utils.Wrap(reason)));
|
||||||
|
|
||||||
GuildData.Get(cmd.Context.Guild).MemberData[toKick.Id].Roles.Clear();
|
GuildData.Get(cmd.Context.Guild).MemberData[toKick.Id].Roles.Clear();
|
||||||
|
cmd.ConfigWriteScheduled = true;
|
||||||
|
|
||||||
await toKick.KickAsync(guildKickMessage);
|
await toKick.KickAsync(guildKickMessage);
|
||||||
var format = string.Format(Messages.FeedbackMemberKicked, toKick.Mention, Utils.Wrap(reason));
|
var format = string.Format(Messages.FeedbackMemberKicked, toKick.Mention, Utils.Wrap(reason));
|
||||||
|
|
|
@ -8,7 +8,7 @@ public sealed class MuteCommand : ICommand {
|
||||||
public string[] Aliases { get; } = { "mute", "timeout", "заглушить", "мут" };
|
public string[] Aliases { get; } = { "mute", "timeout", "заглушить", "мут" };
|
||||||
|
|
||||||
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
||||||
var toMute = cmd.GetMember(args, cleanArgs, 0, "ToMute");
|
var toMute = cmd.GetMember(args, 0, "ToMute");
|
||||||
if (toMute is null) return;
|
if (toMute is null) return;
|
||||||
|
|
||||||
var duration = CommandProcessor.GetTimeSpan(args, 1);
|
var duration = CommandProcessor.GetTimeSpan(args, 1);
|
||||||
|
@ -40,8 +40,6 @@ public sealed class MuteCommand : ICommand {
|
||||||
await toMute.RemoveRolesAsync(toMute.Roles, requestOptions);
|
await toMute.RemoveRolesAsync(toMute.Roles, requestOptions);
|
||||||
|
|
||||||
await toMute.AddRoleAsync(role, requestOptions);
|
await toMute.AddRoleAsync(role, requestOptions);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -56,6 +54,9 @@ public sealed class MuteCommand : ICommand {
|
||||||
await toMute.SetTimeOutAsync(duration, requestOptions);
|
await toMute.SetTimeOutAsync(duration, requestOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.MemberData[toMute.Id].MutedUntil = DateTimeOffset.Now.Add(duration);
|
||||||
|
cmd.ConfigWriteScheduled = true;
|
||||||
|
|
||||||
var feedback = string.Format(Messages.FeedbackMemberMuted, toMute.Mention,
|
var feedback = string.Format(Messages.FeedbackMemberMuted, toMute.Mention,
|
||||||
Utils.GetHumanizedTimeOffset(duration),
|
Utils.GetHumanizedTimeOffset(duration),
|
||||||
Utils.Wrap(reason));
|
Utils.Wrap(reason));
|
||||||
|
|
|
@ -6,6 +6,7 @@ public sealed class RemindCommand : ICommand {
|
||||||
public string[] Aliases { get; } = { "remind", "reminder", "remindme", "напомни", "напомнить", "напоминание" };
|
public string[] Aliases { get; } = { "remind", "reminder", "remindme", "напомни", "напомнить", "напоминание" };
|
||||||
|
|
||||||
public Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
public Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
||||||
|
// TODO: actually make this good
|
||||||
var remindIn = CommandProcessor.GetTimeSpan(args, 0);
|
var remindIn = CommandProcessor.GetTimeSpan(args, 0);
|
||||||
var reminderText = cmd.GetRemaining(cleanArgs, 1, "ReminderText");
|
var reminderText = cmd.GetRemaining(cleanArgs, 1, "ReminderText");
|
||||||
if (reminderText is not null)
|
if (reminderText is not null)
|
||||||
|
@ -15,6 +16,8 @@ public sealed class RemindCommand : ICommand {
|
||||||
ReminderChannel = cmd.Context.Channel.Id
|
ReminderChannel = cmd.Context.Channel.Id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cmd.ConfigWriteScheduled = true;
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ public sealed class UnmuteCommand : ICommand {
|
||||||
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
public async Task RunAsync(CommandProcessor cmd, string[] args, string[] cleanArgs) {
|
||||||
if (!cmd.HasPermission(GuildPermission.ModerateMembers)) return;
|
if (!cmd.HasPermission(GuildPermission.ModerateMembers)) return;
|
||||||
|
|
||||||
var toUnmute = cmd.GetMember(args, cleanArgs, 0, "ToUnmute");
|
var toUnmute = cmd.GetMember(args, 0, "ToUnmute");
|
||||||
if (toUnmute is null) return;
|
if (toUnmute is null) return;
|
||||||
var reason = cmd.GetRemaining(args, 1, "UnmuteReason");
|
var reason = cmd.GetRemaining(args, 1, "UnmuteReason");
|
||||||
if (reason is not null && cmd.CanInteractWith(toUnmute, "Unmute"))
|
if (reason is not null && cmd.CanInteractWith(toUnmute, "Unmute"))
|
||||||
|
@ -27,6 +27,8 @@ public sealed class UnmuteCommand : ICommand {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.ConfigWriteScheduled = true;
|
||||||
|
|
||||||
var feedback = string.Format(Messages.FeedbackMemberUnmuted, toUnmute.Mention, Utils.Wrap(reason));
|
var feedback = string.Format(Messages.FeedbackMemberUnmuted, toUnmute.Mention, Utils.Wrap(reason));
|
||||||
cmd.Reply(feedback, ReplyEmojis.Unmuted);
|
cmd.Reply(feedback, ReplyEmojis.Unmuted);
|
||||||
cmd.Audit(feedback);
|
cmd.Audit(feedback);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
@ -24,10 +25,17 @@ public record GuildData {
|
||||||
{ "AutoStartEvents", "false" }
|
{ "AutoStartEvents", "false" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<ulong, GuildData> GuildDataDictionary = new();
|
public static readonly ConcurrentDictionary<ulong, GuildData> GuildDataDictionary = new();
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions Options = new() {
|
||||||
|
IncludeFields = true,
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
|
||||||
private readonly string _configurationFile;
|
private readonly string _configurationFile;
|
||||||
|
|
||||||
|
private readonly ulong _id;
|
||||||
|
|
||||||
public readonly List<ulong> EarlyNotifications = new();
|
public readonly List<ulong> EarlyNotifications = new();
|
||||||
|
|
||||||
public readonly Dictionary<ulong, MemberData> MemberData;
|
public readonly Dictionary<ulong, MemberData> MemberData;
|
||||||
|
@ -38,17 +46,16 @@ public record GuildData {
|
||||||
private SocketTextChannel? _cachedPrivateFeedbackChannel;
|
private SocketTextChannel? _cachedPrivateFeedbackChannel;
|
||||||
private SocketTextChannel? _cachedPublicFeedbackChannel;
|
private SocketTextChannel? _cachedPublicFeedbackChannel;
|
||||||
|
|
||||||
private ulong _id;
|
|
||||||
|
|
||||||
[SuppressMessage("Performance", "CA1853:Unnecessary call to \'Dictionary.ContainsKey(key)\'")]
|
[SuppressMessage("Performance", "CA1853:Unnecessary call to \'Dictionary.ContainsKey(key)\'")]
|
||||||
// https://github.com/dotnet/roslyn-analyzers/issues/6377
|
// https://github.com/dotnet/roslyn-analyzers/issues/6377
|
||||||
private GuildData(SocketGuild guild) {
|
private GuildData(SocketGuild guild) {
|
||||||
|
_id = guild.Id;
|
||||||
var idString = $"{_id}";
|
var idString = $"{_id}";
|
||||||
var memberDataDir = $"{_id}/MemberData";
|
var memberDataDir = $"{_id}/MemberData";
|
||||||
_configurationFile = $"{_id}/Configuration.json";
|
_configurationFile = $"{_id}/Configuration.json";
|
||||||
if (!Directory.Exists(idString)) Directory.CreateDirectory(idString);
|
if (!Directory.Exists(idString)) Directory.CreateDirectory(idString);
|
||||||
if (!Directory.Exists(memberDataDir)) Directory.CreateDirectory(memberDataDir);
|
if (!Directory.Exists(memberDataDir)) Directory.CreateDirectory(memberDataDir);
|
||||||
if (!File.Exists(_configurationFile)) File.Create(_configurationFile).Dispose();
|
if (!File.Exists(_configurationFile)) File.WriteAllText(_configurationFile, "{}");
|
||||||
Preferences
|
Preferences
|
||||||
= JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(_configurationFile)) ??
|
= JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(_configurationFile)) ??
|
||||||
new Dictionary<string, string>();
|
new Dictionary<string, string>();
|
||||||
|
@ -64,16 +71,17 @@ public record GuildData {
|
||||||
MemberData = new Dictionary<ulong, MemberData>();
|
MemberData = new Dictionary<ulong, MemberData>();
|
||||||
foreach (var data in Directory.GetFiles(memberDataDir)) {
|
foreach (var data in Directory.GetFiles(memberDataDir)) {
|
||||||
var deserialised
|
var deserialised
|
||||||
= JsonSerializer.Deserialize<MemberData>(File.ReadAllText($"{_id}/MemberData/{data}.json"));
|
= JsonSerializer.Deserialize<MemberData>(File.ReadAllText(data), Options);
|
||||||
MemberData.Add(deserialised!.Id, deserialised);
|
MemberData.Add(deserialised!.Id, deserialised);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guild.DownloadUsersAsync().Wait();
|
||||||
foreach (var member in guild.Users) {
|
foreach (var member in guild.Users.Where(user => !user.IsBot)) {
|
||||||
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().ToUnixTimeSeconds(), memberData.BannedUntil.ToUnixTimeSeconds()) >
|
Math.Max(memberData.LeftAt.Last().ToUnixTimeSeconds(),
|
||||||
|
memberData.BannedUntil?.ToUnixTimeSeconds() ?? 0) >
|
||||||
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);
|
||||||
|
@ -117,10 +125,8 @@ public record GuildData {
|
||||||
|
|
||||||
public static GuildData Get(SocketGuild guild) {
|
public static GuildData Get(SocketGuild guild) {
|
||||||
if (GuildDataDictionary.TryGetValue(guild.Id, out var stored)) return stored;
|
if (GuildDataDictionary.TryGetValue(guild.Id, out var stored)) return stored;
|
||||||
var newData = new GuildData(guild) {
|
var newData = new GuildData(guild);
|
||||||
_id = guild.Id
|
while (!GuildDataDictionary.ContainsKey(guild.Id)) GuildDataDictionary.TryAdd(guild.Id, newData);
|
||||||
};
|
|
||||||
GuildDataDictionary.Add(guild.Id, newData);
|
|
||||||
return newData;
|
return newData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +137,6 @@ public record GuildData {
|
||||||
if (saveMemberData)
|
if (saveMemberData)
|
||||||
foreach (var data in MemberData.Values)
|
foreach (var data in MemberData.Values)
|
||||||
await File.WriteAllTextAsync($"{_id}/MemberData/{data.Id}.json",
|
await File.WriteAllTextAsync($"{_id}/MemberData/{data.Id}.json",
|
||||||
JsonSerializer.Serialize(data));
|
JsonSerializer.Serialize(data, Options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
namespace Boyfriend.Data;
|
namespace Boyfriend.Data;
|
||||||
|
|
||||||
public record MemberData {
|
public record MemberData {
|
||||||
public DateTimeOffset BannedUntil;
|
public DateTimeOffset? BannedUntil;
|
||||||
public ulong Id;
|
public ulong Id;
|
||||||
public bool IsInGuild;
|
public bool IsInGuild;
|
||||||
public List<DateTimeOffset> JoinedAt;
|
public List<DateTimeOffset> JoinedAt;
|
||||||
public List<DateTimeOffset> LeftAt;
|
public List<DateTimeOffset> LeftAt;
|
||||||
public DateTimeOffset MutedUntil;
|
public DateTimeOffset? MutedUntil;
|
||||||
public List<Reminder> Reminders;
|
public List<Reminder> Reminders;
|
||||||
public List<ulong> Roles;
|
public List<ulong> Roles;
|
||||||
|
|
||||||
|
@ -19,7 +19,5 @@ public record MemberData {
|
||||||
LeftAt = new List<DateTimeOffset>();
|
LeftAt = new List<DateTimeOffset>();
|
||||||
Roles = user.RoleIds.ToList();
|
Roles = user.RoleIds.ToList();
|
||||||
Reminders = new List<Reminder>();
|
Reminders = new List<Reminder>();
|
||||||
MutedUntil = DateTimeOffset.MinValue;
|
|
||||||
BannedUntil = DateTimeOffset.MinValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,9 @@ public static class EventHandler {
|
||||||
var i = Random.Shared.Next(3);
|
var i = Random.Shared.Next(3);
|
||||||
|
|
||||||
foreach (var guild in Client.Guilds) {
|
foreach (var guild in Client.Guilds) {
|
||||||
var config = GuildData.Get(guild).Preferences;
|
var data = GuildData.Get(guild);
|
||||||
var channel = guild.GetTextChannel(Utils.ParseMention(config["BotLogChannel"]));
|
var config = data.Preferences;
|
||||||
|
var channel = data.PrivateFeedbackChannel;
|
||||||
Utils.SetCurrentLanguage(guild);
|
Utils.SetCurrentLanguage(guild);
|
||||||
|
|
||||||
if (config["ReceiveStartupMessages"] is not "true" || channel is null) continue;
|
if (config["ReceiveStartupMessages"] is not "true" || channel is null) continue;
|
||||||
|
@ -101,12 +102,13 @@ public static class EventHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task UserJoinedEvent(SocketGuildUser user) {
|
private static async Task UserJoinedEvent(SocketGuildUser user) {
|
||||||
|
if (user.IsBot) return;
|
||||||
var guild = user.Guild;
|
var guild = user.Guild;
|
||||||
var data = GuildData.Get(guild);
|
var data = GuildData.Get(guild);
|
||||||
var config = data.Preferences;
|
var config = data.Preferences;
|
||||||
Utils.SetCurrentLanguage(guild);
|
Utils.SetCurrentLanguage(guild);
|
||||||
|
|
||||||
if (config["SendWelcomeMessages"] is "true")
|
if (config["SendWelcomeMessages"] is "true" && data.PublicFeedbackChannel is not null)
|
||||||
await Utils.SilentSendAsync(data.PublicFeedbackChannel,
|
await Utils.SilentSendAsync(data.PublicFeedbackChannel,
|
||||||
string.Format(config["WelcomeMessage"] is "default"
|
string.Format(config["WelcomeMessage"] is "default"
|
||||||
? Messages.DefaultWelcomeMessage
|
? Messages.DefaultWelcomeMessage
|
||||||
|
@ -117,7 +119,7 @@ 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 = DateTimeOffset.MinValue;
|
memberData.BannedUntil = null;
|
||||||
if (memberData.LeftAt.Count > 0) {
|
if (memberData.LeftAt.Count > 0) {
|
||||||
if (memberData.JoinedAt.Contains(user.JoinedAt!.Value))
|
if (memberData.JoinedAt.Contains(user.JoinedAt!.Value))
|
||||||
throw new UnreachableException();
|
throw new UnreachableException();
|
||||||
|
@ -127,9 +129,6 @@ public static class EventHandler {
|
||||||
if (memberData.MutedUntil < DateTimeOffset.Now) {
|
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
|
|
||||||
await user.SetTimeOutAsync(DateTimeOffset.Now - 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);
|
||||||
} else if (config["ReturnRolesOnRejoin"] is "true") { await user.AddRolesAsync(memberData.Roles); }
|
} else if (config["ReturnRolesOnRejoin"] is "true") { await user.AddRolesAsync(memberData.Roles); }
|
||||||
|
|
57
Boyfriend/Messages.Designer.cs
generated
57
Boyfriend/Messages.Designer.cs
generated
|
@ -284,6 +284,15 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Adds a reminder.
|
||||||
|
/// </summary>
|
||||||
|
internal static string CommandDescriptionRemind {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CommandDescriptionRemind", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Allows you to change certain preferences for this guild.
|
/// Looks up a localized string similar to Allows you to change certain preferences for this guild.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -492,7 +501,7 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You need to specify a guild member instead of {0}!.
|
/// Looks up a localized string similar to You did not specify a member of this guild!.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string InvalidMember {
|
internal static string InvalidMember {
|
||||||
get {
|
get {
|
||||||
|
@ -608,6 +617,15 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to You need to specify reminder text!.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MissingReminderText {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("MissingReminderText", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You need to specify a setting to change!.
|
/// Looks up a localized string similar to You need to specify a setting to change!.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -717,11 +735,11 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Bot log channel.
|
/// Looks up a localized string similar to Automatically start scheduled events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string SettingsBotLogChannel {
|
internal static string SettingsAutoStartEvents {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("SettingsBotLogChannel", resourceCulture);
|
return ResourceManager.GetString("SettingsAutoStartEvents", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,6 +824,24 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Channel for private notifications.
|
||||||
|
/// </summary>
|
||||||
|
internal static string SettingsPrivateFeedbackChannel {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SettingsPrivateFeedbackChannel", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Channel for public notifications.
|
||||||
|
/// </summary>
|
||||||
|
internal static string SettingsPublicFeedbackChannel {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SettingsPublicFeedbackChannel", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Receive startup messages.
|
/// Looks up a localized string similar to Receive startup messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -824,6 +860,15 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Return roles on rejoin.
|
||||||
|
/// </summary>
|
||||||
|
internal static string SettingsReturnRolesOnRejoin {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SettingsReturnRolesOnRejoin", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Send welcome messages.
|
/// Looks up a localized string similar to Send welcome messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1068,7 +1113,7 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You were banned by {0} in guild {1} for {2}.
|
/// Looks up a localized string similar to You were banned by {0} in guild `{1}` for {2}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string YouWereBanned {
|
internal static string YouWereBanned {
|
||||||
get {
|
get {
|
||||||
|
@ -1077,7 +1122,7 @@ namespace Boyfriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You were kicked by {0} in guild {1} for {2}.
|
/// Looks up a localized string similar to You were kicked by {0} in guild `{1}` for {2}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string YouWereKicked {
|
internal static string YouWereKicked {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
<value>You do not have permission to execute this command!</value>
|
<value>You do not have permission to execute this command!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereBanned" xml:space="preserve">
|
<data name="YouWereBanned" xml:space="preserve">
|
||||||
<value>You were banned by {0} in guild {1} for {2}</value>
|
<value>You were banned by {0} in guild `{1}` for {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PunishmentExpired" xml:space="preserve">
|
<data name="PunishmentExpired" xml:space="preserve">
|
||||||
<value>Punishment expired</value>
|
<value>Punishment expired</value>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<value>Command help:</value>
|
<value>Command help:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereKicked" xml:space="preserve">
|
<data name="YouWereKicked" xml:space="preserve">
|
||||||
<value>You were kicked by {0} in guild {1} for {2}</value>
|
<value>You were kicked by {0} in guild `{1}` for {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Milliseconds" xml:space="preserve">
|
<data name="Milliseconds" xml:space="preserve">
|
||||||
<value>ms</value>
|
<value>ms</value>
|
||||||
|
@ -192,9 +192,6 @@
|
||||||
<data name="SettingsMuteRole" xml:space="preserve">
|
<data name="SettingsMuteRole" xml:space="preserve">
|
||||||
<value>Mute role</value>
|
<value>Mute role</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsBotLogChannel" xml:space="preserve">
|
|
||||||
<value>Bot log channel</value>
|
|
||||||
</data>
|
|
||||||
<data name="LanguageNotSupported" xml:space="preserve">
|
<data name="LanguageNotSupported" xml:space="preserve">
|
||||||
<value>Language not supported! Supported languages:</value>
|
<value>Language not supported! Supported languages:</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -337,7 +334,7 @@
|
||||||
<value>You need to specify a guild member!</value>
|
<value>You need to specify a guild member!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidMember" xml:space="preserve">
|
<data name="InvalidMember" xml:space="preserve">
|
||||||
<value>You need to specify a guild member instead of {0}!</value>
|
<value>You did not specify a member of this guild!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserCannotBanMembers" xml:space="preserve">
|
<data name="UserCannotBanMembers" xml:space="preserve">
|
||||||
<value>You cannot ban users from this guild!</value>
|
<value>You cannot ban users from this guild!</value>
|
||||||
|
@ -459,4 +456,22 @@
|
||||||
<data name="SettingsStarterRole" xml:space="preserve">
|
<data name="SettingsStarterRole" xml:space="preserve">
|
||||||
<value>Starter role</value>
|
<value>Starter role</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CommandDescriptionRemind" xml:space="preserve">
|
||||||
|
<value>Adds a reminder</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPublicFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>Channel for public notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPrivateFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>Channel for private notifications</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsReturnRolesOnRejoin" xml:space="preserve">
|
||||||
|
<value>Return roles on rejoin</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsAutoStartEvents" xml:space="preserve">
|
||||||
|
<value>Automatically start scheduled events</value>
|
||||||
|
</data>
|
||||||
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
|
<value>You need to specify reminder text!</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
<value>У тебя недостаточно прав для выполнения этой команды!</value>
|
<value>У тебя недостаточно прав для выполнения этой команды!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereBanned" xml:space="preserve">
|
<data name="YouWereBanned" xml:space="preserve">
|
||||||
<value>Тебя забанил {0} на сервере {1} за {2}</value>
|
<value>Тебя забанил {0} на сервере `{1}` за {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PunishmentExpired" xml:space="preserve">
|
<data name="PunishmentExpired" xml:space="preserve">
|
||||||
<value>Время наказания истекло</value>
|
<value>Время наказания истекло</value>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<value>Справка по командам:</value>
|
<value>Справка по командам:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereKicked" xml:space="preserve">
|
<data name="YouWereKicked" xml:space="preserve">
|
||||||
<value>Тебя кикнул {0} на сервере {1} за {2}</value>
|
<value>Тебя кикнул {0} на сервере `{1}` за {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Milliseconds" xml:space="preserve">
|
<data name="Milliseconds" xml:space="preserve">
|
||||||
<value>мс</value>
|
<value>мс</value>
|
||||||
|
@ -192,9 +192,6 @@
|
||||||
<data name="SettingsMuteRole" xml:space="preserve">
|
<data name="SettingsMuteRole" xml:space="preserve">
|
||||||
<value>Роль мута</value>
|
<value>Роль мута</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsBotLogChannel" xml:space="preserve">
|
|
||||||
<value>Канал бот-уведомлений</value>
|
|
||||||
</data>
|
|
||||||
<data name="LanguageNotSupported" xml:space="preserve">
|
<data name="LanguageNotSupported" xml:space="preserve">
|
||||||
<value>Язык не поддерживается! Поддерживаемые языки:</value>
|
<value>Язык не поддерживается! Поддерживаемые языки:</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -337,7 +334,7 @@
|
||||||
<value>Надо указать участника сервера!</value>
|
<value>Надо указать участника сервера!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidMember" xml:space="preserve">
|
<data name="InvalidMember" xml:space="preserve">
|
||||||
<value>Надо указать участника сервера вместо {0}!</value>
|
<value>Тебе надо указать участника этого сервера!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserCannotBanMembers" xml:space="preserve">
|
<data name="UserCannotBanMembers" xml:space="preserve">
|
||||||
<value>Ты не можешь банить пользователей на этом сервере!</value>
|
<value>Ты не можешь банить пользователей на этом сервере!</value>
|
||||||
|
@ -459,4 +456,22 @@
|
||||||
<data name="SettingsStarterRole" xml:space="preserve">
|
<data name="SettingsStarterRole" xml:space="preserve">
|
||||||
<value>Начальная роль</value>
|
<value>Начальная роль</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CommandDescriptionRemind" xml:space="preserve">
|
||||||
|
<value>Добавляет напоминание</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPublicFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>Канал для публичных уведомлений</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPrivateFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>Канал для приватных уведомлений</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsReturnRolesOnRejoin" xml:space="preserve">
|
||||||
|
<value>Возвращать роли при перезаходе</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsAutoStartEvents" xml:space="preserve">
|
||||||
|
<value>Автоматически начинать события</value>
|
||||||
|
</data>
|
||||||
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
|
<value>Тебе нужно указать текст напоминания!</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
<value>у тебя прав нету, твои проблемы.</value>
|
<value>у тебя прав нету, твои проблемы.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereBanned" xml:space="preserve">
|
<data name="YouWereBanned" xml:space="preserve">
|
||||||
<value>здарова, тебя крч забанил {0} на сервере {1} за {2}</value>
|
<value>здарова, тебя крч забанил {0} на сервере `{1}` за {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PunishmentExpired" xml:space="preserve">
|
<data name="PunishmentExpired" xml:space="preserve">
|
||||||
<value>время бана закончиловсь</value>
|
<value>время бана закончиловсь</value>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<value>туториал по приколам:</value>
|
<value>туториал по приколам:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="YouWereKicked" xml:space="preserve">
|
<data name="YouWereKicked" xml:space="preserve">
|
||||||
<value>здарова, тебя крч кикнул {0} на сервере {1} за {2}</value>
|
<value>здарова, тебя крч кикнул {0} на сервере `{1}` за {2}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Milliseconds" xml:space="preserve">
|
<data name="Milliseconds" xml:space="preserve">
|
||||||
<value>мс</value>
|
<value>мс</value>
|
||||||
|
@ -192,9 +192,6 @@
|
||||||
<data name="SettingsMuteRole" xml:space="preserve">
|
<data name="SettingsMuteRole" xml:space="preserve">
|
||||||
<value>роль замученного</value>
|
<value>роль замученного</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsBotLogChannel" xml:space="preserve">
|
|
||||||
<value>канал бот-уведомлений</value>
|
|
||||||
</data>
|
|
||||||
<data name="LanguageNotSupported" xml:space="preserve">
|
<data name="LanguageNotSupported" xml:space="preserve">
|
||||||
<value>такого языка нету, ты шо, есть только такие:</value>
|
<value>такого языка нету, ты шо, есть только такие:</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -337,7 +334,7 @@
|
||||||
<value>укажи самого шизика</value>
|
<value>укажи самого шизика</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidMember" xml:space="preserve">
|
<data name="InvalidMember" xml:space="preserve">
|
||||||
<value>укажи шизоида сервера вместо {0}!</value>
|
<value>укажи шизоида сервера!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserCannotBanMembers" xml:space="preserve">
|
<data name="UserCannotBanMembers" xml:space="preserve">
|
||||||
<value>бан</value>
|
<value>бан</value>
|
||||||
|
@ -459,4 +456,22 @@
|
||||||
<data name="SettingsStarterRole" xml:space="preserve">
|
<data name="SettingsStarterRole" xml:space="preserve">
|
||||||
<value>базовое звание</value>
|
<value>базовое звание</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CommandDescriptionRemind" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPrivateFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsPublicFeedbackChannel" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsReturnRolesOnRejoin" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
|
<data name="SettingsAutoStartEvents" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
|
<data name="MissingReminderText" xml:space="preserve">
|
||||||
|
<value>TODO</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -147,6 +147,7 @@ public static partial class Utils {
|
||||||
if (!toUnmute.Roles.Contains(role)) return false;
|
if (!toUnmute.Roles.Contains(role)) return false;
|
||||||
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);
|
||||||
|
data.MemberData[toUnmute.Id].MutedUntil = null;
|
||||||
} else {
|
} else {
|
||||||
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value < DateTimeOffset.Now) return false;
|
if (toUnmute.TimedOutUntil is null || toUnmute.TimedOutUntil.Value < DateTimeOffset.Now) return false;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue