diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 09ee1314..050e840f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -1,19 +1,15 @@ ο»Ώusing Discord; using Discord.Commands; -using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Modules.Permissions; using NadekoBot.Services; +using NadekoBot.Services.Administration; using NadekoBot.Services.Database.Models; -using NLog; using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; +using static NadekoBot.Services.Administration.LogCommandService; namespace NadekoBot.Modules.Administration { @@ -22,878 +18,13 @@ namespace NadekoBot.Modules.Administration [Group] public class LogCommands : NadekoSubmodule { - private static DiscordShardedClient Client { get; } - private new static Logger _log { get; } + private readonly LogCommandService _lc; + private readonly DbService _db; - private static string PrettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; - private static string CurrentTime => $"{DateTime.Now:HH:mm:ss}"; - - public static ConcurrentDictionary GuildLogSettings { get; } - - private static ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); - private static readonly Timer _timerReference; - - static LogCommands() + public LogCommands(LogCommandService lc, DbService db) { - Client = _client; - _log = LogManager.GetCurrentClassLogger(); - var sw = Stopwatch.StartNew(); - - GuildLogSettings = new ConcurrentDictionary(gcs - .ToDictionary(g => g.GuildId, g => g.LogSetting)); - - _timerReference = new Timer(async (state) => - { - try - { - var keys = PresenceUpdates.Keys.ToList(); - - await Task.WhenAll(keys.Select(async key => - { - if (PresenceUpdates.TryRemove(key, out List messages)) - try { await key.SendConfirmAsync(key.Guild.GetLogText("presence_updates"), string.Join(Environment.NewLine, messages)); } - catch - { - // ignored - } - })); - } - catch (Exception ex) - { - _log.Warn(ex); - } - }, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); - - sw.Stop(); - _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); - - //_client.MessageReceived += _client_MessageReceived; - Client.MessageUpdated += _client_MessageUpdated; - Client.MessageDeleted += _client_MessageDeleted; - Client.UserBanned += _client_UserBanned; - Client.UserUnbanned += _client_UserUnbanned; - Client.UserJoined += _client_UserJoined; - Client.UserLeft += _client_UserLeft; - Client.UserPresenceUpdated += _client_UserPresenceUpdated; - Client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; - Client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; - Client.GuildMemberUpdated += _client_GuildUserUpdated; -#if !GLOBAL_NADEKO - Client.UserUpdated += _client_UserUpdated; -#endif - Client.ChannelCreated += _client_ChannelCreated; - Client.ChannelDestroyed += _client_ChannelDestroyed; - Client.ChannelUpdated += _client_ChannelUpdated; - - MuteCommands.UserMuted += MuteCommands_UserMuted; - MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; - } - - private static async Task _client_UserUpdated(SocketUser before, SocketUser uAfter) - { - try - { - var after = uAfter as SocketGuildUser; - - if (after == null) - return; - - var g = after.Guild; - - if (!GuildLogSettings.TryGetValue(g.Id, out LogSetting logSetting) - || (logSetting.UserUpdatedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) - return; - - var embed = new EmbedBuilder(); - - - if (before.Username != after.Username) - { - embed.WithTitle("πŸ‘₯ " + g.GetLogText("username_changed")) - .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") - .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) - .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) - .WithFooter(fb => fb.WithText(CurrentTime)) - .WithOkColor(); - } - else if (before.AvatarId != after.AvatarId) - { - embed.WithTitle("πŸ‘₯" + g.GetLogText("avatar_changed")) - .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") - .WithThumbnailUrl(before.GetAvatarUrl()) - .WithImageUrl(after.GetAvatarUrl()) - .WithFooter(fb => fb.WithText(CurrentTime)) - .WithOkColor(); - } - else - { - return; - } - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - - //var guildsMemberOf = _client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); - //foreach (var g in guildsMemberOf) - //{ - // LogSetting logSetting; - // if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) - // || (logSetting.UserUpdatedId == null)) - // return; - - // ITextChannel logChannel; - // if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) - // return; - - // try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } - //} - } - catch - { - // ignored - } - } - - private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) - { - try - { - var usr = iusr as IGuildUser; - if (usr == null) - return; - - var beforeVch = before.VoiceChannel; - var afterVch = after.VoiceChannel; - - if (beforeVch == afterVch) - return; - - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.LogVoicePresenceTTSId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTTS)) == null) - return; - - var str = ""; - if (beforeVch?.Guild == afterVch?.Guild) - { - str = logChannel.Guild.GetLogText("moved", usr.Username, beforeVch?.Name, afterVch?.Name); - } - else if (beforeVch == null) - { - str = logChannel.Guild.GetLogText("joined", usr.Username, afterVch.Name); - } - else if (afterVch == null) - { - str = logChannel.Guild.GetLogText("left", usr.Username, beforeVch.Name); - } - var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); - toDelete.DeleteAfter(5); - } - catch - { - // ignored - } - } - - private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType) - { - try - { - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.UserMutedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) - return; - var mutes = ""; - var mutedLocalized = logChannel.Guild.GetLogText("muted_sn"); - switch (muteType) - { - case MuteCommands.MuteType.Voice: - mutes = "πŸ”‡ " + logChannel.Guild.GetLogText("xmuted_voice", mutedLocalized); - break; - case MuteCommands.MuteType.Chat: - mutes = "πŸ”‡ " + logChannel.Guild.GetLogText("xmuted_text", mutedLocalized); - break; - case MuteCommands.MuteType.All: - mutes = "πŸ”‡ " + logChannel.Guild.GetLogText("xmuted_text_and_voice", mutedLocalized); - break; - } - - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) - .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") - .WithFooter(fb => fb.WithText(CurrentTime)) - .WithOkColor(); - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType) - { - try - { - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.UserMutedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) - return; - - var mutes = ""; - var unmutedLocalized = logChannel.Guild.GetLogText("unmuted_sn"); - switch (muteType) - { - case MuteCommands.MuteType.Voice: - mutes = "πŸ”Š " + logChannel.Guild.GetLogText("xmuted_voice", unmutedLocalized); - break; - case MuteCommands.MuteType.Chat: - mutes = "πŸ”Š " + logChannel.Guild.GetLogText("xmuted_text", unmutedLocalized); - break; - case MuteCommands.MuteType.All: - mutes = "πŸ”Š " + logChannel.Guild.GetLogText("xmuted_text_and_voice", unmutedLocalized); - break; - } - - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) - .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") - .WithFooter(fb => fb.WithText($"{CurrentTime}")) - .WithOkColor(); - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection) - { - try - { - if (users.Length == 0) - return; - - if (!GuildLogSettings.TryGetValue(users.First().Guild.Id, out LogSetting logSetting) - || (logSetting.LogOtherId == null)) - return; - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(users.First().Guild, logSetting, LogType.Other)) == null) - return; - - var punishment = ""; - switch (action) - { - case PunishmentAction.Mute: - punishment = "πŸ”‡ " + logChannel.Guild.GetLogText("muted_pl").ToUpperInvariant(); - break; - case PunishmentAction.Kick: - punishment = "πŸ‘’ " + logChannel.Guild.GetLogText("kicked_pl").ToUpperInvariant(); - break; - case PunishmentAction.Softban: - punishment = "☣ " + logChannel.Guild.GetLogText("soft_banned_pl").ToUpperInvariant(); - break; - case PunishmentAction.Ban: - punishment = "⛔️ " + logChannel.Guild.GetLogText("banned_pl").ToUpperInvariant(); - break; - } - - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"πŸ›‘ Anti-{protection}")) - .WithTitle(logChannel.Guild.GetLogText("users") + " " + punishment) - .WithDescription(string.Join("\n", users.Select(u => u.ToString()))) - .WithFooter(fb => fb.WithText($"{CurrentTime}")) - .WithOkColor(); - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) - { - try - { - if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting) - || (logSetting.UserUpdatedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null) - return; - var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(CurrentTime)) - .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); - if (before.Nickname != after.Nickname) - { - embed.WithAuthor(eab => eab.WithName("πŸ‘₯ " + logChannel.Guild.GetLogText("nick_change"))) - - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}")) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}")); - } - else if (!before.Roles.SequenceEqual(after.Roles)) - { - if (before.Roles.Count < after.Roles.Count) - { - var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => r.Name); - embed.WithAuthor(eab => eab.WithName("βš” " + logChannel.Guild.GetLogText("user_role_add"))) - .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); - } - else if (before.Roles.Count > after.Roles.Count) - { - var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r)).Select(r => r.Name); - embed.WithAuthor(eab => eab.WithName("βš” " + logChannel.Guild.GetLogText("user_role_rem"))) - .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); - } - } - else - return; - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) - { - try - { - var before = cbefore as IGuildChannel; - if (before == null) - return; - var after = (IGuildChannel)cafter; - - if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting) - || (logSetting.ChannelUpdatedId == null) - || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) == null) - return; - - var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(CurrentTime)); - - var beforeTextChannel = cbefore as ITextChannel; - var afterTextChannel = cafter as ITextChannel; - - if (before.Name != after.Name) - { - embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_name_change")) - .WithDescription($"{after} | {after.Id}") - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("ch_old_name")).WithValue(before.Name)); - } - else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) - { - embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_topic_change")) - .WithDescription($"{after} | {after.Id}") - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_topic")).WithValue(beforeTextChannel?.Topic ?? "-")) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_topic")).WithValue(afterTextChannel?.Topic ?? "-")); - } - else - return; - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async Task _client_ChannelDestroyed(IChannel ich) - { - try - { - var ch = ich as IGuildChannel; - if (ch == null) - return; - - if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) - || (logSetting.ChannelDestroyedId == null) - || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null) - return; - string title; - if (ch is IVoiceChannel) - { - title = logChannel.Guild.GetLogText("voice_chan_destroyed"); - } - else - title = logChannel.Guild.GetLogText("text_chan_destroyed"); - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("πŸ†• " + title) - .WithDescription($"{ch.Name} | {ch.Id}") - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async Task _client_ChannelCreated(IChannel ich) - { - try - { - var ch = ich as IGuildChannel; - if (ch == null) - return; - - if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) - || (logSetting.ChannelCreatedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null) - return; - string title; - if (ch is IVoiceChannel) - { - title = logChannel.Guild.GetLogText("voice_chan_created"); - } - else - title = logChannel.Guild.GetLogText("text_chan_created"); - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("πŸ†• " + title) - .WithDescription($"{ch.Name} | {ch.Id}") - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex); } - } - - private static async Task _client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) - { - try - { - var usr = iusr as IGuildUser; - if (usr == null) - return; - - var beforeVch = before.VoiceChannel; - var afterVch = after.VoiceChannel; - - if (beforeVch == afterVch) - return; - - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.LogVoicePresenceId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresence)) == null) - return; - - string str = null; - if (beforeVch?.Guild == afterVch?.Guild) - { - str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + logChannel.Guild.GetLogText("user_vmoved", - "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), - Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? "")); - } - else if (beforeVch == null) - { - str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + logChannel.Guild.GetLogText("user_vjoined", - "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), - Format.Bold(afterVch.Name ?? "")); - } - else if (afterVch == null) - { - str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft", - "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), - Format.Bold(beforeVch.Name ?? "")); - } - if (str != null) - PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); - } - catch - { - // ignored - } - } - - private static async Task _client_UserPresenceUpdated(Optional optGuild, SocketUser usr, SocketPresence before, SocketPresence after) - { - try - { - var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild; - - if (guild == null) - return; - - if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) - || (logSetting.LogUserPresenceId == null) - || before.Status == after.Status) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) == null) - return; - string str = ""; - if (before.Status != after.Status) - str = "🎭" + Format.Code(PrettyCurrentTime) + - logChannel.Guild.GetLogText("user_status_change", - "πŸ‘€" + Format.Bold(usr.Username), - Format.Bold(after.Status.ToString())); - - //if (before.Game?.Name != after.Game?.Name) - //{ - // if (str != "") - // str += "\n"; - // str += $"πŸ‘Ύ`{prettyCurrentTime}`πŸ‘€__**{usr.Username}**__ is now playing **{after.Game?.Name}**."; - //} - - PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); - } - catch - { - // ignored - } - } - - private static async Task _client_UserLeft(IGuildUser usr) - { - try - { - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.UserLeftId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft)) == null) - return; - - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("❌ " + logChannel.Guild.GetLogText("user_left")) - .WithThumbnailUrl(usr.GetAvatarUrl()) - .WithDescription(usr.ToString()) - .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - private static async Task _client_UserJoined(IGuildUser usr) - { - try - { - if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) - || (logSetting.UserJoinedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) == null) - return; - - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("βœ… " + logChannel.Guild.GetLogText("user_joined")) - .WithThumbnailUrl(usr.GetAvatarUrl()) - .WithDescription($"{usr}") - .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex); } - } - - private static async Task _client_UserUnbanned(IUser usr, IGuild guild) - { - try - { - if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) - || (logSetting.UserUnbannedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) == null) - return; - - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("♻️ " + logChannel.Guild.GetLogText("user_unbanned")) - .WithThumbnailUrl(usr.GetAvatarUrl()) - .WithDescription(usr.ToString()) - .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex); } - } - - private static async Task _client_UserBanned(IUser usr, IGuild guild) - { - try - { - if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) - || (logSetting.UserBannedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null) - return; - await logChannel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("🚫 " + logChannel.Guild.GetLogText("user_banned")) - .WithThumbnailUrl(usr.GetAvatarUrl()) - .WithDescription(usr.ToString()) - .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) - .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex); } - } - - private static async Task _client_MessageDeleted(Cacheable optMsg, ISocketMessageChannel ch) - { - - try - { - var msg = (optMsg.HasValue ? optMsg.Value : null) as IUserMessage; - if (msg == null || msg.IsAuthor()) - return; - - var channel = ch as ITextChannel; - if (channel == null) - return; - - if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) - || (logSetting.MessageDeletedId == null) - || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) == null || logChannel.Id == msg.Id) - return; - var embed = new EmbedBuilder() - .WithOkColor() - .WithTitle("πŸ—‘ " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name)) - .WithDescription(msg.Author.ToString()) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(string.IsNullOrWhiteSpace(msg.Content) ? "-" : msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) - .WithFooter(efb => efb.WithText(CurrentTime)); - if (msg.Attachments.Any()) - embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.Url))).WithIsInline(false)); - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch (Exception ex) - { - _log.Warn(ex); - // ignored - } - } - - private static async Task _client_MessageUpdated(Cacheable optmsg, SocketMessage imsg2, ISocketMessageChannel ch) - { - try - { - var after = imsg2 as IUserMessage; - if (after == null || after.IsAuthor()) - return; - - var before = (optmsg.HasValue ? optmsg.Value : null) as IUserMessage; - if (before == null) - return; - - var channel = ch as ITextChannel; - if (channel == null) - return; - - if (before.Content == after.Content) - return; - - if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) - || (logSetting.MessageUpdatedId == null) - || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) == null || logChannel.Id == after.Channel.Id) - return; - - var embed = new EmbedBuilder() - .WithOkColor() - .WithTitle("πŸ“ " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name)) - .WithDescription(after.Author.ToString()) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) - .WithFooter(efb => efb.WithText(CurrentTime)); - - await logChannel.EmbedAsync(embed).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - public enum LogType - { - Other, - MessageUpdated, - MessageDeleted, - UserJoined, - UserLeft, - UserBanned, - UserUnbanned, - UserUpdated, - ChannelCreated, - ChannelDestroyed, - ChannelUpdated, - UserPresence, - VoicePresence, - VoicePresenceTTS, - UserMuted - }; - - private static async Task TryGetLogChannel(IGuild guild, LogSetting logSetting, LogType logChannelType) - { - ulong? id = null; - switch (logChannelType) - { - case LogType.Other: - id = logSetting.LogOtherId; - break; - case LogType.MessageUpdated: - id = logSetting.MessageUpdatedId; - break; - case LogType.MessageDeleted: - id = logSetting.MessageDeletedId; - break; - case LogType.UserJoined: - id = logSetting.UserJoinedId; - break; - case LogType.UserLeft: - id = logSetting.UserLeftId; - break; - case LogType.UserBanned: - id = logSetting.UserBannedId; - break; - case LogType.UserUnbanned: - id = logSetting.UserUnbannedId; - break; - case LogType.UserUpdated: - id = logSetting.UserUpdatedId; - break; - case LogType.ChannelCreated: - id = logSetting.ChannelCreatedId; - break; - case LogType.ChannelDestroyed: - id = logSetting.ChannelDestroyedId; - break; - case LogType.ChannelUpdated: - id = logSetting.ChannelUpdatedId; - break; - case LogType.UserPresence: - id = logSetting.LogUserPresenceId; - break; - case LogType.VoicePresence: - id = logSetting.LogVoicePresenceId; - break; - case LogType.VoicePresenceTTS: - id = logSetting.LogVoicePresenceTTSId; - break; - case LogType.UserMuted: - id = logSetting.UserMutedId; - break; - } - - if (!id.HasValue) - { - UnsetLogSetting(guild.Id, logChannelType); - return null; - } - var channel = await guild.GetTextChannelAsync(id.Value).ConfigureAwait(false); - - if (channel == null) - { - UnsetLogSetting(guild.Id, logChannelType); - return null; - } - else - return channel; - } - - private static void UnsetLogSetting(ulong guildId, LogType logChannelType) - { - using (var uow = _db.UnitOfWork) - { - var newLogSetting = uow.GuildConfigs.LogSettingsFor(guildId).LogSetting; - switch (logChannelType) - { - case LogType.Other: - newLogSetting.LogOtherId = null; - break; - case LogType.MessageUpdated: - newLogSetting.MessageUpdatedId = null; - break; - case LogType.MessageDeleted: - newLogSetting.MessageDeletedId = null; - break; - case LogType.UserJoined: - newLogSetting.UserJoinedId = null; - break; - case LogType.UserLeft: - newLogSetting.UserLeftId = null; - break; - case LogType.UserBanned: - newLogSetting.UserBannedId = null; - break; - case LogType.UserUnbanned: - newLogSetting.UserUnbannedId = null; - break; - case LogType.UserUpdated: - newLogSetting.UserUpdatedId = null; - break; - case LogType.UserMuted: - newLogSetting.UserMutedId = null; - break; - case LogType.ChannelCreated: - newLogSetting.ChannelCreatedId = null; - break; - case LogType.ChannelDestroyed: - newLogSetting.ChannelDestroyedId = null; - break; - case LogType.ChannelUpdated: - newLogSetting.ChannelUpdatedId = null; - break; - case LogType.UserPresence: - newLogSetting.LogUserPresenceId = null; - break; - case LogType.VoicePresence: - newLogSetting.LogVoicePresenceId = null; - break; - case LogType.VoicePresenceTTS: - newLogSetting.LogVoicePresenceTTSId = null; - break; - } - GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting); - uow.Complete(); - } + _lc = lc; + _db = db; } public enum EnableDisable @@ -913,7 +44,7 @@ namespace NadekoBot.Modules.Administration using (var uow = _db.UnitOfWork) { logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting; - GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + _lc.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); logSetting.LogOtherId = logSetting.MessageUpdatedId = logSetting.MessageDeletedId = @@ -949,7 +80,7 @@ namespace NadekoBot.Modules.Administration using (var uow = _db.UnitOfWork) { var config = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id); - LogSetting logSetting = GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting); + LogSetting logSetting = _lc.GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting); removed = logSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == channel.Id); config.LogSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == channel.Id); if (removed == 0) @@ -989,7 +120,7 @@ namespace NadekoBot.Modules.Administration using (var uow = _db.UnitOfWork) { var logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting; - GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); + _lc.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting); switch (type) { case LogType.Other: @@ -1049,13 +180,4 @@ namespace NadekoBot.Modules.Administration } } } - - public static class GuildExtensions - { - public static string GetLogText(this IGuild guild, string key, params object[] replacements) - => NadekoTopLevelModule.GetTextStatic(key, - NadekoBot.Localization.GetCultureInfo(guild), - typeof(Administration).Name.ToLowerInvariant(), - replacements); - } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 9556f43c..1b80a935 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -101,8 +101,8 @@ namespace NadekoBot.Modules.Help if (alias != null) str += string.Format(" **/ `{0}`**", Prefix + alias); var embed = new EmbedBuilder() - .AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary()} {GetCommandRequirements(com)}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks()).WithIsInline(false)) + .AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary(Prefix)} {GetCommandRequirements(com)}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks(Prefix)).WithIsInline(false)) .WithColor(NadekoBot.OkColor); await channel.EmbedAsync(embed).ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index a15f1906..2fd612a3 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Permissions .Select(p => { var str = - $"`{p.Index + 1}.` {Format.Bold(p.GetCommand((SocketGuild) Context.Guild))}"; + $"`{p.Index + 1}.` {Format.Bold(p.GetCommand(Prefix, (SocketGuild) Context.Guild))}"; if (p.Index == 0) str += $" [{GetText("uneditable")}]"; return str; @@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Permissions } await ReplyConfirmLocalized("removed", index + 1, - Format.Code(p.GetCommand((SocketGuild) Context.Guild))).ConfigureAwait(false); + Format.Code(p.GetCommand(Prefix, (SocketGuild) Context.Guild))).ConfigureAwait(false); } catch (IndexOutOfRangeException) { @@ -171,7 +171,7 @@ namespace NadekoBot.Modules.Permissions _service.UpdateCache(config); } await ReplyConfirmLocalized("moved_permission", - Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)), + Format.Code(fromPerm.GetCommand(Prefix, (SocketGuild) Context.Guild)), ++from, ++to) .ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 1566fd19..3a5b8fe8 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -18,13 +18,11 @@ namespace NadekoBot.Modules.Utility { private readonly DiscordShardedClient _client; private readonly IStatsService _stats; - private readonly CommandHandler _cmdHandler; public InfoCommands(DiscordShardedClient client, IStatsService stats, CommandHandler ch) { _client = client; _stats = stats; - _cmdHandler = ch; } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 11fd67d2..a5e40fef 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -97,7 +97,7 @@ namespace NadekoBot var commandHandler = new CommandHandler(Client, Db, BotConfig, AllGuildConfigs, CommandService, Credentials, this); var stats = new StatsService(Client, commandHandler, Credentials); var images = new ImagesService(); - var currencyHandler = new CurrencyService(BotConfig, Db); + var currencyService = new CurrencyService(BotConfig, Db); //module services //todo 90 - autodiscover, DI, and add instead of manual like this @@ -107,10 +107,11 @@ namespace NadekoBot var repeaterService = new MessageRepeaterService(Client, AllGuildConfigs); var converterService = new ConverterService(Db); var commandMapService = new CommandMapService(AllGuildConfigs); + var patreonRewardsService = new PatreonRewardsService(Credentials, Db, currencyService); #endregion #region permissions - var permissionsService = new PermissionService(Db, BotConfig); + var permissionsService = new PermissionService(Db, BotConfig, commandHandler); var blacklistService = new BlacklistService(BotConfig); var cmdcdsService = new CmdCdService(AllGuildConfigs); var filterService = new FilterService(Client, AllGuildConfigs); @@ -124,12 +125,12 @@ namespace NadekoBot var clashService = new ClashOfClansService(Client, Db, localization, strings); var musicService = new MusicService(googleApiService, strings, localization, Db, soundcloudApiService, Credentials, AllGuildConfigs); - var crService = new CustomReactionsService(permissionsService, Db, Client); + var crService = new CustomReactionsService(permissionsService, Db, Client, commandHandler); var helpService = new HelpService(BotConfig); #region Games var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, strings, images, commandHandler); - var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs); + var chatterBotService = new ChatterBotService(Client, permissionsService, AllGuildConfigs, commandHandler); var pollService = new PollService(Client, strings); #endregion @@ -145,6 +146,7 @@ namespace NadekoBot var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService); var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs); var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs); + var logCommandService = new LogCommandService(Client, strings, AllGuildConfigs, Db, muteService, protectionService); #endregion @@ -160,7 +162,7 @@ namespace NadekoBot .Add(strings) .Add(Client) .Add(BotConfig) - .Add(currencyHandler) + .Add(currencyService) .Add(commandHandler) .Add(Db) //modules @@ -189,6 +191,7 @@ namespace NadekoBot .Add(gameVcService) .Add(autoAssignRoleService) .Add(protectionService) + .Add(logCommandService) .Add(permissionsService) .Add(blacklistService) .Add(cmdcdsService) diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index b3883234..3e87c172 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -36,6 +36,7 @@ + diff --git a/src/NadekoBot/Services/Administration/LogCommandService.cs b/src/NadekoBot/Services/Administration/LogCommandService.cs new file mode 100644 index 00000000..1df6ce97 --- /dev/null +++ b/src/NadekoBot/Services/Administration/LogCommandService.cs @@ -0,0 +1,909 @@ +ο»Ώusing Discord; +using Discord.WebSocket; +using NadekoBot.Extensions; +using NadekoBot.Services.Database.Models; +using NLog; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Administration +{ + public class LogCommandService + { + + private readonly DiscordShardedClient _client; + private readonly Logger _log; + + private string PrettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; + private string CurrentTime => $"{DateTime.Now:HH:mm:ss}"; + + public ConcurrentDictionary GuildLogSettings { get; } + + private ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); + private readonly Timer _timerReference; + private readonly NadekoStrings _strings; + private readonly DbService _db; + private readonly MuteService _mute; + private readonly ProtectionService _prot; + + public LogCommandService(DiscordShardedClient client, NadekoStrings strings, + IEnumerable gcs, DbService db, MuteService mute, ProtectionService prot) + { + _client = client; + _log = LogManager.GetCurrentClassLogger(); + _strings = strings; + _db = db; + _mute = mute; + _prot = prot; + + var sw = Stopwatch.StartNew(); + + GuildLogSettings = gcs + .ToDictionary(g => g.GuildId, g => g.LogSetting) + .ToConcurrent(); + + _timerReference = new Timer(async (state) => + { + try + { + var keys = PresenceUpdates.Keys.ToList(); + + await Task.WhenAll(keys.Select(async key => + { + if (PresenceUpdates.TryRemove(key, out List messages)) + try { await key.SendConfirmAsync(GetText(key.Guild, "presence_updates"), string.Join(Environment.NewLine, messages)); } + catch + { + // ignored + } + })); + } + catch (Exception ex) + { + _log.Warn(ex); + } + }, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + + sw.Stop(); + _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); + + //_client.MessageReceived += _client_MessageReceived; + _client.MessageUpdated += _client_MessageUpdated; + _client.MessageDeleted += _client_MessageDeleted; + _client.UserBanned += _client_UserBanned; + _client.UserUnbanned += _client_UserUnbanned; + _client.UserJoined += _client_UserJoined; + _client.UserLeft += _client_UserLeft; + _client.UserPresenceUpdated += _client_UserPresenceUpdated; + _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; + _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; + _client.GuildMemberUpdated += _client_GuildUserUpdated; +#if !GLOBAL_NADEKO + _client.UserUpdated += _client_UserUpdated; +#endif + _client.ChannelCreated += _client_ChannelCreated; + _client.ChannelDestroyed += _client_ChannelDestroyed; + _client.ChannelUpdated += _client_ChannelUpdated; + + _mute.UserMuted += MuteCommands_UserMuted; + _mute.UserUnmuted += MuteCommands_UserUnmuted; + + _prot.OnAntiProtectionTriggered += TriggeredAntiProtection; + } + + private string GetText(IGuild guild, string key, params object[] replacements) => + _strings.GetText(key, guild.Id, "Administration".ToLowerInvariant(), replacements); + + private async Task _client_UserUpdated(SocketUser before, SocketUser uAfter) + { + try + { + var after = uAfter as SocketGuildUser; + + if (after == null) + return; + + var g = after.Guild; + + if (!GuildLogSettings.TryGetValue(g.Id, out LogSetting logSetting) + || (logSetting.UserUpdatedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) + return; + + var embed = new EmbedBuilder(); + + + if (before.Username != after.Username) + { + embed.WithTitle("πŸ‘₯ " + GetText(g, "username_changed")) + .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") + .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) + .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) + .WithFooter(fb => fb.WithText(CurrentTime)) + .WithOkColor(); + } + else if (before.AvatarId != after.AvatarId) + { + embed.WithTitle("πŸ‘₯" + GetText(g, "avatar_changed")) + .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") + .WithThumbnailUrl(before.GetAvatarUrl()) + .WithImageUrl(after.GetAvatarUrl()) + .WithFooter(fb => fb.WithText(CurrentTime)) + .WithOkColor(); + } + else + { + return; + } + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + + //var guildsMemberOf = _client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); + //foreach (var g in guildsMemberOf) + //{ + // LogSetting logSetting; + // if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) + // || (logSetting.UserUpdatedId == null)) + // return; + + // ITextChannel logChannel; + // if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) + // return; + + // try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } + //} + } + catch + { + // ignored + } + } + + private async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) + { + try + { + var usr = iusr as IGuildUser; + if (usr == null) + return; + + var beforeVch = before.VoiceChannel; + var afterVch = after.VoiceChannel; + + if (beforeVch == afterVch) + return; + + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.LogVoicePresenceTTSId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTTS)) == null) + return; + + var str = ""; + if (beforeVch?.Guild == afterVch?.Guild) + { + str = GetText(logChannel.Guild, "moved", usr.Username, beforeVch?.Name, afterVch?.Name); + } + else if (beforeVch == null) + { + str = GetText(logChannel.Guild, "joined", usr.Username, afterVch.Name); + } + else if (afterVch == null) + { + str = GetText(logChannel.Guild, "left", usr.Username, beforeVch.Name); + } + var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); + toDelete.DeleteAfter(5); + } + catch + { + // ignored + } + } + + private async void MuteCommands_UserMuted(IGuildUser usr, MuteType muteType) + { + try + { + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.UserMutedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) + return; + var mutes = ""; + var mutedLocalized = GetText(logChannel.Guild, "muted_sn"); + switch (muteType) + { + case MuteType.Voice: + mutes = "πŸ”‡ " + GetText(logChannel.Guild, "xmuted_voice", mutedLocalized); + break; + case MuteType.Chat: + mutes = "πŸ”‡ " + GetText(logChannel.Guild, "xmuted_text", mutedLocalized); + break; + case MuteType.All: + mutes = "πŸ”‡ " + GetText(logChannel.Guild, "xmuted_text_and_voice", mutedLocalized); + break; + } + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) + .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") + .WithFooter(fb => fb.WithText(CurrentTime)) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async void MuteCommands_UserUnmuted(IGuildUser usr, MuteType muteType) + { + try + { + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.UserMutedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) + return; + + var mutes = ""; + var unmutedLocalized = GetText(logChannel.Guild, "unmuted_sn"); + switch (muteType) + { + case MuteType.Voice: + mutes = "πŸ”Š " + GetText(logChannel.Guild, "xmuted_voice", unmutedLocalized); + break; + case MuteType.Chat: + mutes = "πŸ”Š " + GetText(logChannel.Guild, "xmuted_text", unmutedLocalized); + break; + case MuteType.All: + mutes = "πŸ”Š " + GetText(logChannel.Guild, "xmuted_text_and_voice", unmutedLocalized); + break; + } + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) + .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") + .WithFooter(fb => fb.WithText($"{CurrentTime}")) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + public async Task TriggeredAntiProtection(PunishmentAction action, ProtectionType protection, params IGuildUser[] users) + { + try + { + if (users.Length == 0) + return; + + if (!GuildLogSettings.TryGetValue(users.First().Guild.Id, out LogSetting logSetting) + || (logSetting.LogOtherId == null)) + return; + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(users.First().Guild, logSetting, LogType.Other)) == null) + return; + + var punishment = ""; + switch (action) + { + case PunishmentAction.Mute: + punishment = "πŸ”‡ " + GetText(logChannel.Guild, "muted_pl").ToUpperInvariant(); + break; + case PunishmentAction.Kick: + punishment = "πŸ‘’ " + GetText(logChannel.Guild, "kicked_pl").ToUpperInvariant(); + break; + case PunishmentAction.Softban: + punishment = "☣ " + GetText(logChannel.Guild, "soft_banned_pl").ToUpperInvariant(); + break; + case PunishmentAction.Ban: + punishment = "⛔️ " + GetText(logChannel.Guild, "banned_pl").ToUpperInvariant(); + break; + } + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"πŸ›‘ Anti-{protection}")) + .WithTitle(GetText(logChannel.Guild, "users") + " " + punishment) + .WithDescription(string.Join("\n", users.Select(u => u.ToString()))) + .WithFooter(fb => fb.WithText($"{CurrentTime}")) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) + { + try + { + if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting) + || (logSetting.UserUpdatedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null) + return; + var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(CurrentTime)) + .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); + if (before.Nickname != after.Nickname) + { + embed.WithAuthor(eab => eab.WithName("πŸ‘₯ " + GetText(logChannel.Guild, "nick_change"))) + + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}")) + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}")); + } + else if (!before.Roles.SequenceEqual(after.Roles)) + { + if (before.Roles.Count < after.Roles.Count) + { + var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => r.Name); + embed.WithAuthor(eab => eab.WithName("βš” " + GetText(logChannel.Guild, "user_role_add"))) + .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); + } + else if (before.Roles.Count > after.Roles.Count) + { + var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r)).Select(r => r.Name); + embed.WithAuthor(eab => eab.WithName("βš” " + GetText(logChannel.Guild, "user_role_rem"))) + .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); + } + } + else + return; + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) + { + try + { + var before = cbefore as IGuildChannel; + if (before == null) + return; + var after = (IGuildChannel)cafter; + + if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting) + || (logSetting.ChannelUpdatedId == null) + || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) == null) + return; + + var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(CurrentTime)); + + var beforeTextChannel = cbefore as ITextChannel; + var afterTextChannel = cafter as ITextChannel; + + if (before.Name != after.Name) + { + embed.WithTitle("ℹ️ " + GetText(logChannel.Guild, "ch_name_change")) + .WithDescription($"{after} | {after.Id}") + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "ch_old_name")).WithValue(before.Name)); + } + else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) + { + embed.WithTitle("ℹ️ " + GetText(logChannel.Guild, "ch_topic_change")) + .WithDescription($"{after} | {after.Id}") + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "old_topic")).WithValue(beforeTextChannel?.Topic ?? "-")) + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "new_topic")).WithValue(afterTextChannel?.Topic ?? "-")); + } + else + return; + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async Task _client_ChannelDestroyed(IChannel ich) + { + try + { + var ch = ich as IGuildChannel; + if (ch == null) + return; + + if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) + || (logSetting.ChannelDestroyedId == null) + || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null) + return; + string title; + if (ch is IVoiceChannel) + { + title = GetText(logChannel.Guild, "voice_chan_destroyed"); + } + else + title = GetText(logChannel.Guild, "text_chan_destroyed"); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("πŸ†• " + title) + .WithDescription($"{ch.Name} | {ch.Id}") + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async Task _client_ChannelCreated(IChannel ich) + { + try + { + var ch = ich as IGuildChannel; + if (ch == null) + return; + + if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) + || (logSetting.ChannelCreatedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null) + return; + string title; + if (ch is IVoiceChannel) + { + title = GetText(logChannel.Guild, "voice_chan_created"); + } + else + title = GetText(logChannel.Guild, "text_chan_created"); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("πŸ†• " + title) + .WithDescription($"{ch.Name} | {ch.Id}") + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex); } + } + + private async Task _client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) + { + try + { + var usr = iusr as IGuildUser; + if (usr == null) + return; + + var beforeVch = before.VoiceChannel; + var afterVch = after.VoiceChannel; + + if (beforeVch == afterVch) + return; + + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.LogVoicePresenceId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresence)) == null) + return; + + string str = null; + if (beforeVch?.Guild == afterVch?.Guild) + { + str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + GetText(logChannel.Guild, "user_vmoved", + "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? "")); + } + else if (beforeVch == null) + { + str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + GetText(logChannel.Guild, "user_vjoined", + "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(afterVch.Name ?? "")); + } + else if (afterVch == null) + { + str = "πŸŽ™" + Format.Code(PrettyCurrentTime) + GetText(logChannel.Guild, "user_vleft", + "πŸ‘€" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(beforeVch.Name ?? "")); + } + if (str != null) + PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + } + catch + { + // ignored + } + } + + private async Task _client_UserPresenceUpdated(Optional optGuild, SocketUser usr, SocketPresence before, SocketPresence after) + { + try + { + var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild; + + if (guild == null) + return; + + if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) + || (logSetting.LogUserPresenceId == null) + || before.Status == after.Status) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) == null) + return; + string str = ""; + if (before.Status != after.Status) + str = "🎭" + Format.Code(PrettyCurrentTime) + + GetText(logChannel.Guild, "user_status_change", + "πŸ‘€" + Format.Bold(usr.Username), + Format.Bold(after.Status.ToString())); + + //if (before.Game?.Name != after.Game?.Name) + //{ + // if (str != "") + // str += "\n"; + // str += $"πŸ‘Ύ`{prettyCurrentTime}`πŸ‘€__**{usr.Username}**__ is now playing **{after.Game?.Name}**."; + //} + + PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + } + catch + { + // ignored + } + } + + private async Task _client_UserLeft(IGuildUser usr) + { + try + { + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.UserLeftId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft)) == null) + return; + + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("❌ " + GetText(logChannel.Guild, "user_left")) + .WithThumbnailUrl(usr.GetAvatarUrl()) + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + private async Task _client_UserJoined(IGuildUser usr) + { + try + { + if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) + || (logSetting.UserJoinedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) == null) + return; + + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("βœ… " + GetText(logChannel.Guild, "user_joined")) + .WithThumbnailUrl(usr.GetAvatarUrl()) + .WithDescription($"{usr}") + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex); } + } + + private async Task _client_UserUnbanned(IUser usr, IGuild guild) + { + try + { + if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) + || (logSetting.UserUnbannedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) == null) + return; + + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("♻️ " + GetText(logChannel.Guild, "user_unbanned")) + .WithThumbnailUrl(usr.GetAvatarUrl()) + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex); } + } + + private async Task _client_UserBanned(IUser usr, IGuild guild) + { + try + { + if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) + || (logSetting.UserBannedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null) + return; + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("🚫 " + GetText(logChannel.Guild, "user_banned")) + .WithThumbnailUrl(usr.GetAvatarUrl()) + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) + .WithFooter(efb => efb.WithText(CurrentTime))).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex); } + } + + private async Task _client_MessageDeleted(Cacheable optMsg, ISocketMessageChannel ch) + { + + try + { + var msg = (optMsg.HasValue ? optMsg.Value : null) as IUserMessage; + if (msg == null || msg.IsAuthor(_client)) + return; + + var channel = ch as ITextChannel; + if (channel == null) + return; + + if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) + || (logSetting.MessageDeletedId == null) + || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) == null || logChannel.Id == msg.Id) + return; + var embed = new EmbedBuilder() + .WithOkColor() + .WithTitle("πŸ—‘ " + GetText(logChannel.Guild, "msg_del", ((ITextChannel)msg.Channel).Name)) + .WithDescription(msg.Author.ToString()) + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "content")).WithValue(string.IsNullOrWhiteSpace(msg.Content) ? "-" : msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) + .WithFooter(efb => efb.WithText(CurrentTime)); + if (msg.Attachments.Any()) + embed.AddField(efb => efb.WithName(GetText(logChannel.Guild, "attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.Url))).WithIsInline(false)); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch (Exception ex) + { + _log.Warn(ex); + // ignored + } + } + + private async Task _client_MessageUpdated(Cacheable optmsg, SocketMessage imsg2, ISocketMessageChannel ch) + { + try + { + var after = imsg2 as IUserMessage; + if (after == null || after.IsAuthor(_client)) + return; + + var before = (optmsg.HasValue ? optmsg.Value : null) as IUserMessage; + if (before == null) + return; + + var channel = ch as ITextChannel; + if (channel == null) + return; + + if (before.Content == after.Content) + return; + + if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) + || (logSetting.MessageUpdatedId == null) + || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) == null || logChannel.Id == after.Channel.Id) + return; + + var embed = new EmbedBuilder() + .WithOkColor() + .WithTitle("πŸ“ " + GetText(logChannel.Guild, "msg_update", ((ITextChannel)after.Channel).Name)) + .WithDescription(after.Author.ToString()) + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(GetText(logChannel.Guild, "new_msg")).WithValue(string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) + .WithFooter(efb => efb.WithText(CurrentTime)); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + } + catch + { + // ignored + } + } + + public enum LogType + { + Other, + MessageUpdated, + MessageDeleted, + UserJoined, + UserLeft, + UserBanned, + UserUnbanned, + UserUpdated, + ChannelCreated, + ChannelDestroyed, + ChannelUpdated, + UserPresence, + VoicePresence, + VoicePresenceTTS, + UserMuted + }; + + private async Task TryGetLogChannel(IGuild guild, LogSetting logSetting, LogType logChannelType) + { + ulong? id = null; + switch (logChannelType) + { + case LogType.Other: + id = logSetting.LogOtherId; + break; + case LogType.MessageUpdated: + id = logSetting.MessageUpdatedId; + break; + case LogType.MessageDeleted: + id = logSetting.MessageDeletedId; + break; + case LogType.UserJoined: + id = logSetting.UserJoinedId; + break; + case LogType.UserLeft: + id = logSetting.UserLeftId; + break; + case LogType.UserBanned: + id = logSetting.UserBannedId; + break; + case LogType.UserUnbanned: + id = logSetting.UserUnbannedId; + break; + case LogType.UserUpdated: + id = logSetting.UserUpdatedId; + break; + case LogType.ChannelCreated: + id = logSetting.ChannelCreatedId; + break; + case LogType.ChannelDestroyed: + id = logSetting.ChannelDestroyedId; + break; + case LogType.ChannelUpdated: + id = logSetting.ChannelUpdatedId; + break; + case LogType.UserPresence: + id = logSetting.LogUserPresenceId; + break; + case LogType.VoicePresence: + id = logSetting.LogVoicePresenceId; + break; + case LogType.VoicePresenceTTS: + id = logSetting.LogVoicePresenceTTSId; + break; + case LogType.UserMuted: + id = logSetting.UserMutedId; + break; + } + + if (!id.HasValue) + { + UnsetLogSetting(guild.Id, logChannelType); + return null; + } + var channel = await guild.GetTextChannelAsync(id.Value).ConfigureAwait(false); + + if (channel == null) + { + UnsetLogSetting(guild.Id, logChannelType); + return null; + } + else + return channel; + } + + private void UnsetLogSetting(ulong guildId, LogType logChannelType) + { + using (var uow = _db.UnitOfWork) + { + var newLogSetting = uow.GuildConfigs.LogSettingsFor(guildId).LogSetting; + switch (logChannelType) + { + case LogType.Other: + newLogSetting.LogOtherId = null; + break; + case LogType.MessageUpdated: + newLogSetting.MessageUpdatedId = null; + break; + case LogType.MessageDeleted: + newLogSetting.MessageDeletedId = null; + break; + case LogType.UserJoined: + newLogSetting.UserJoinedId = null; + break; + case LogType.UserLeft: + newLogSetting.UserLeftId = null; + break; + case LogType.UserBanned: + newLogSetting.UserBannedId = null; + break; + case LogType.UserUnbanned: + newLogSetting.UserUnbannedId = null; + break; + case LogType.UserUpdated: + newLogSetting.UserUpdatedId = null; + break; + case LogType.UserMuted: + newLogSetting.UserMutedId = null; + break; + case LogType.ChannelCreated: + newLogSetting.ChannelCreatedId = null; + break; + case LogType.ChannelDestroyed: + newLogSetting.ChannelDestroyedId = null; + break; + case LogType.ChannelUpdated: + newLogSetting.ChannelUpdatedId = null; + break; + case LogType.UserPresence: + newLogSetting.LogUserPresenceId = null; + break; + case LogType.VoicePresence: + newLogSetting.LogVoicePresenceId = null; + break; + case LogType.VoicePresenceTTS: + newLogSetting.LogVoicePresenceTTSId = null; + break; + } + GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting); + uow.Complete(); + } + } + } +} diff --git a/src/NadekoBot/Services/Administration/MuteService.cs b/src/NadekoBot/Services/Administration/MuteService.cs index 16228bd2..8d454ab1 100644 --- a/src/NadekoBot/Services/Administration/MuteService.cs +++ b/src/NadekoBot/Services/Administration/MuteService.cs @@ -13,6 +13,13 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Administration { + public enum MuteType + { + Voice, + Chat, + All + } + public class MuteService { public ConcurrentDictionary GuildMuteRoles { get; } @@ -267,11 +274,4 @@ namespace NadekoBot.Services.Administration } } } - - public enum MuteType - { - Voice, - Chat, - All - } } diff --git a/src/NadekoBot/Services/Administration/ProtectionService.cs b/src/NadekoBot/Services/Administration/ProtectionService.cs index 1737df9a..23bca7ad 100644 --- a/src/NadekoBot/Services/Administration/ProtectionService.cs +++ b/src/NadekoBot/Services/Administration/ProtectionService.cs @@ -17,9 +17,8 @@ namespace NadekoBot.Services.Administration // guildId | (userId|messages) public readonly ConcurrentDictionary AntiSpamGuilds = new ConcurrentDictionary(); - - //todo sub LogCommands to this - public event Func, PunishmentAction, ProtectionType, Task> OnAntiProtectionTriggered = delegate { return Task.CompletedTask; }; + + public event Func OnAntiProtectionTriggered = delegate { return Task.CompletedTask; }; private readonly Logger _log; private readonly DiscordShardedClient _client; @@ -175,7 +174,7 @@ namespace NadekoBot.Services.Administration break; } } - await OnAntiProtectionTriggered(gus, action, pt).ConfigureAwait(false); + await OnAntiProtectionTriggered(action, pt, gus).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 4099ffc2..addcf2da 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -15,6 +15,7 @@ using NadekoBot.DataStructures.ModuleBehaviors; using NadekoBot.Services.Database.Models; using NadekoBot.Services; using System.IO; +using Discord.Net; namespace NadekoBot.Services { @@ -357,10 +358,17 @@ namespace NadekoBot.Services } var execResult = await commands[i].ExecuteAsync(context, parseResult, serviceProvider); - if (execResult.Exception != null) + if (execResult.Exception != null && (!(execResult.Exception is HttpException he) || he.DiscordCode != 50013)) { - File.AppendAllText($"./Command Errors {DateTime.Now:yyyy-MM-dd}.txt", execResult.Exception.ToString() + "\n\n\n\n"); - _log.Warn(execResult.Exception); + lock (errorLogLock) + { + var now = DateTime.Now; + File.AppendAllText($"./Command Errors {now:yyyy-MM-dd}.txt", + $"[{now:HH:mm-yyyy-MM-dd}]" + Environment.NewLine + + execResult.Exception.ToString() + Environment.NewLine + + "------" + Environment.NewLine); + _log.Warn(execResult.Exception); + } } return (true, null); } @@ -368,5 +376,7 @@ namespace NadekoBot.Services return (false, null); //return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); } + + private readonly object errorLogLock = new object(); } } \ No newline at end of file diff --git a/src/NadekoBot/Services/CurrencyHandler.cs b/src/NadekoBot/Services/CurrencyService.cs similarity index 100% rename from src/NadekoBot/Services/CurrencyHandler.cs rename to src/NadekoBot/Services/CurrencyService.cs diff --git a/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs index 6b7a3f20..b63101e1 100644 --- a/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs +++ b/src/NadekoBot/Services/CustomReactions/CustomReactionsService.cs @@ -24,13 +24,16 @@ namespace NadekoBot.Services.CustomReactions private readonly DbService _db; private readonly DiscordShardedClient _client; private readonly PermissionService _perms; + private readonly CommandHandler _cmd; - public CustomReactionsService(PermissionService perms, DbService db, DiscordShardedClient client) + public CustomReactionsService(PermissionService perms, DbService db, + DiscordShardedClient client, CommandHandler cmd) { _log = LogManager.GetCurrentClassLogger(); _db = db; _client = client; _perms = perms; + _cmd = cmd; var sw = Stopwatch.StartNew(); using (var uow = _db.UnitOfWork) @@ -109,7 +112,7 @@ namespace NadekoBot.Services.CustomReactions { if (pc.Verbose) { - var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(sg)}** is preventing this action."; + var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), sg)}** is preventing this action."; try { await msg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { } _log.Info(returnMsg); } diff --git a/src/NadekoBot/Services/DbHandler.cs b/src/NadekoBot/Services/DbService.cs similarity index 100% rename from src/NadekoBot/Services/DbHandler.cs rename to src/NadekoBot/Services/DbService.cs diff --git a/src/NadekoBot/Services/Games/ChatterbotService.cs b/src/NadekoBot/Services/Games/ChatterbotService.cs index 235d1b63..8039b79d 100644 --- a/src/NadekoBot/Services/Games/ChatterbotService.cs +++ b/src/NadekoBot/Services/Games/ChatterbotService.cs @@ -18,14 +18,16 @@ namespace NadekoBot.Services.Games private readonly DiscordShardedClient _client; private readonly Logger _log; private readonly PermissionService _perms; + private readonly CommandHandler _cmd; public ConcurrentDictionary> ChatterBotGuilds { get; } - public ChatterBotService(DiscordShardedClient client, PermissionService perms, IEnumerable gcs) + public ChatterBotService(DiscordShardedClient client, PermissionService perms, IEnumerable gcs, CommandHandler cmd) { _client = client; _log = LogManager.GetCurrentClassLogger(); _perms = perms; + _cmd = cmd; ChatterBotGuilds = new ConcurrentDictionary>( gcs.Where(gc => gc.CleverbotEnabled) @@ -99,8 +101,8 @@ namespace NadekoBot.Services.Games { if (pc.Verbose) { - //todo move this to permissions, prefix is always "." as a placeholder, fix that when you move it - var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(sg)}** is preventing this action."; + //todo move this to permissions + var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), sg)}** is preventing this action."; try { await usrMsg.Channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { } _log.Info(returnMsg); } diff --git a/src/NadekoBot/Services/Permissions/PermissionExtensions.cs b/src/NadekoBot/Services/Permissions/PermissionExtensions.cs index 90e20ad0..960e7c32 100644 --- a/src/NadekoBot/Services/Permissions/PermissionExtensions.cs +++ b/src/NadekoBot/Services/Permissions/PermissionExtensions.cs @@ -68,7 +68,7 @@ namespace NadekoBot.Services.Permissions return null; } - public static string GetCommand(this Permissionv2 perm, SocketGuild guild = null) + public static string GetCommand(this Permissionv2 perm, string prefix, SocketGuild guild = null) { var com = ""; switch (perm.PrimaryTarget) @@ -99,7 +99,10 @@ namespace NadekoBot.Services.Permissions com = "a" + com + "m"; break; } - com += " " + (perm.SecondaryTargetName != "*" ? perm.SecondaryTargetName + " " : "") + (perm.State ? "enable" : "disable") + " "; + + var secName = perm.SecondaryTarget == SecondaryPermissionType.Command ? + prefix + perm.SecondaryTargetName : perm.SecondaryTargetName; + com += " " + (perm.SecondaryTargetName != "*" ? secName + " " : "") + (perm.State ? "enable" : "disable") + " "; switch (perm.PrimaryTarget) { @@ -116,7 +119,7 @@ namespace NadekoBot.Services.Permissions break; } - return "." + com; + return prefix + com; } public static IEnumerable AsEnumerable(this Permission perm) diff --git a/src/NadekoBot/Services/Permissions/PermissionsService.cs b/src/NadekoBot/Services/Permissions/PermissionsService.cs index b82b5bb2..765cb131 100644 --- a/src/NadekoBot/Services/Permissions/PermissionsService.cs +++ b/src/NadekoBot/Services/Permissions/PermissionsService.cs @@ -17,15 +17,17 @@ namespace NadekoBot.Services.Permissions { private readonly DbService _db; private readonly Logger _log; + private readonly CommandHandler _cmd; //guildid, root permission public ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); - public PermissionService(DbService db, BotConfig bc) + public PermissionService(DbService db, BotConfig bc, CommandHandler cmd) { _log = LogManager.GetCurrentClassLogger(); _db = db; + _cmd = cmd; var sw = Stopwatch.StartNew(); TryMigratePermissions(bc); @@ -68,7 +70,8 @@ namespace NadekoBot.Services.Permissions var log = LogManager.GetCurrentClassLogger(); using (var uow = _db.UnitOfWork) { - if (bc.PermissionVersion <= 1) + var _bc = uow.BotConfig.GetOrCreate(); + if (_bc.PermissionVersion <= 1) { log.Info("Permission version is 1, upgrading to 2."); var oldCache = new ConcurrentDictionary(uow.GuildConfigs @@ -123,9 +126,27 @@ namespace NadekoBot.Services.Permissions log.Info("Permission migration to v2 is done."); } - uow.BotConfig.GetOrCreate().PermissionVersion = 2; - uow.Complete(); + _bc.PermissionVersion = 2; } + if (_bc.PermissionVersion <= 2) + { + var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" }; + uow._context.Database.ExecuteSqlCommand( +$@"UPDATE {nameof(Permissionv2)} +SET secondaryTargetName=trim(substr(secondaryTargetName, 3)) +WHERE secondaryTargetName LIKE '!!%' OR secondaryTargetName LIKE '!m%'; + +UPDATE {nameof(Permissionv2)} +SET secondaryTargetName=substr(secondaryTargetName, 2) +WHERE secondaryTargetName LIKE '.%' OR + secondaryTargetName LIKE '~%' OR + secondaryTargetName LIKE ';%' OR + secondaryTargetName LIKE '>%' OR + secondaryTargetName LIKE '-%' OR + secondaryTargetName LIKE '!%';"); + _bc.PermissionVersion = 3; + } + uow.Complete(); } } @@ -177,7 +198,7 @@ namespace NadekoBot.Services.Permissions PermissionCache pc = GetCache(guild.Id); if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index)) { - var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand((SocketGuild)guild)}** is preventing this action."; + var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), (SocketGuild)guild)}** is preventing this action."; if (pc.Verbose) try { await channel.SendErrorAsync(returnMsg).ConfigureAwait(false); } catch { } return true; diff --git a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs index 09b90ea6..bb420964 100644 --- a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs +++ b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Services.Utility private readonly DbService _db; private readonly CurrencyService _currency; - private PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency) + public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency) { _creds = creds; _db = db; diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index d71f9ad2..1ae07a8d 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -33,8 +33,8 @@ namespace NadekoBot.Extensions return Convert.ToBase64String(plainTextBytes); } - public static string RealSummary(this CommandInfo cmd) => string.Format(cmd.Summary, "."); - public static string RealRemarks(this CommandInfo cmd) => string.Format(cmd.Remarks, "."); + public static string RealSummary(this CommandInfo cmd, string prefix) => string.Format(cmd.Summary, prefix); + public static string RealRemarks(this CommandInfo cmd, string prefix) => string.Format(cmd.Remarks, prefix); public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) {