#if !GLOBAL_NADEKO using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Discord; using Discord.WebSocket; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using NadekoBot.Common.Collections; using NadekoBot.Services; using NadekoBot.Services.Database.Models; using NadekoBot.Db; using NadekoBot.Extensions; using NadekoBot.Modules.Administration.Common; namespace NadekoBot.Modules.Administration.Services { public class LogCommandService : INService { private readonly DiscordSocketClient _client; public ConcurrentDictionary GuildLogSettings { get; } private ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); private readonly Timer _timerReference; private readonly IBotStrings _strings; private readonly DbService _db; private readonly MuteService _mute; private readonly ProtectionService _prot; private readonly GuildTimezoneService _tz; public LogCommandService(DiscordSocketClient client, IBotStrings strings, DbService db, MuteService mute, ProtectionService prot, GuildTimezoneService tz, IMemoryCache memoryCache) { _client = client; _memoryCache = memoryCache; _strings = strings; _db = db; _mute = mute; _prot = prot; _tz = tz; using (var uow = db.GetDbContext()) { var guildIds = client.Guilds.Select(x => x.Id).ToList(); var configs = uow .Set() .AsQueryable() .Include(gc => gc.LogSetting) .ThenInclude(ls => ls.IgnoredChannels) .Where(x => guildIds.Contains(x.GuildId)) .ToList(); GuildLogSettings = configs .ToDictionary(g => g.GuildId, g => g.LogSetting) .ToConcurrent(); } _timerReference = new Timer(async (state) => { var keys = PresenceUpdates.Keys.ToList(); await Task.WhenAll(keys.Select(key => { if (!((SocketGuild) key.Guild).CurrentUser.GetPermissions(key).SendMessages) return Task.CompletedTask; if (PresenceUpdates.TryRemove(key, out var msgs)) { var title = GetText(key.Guild, "presence_updates"); var desc = string.Join(Environment.NewLine, msgs); return key.SendConfirmAsync(title, desc.TrimTo(2048)); } return Task.CompletedTask; })).ConfigureAwait(false); }, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); //_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; _client.RoleDeleted += _client_RoleDeleted; _mute.UserMuted += MuteCommands_UserMuted; _mute.UserUnmuted += MuteCommands_UserUnmuted; _prot.OnAntiProtectionTriggered += TriggeredAntiProtection; _clearTimer = new Timer(_ => { _ignoreMessageIds.Clear(); }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); } private readonly Timer _clearTimer; private readonly ConcurrentHashSet _ignoreMessageIds = new ConcurrentHashSet(); private readonly IMemoryCache _memoryCache; public void AddDeleteIgnore(ulong messageId) { _ignoreMessageIds.Add(messageId); } public bool LogIgnore(ulong gid, ulong cid) { int removed = 0; using (var uow = _db.GetDbContext()) { var config = uow.LogSettingsFor(gid); LogSetting logSetting = GuildLogSettings.GetOrAdd(gid, (id) => config.LogSetting); removed = logSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == cid); config.LogSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == cid); if (removed == 0) { var toAdd = new IgnoredLogChannel {ChannelId = cid}; logSetting.IgnoredChannels.Add(toAdd); config.LogSetting.IgnoredChannels.Add(toAdd); } uow.SaveChanges(); } return removed > 0; } private string GetText(IGuild guild, string key, params object[] replacements) => _strings.GetText(key, guild.Id, replacements); private string PrettyCurrentTime(IGuild g) { var time = DateTime.UtcNow; if (g != null) time = TimeZoneInfo.ConvertTime(time, _tz.GetTimeZoneOrUtc(g.Id)); return $"【{time:HH:mm:ss}】"; } private string CurrentTime(IGuild g) { DateTime time = DateTime.UtcNow; if (g != null) time = TimeZoneInfo.ConvertTime(time, _tz.GetTimeZoneOrUtc(g.Id)); return $"{time:HH:mm:ss}"; } public async Task LogServer(ulong guildId, ulong channelId, bool value) { LogSetting logSetting; using (var uow = _db.GetDbContext()) { logSetting = uow.LogSettingsFor(guildId).LogSetting; GuildLogSettings.AddOrUpdate(guildId, (id) => logSetting, (id, old) => logSetting); logSetting.LogOtherId = logSetting.MessageUpdatedId = logSetting.MessageDeletedId = logSetting.UserJoinedId = logSetting.UserLeftId = logSetting.UserBannedId = logSetting.UserUnbannedId = logSetting.UserUpdatedId = logSetting.ChannelCreatedId = logSetting.ChannelDestroyedId = logSetting.ChannelUpdatedId = logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = logSetting.UserMutedId = logSetting.LogVoicePresenceTTSId = (value ? channelId : (ulong?) null); await uow.SaveChangesAsync(); } } private Task _client_UserUpdated(SocketUser before, SocketUser uAfter) { var _ = Task.Run(async () => { try { if (!(uAfter is SocketGuildUser after)) return; var g = after.Guild; if (!GuildLogSettings.TryGetValue(g.Id, out LogSetting logSetting) || (logSetting.UserUpdatedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated).ConfigureAwait(false)) is 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(g))) .WithOkColor(); } else if (before.AvatarId != after.AvatarId) { embed.WithTitle("👥" + GetText(g, "avatar_changed")) .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithFooter(fb => fb.WithText(CurrentTime(g))) .WithOkColor(); var bav = before.RealAvatarUrl(); if (bav != null && bav.IsAbsoluteUri) embed.WithThumbnailUrl(bav.ToString()); var aav = after.RealAvatarUrl(); if (aav != null && aav.IsAbsoluteUri) embed.WithImageUrl(aav.ToString()); } else { return; } await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); return Task.CompletedTask; } public bool Log(ulong gid, ulong? cid, LogType type/*, string options*/) { ulong? channelId = null; using (var uow = _db.GetDbContext()) { var logSetting = uow.LogSettingsFor(gid).LogSetting; GuildLogSettings.AddOrUpdate(gid, (id) => logSetting, (id, old) => logSetting); switch (type) { case LogType.Other: channelId = logSetting.LogOtherId = (logSetting.LogOtherId is null ? cid : default); break; case LogType.MessageUpdated: channelId = logSetting.MessageUpdatedId = (logSetting.MessageUpdatedId is null ? cid : default); break; case LogType.MessageDeleted: channelId = logSetting.MessageDeletedId = (logSetting.MessageDeletedId is null ? cid : default); //logSetting.DontLogBotMessageDeleted = (options == "nobot"); break; case LogType.UserJoined: channelId = logSetting.UserJoinedId = (logSetting.UserJoinedId is null ? cid : default); break; case LogType.UserLeft: channelId = logSetting.UserLeftId = (logSetting.UserLeftId is null ? cid : default); break; case LogType.UserBanned: channelId = logSetting.UserBannedId = (logSetting.UserBannedId is null ? cid : default); break; case LogType.UserUnbanned: channelId = logSetting.UserUnbannedId = (logSetting.UserUnbannedId is null ? cid : default); break; case LogType.UserUpdated: channelId = logSetting.UserUpdatedId = (logSetting.UserUpdatedId is null ? cid : default); break; case LogType.UserMuted: channelId = logSetting.UserMutedId = (logSetting.UserMutedId is null ? cid : default); break; case LogType.ChannelCreated: channelId = logSetting.ChannelCreatedId = (logSetting.ChannelCreatedId is null ? cid : default); break; case LogType.ChannelDestroyed: channelId = logSetting.ChannelDestroyedId = (logSetting.ChannelDestroyedId is null ? cid : default); break; case LogType.ChannelUpdated: channelId = logSetting.ChannelUpdatedId = (logSetting.ChannelUpdatedId is null ? cid : default); break; case LogType.UserPresence: channelId = logSetting.LogUserPresenceId = (logSetting.LogUserPresenceId is null ? cid : default); break; case LogType.VoicePresence: channelId = logSetting.LogVoicePresenceId = (logSetting.LogVoicePresenceId is null ? cid : default); break; case LogType.VoicePresenceTTS: channelId = logSetting.LogVoicePresenceTTSId = (logSetting.LogVoicePresenceTTSId is null ? cid : default); break; } uow.SaveChanges(); } return channelId != null; } private Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) { var _ = Task.Run(async () => { try { if (!(iusr is IGuildUser usr)) return; var beforeVch = before.VoiceChannel; var afterVch = after.VoiceChannel; if (beforeVch == afterVch) return; if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.LogVoicePresenceTTSId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTTS) .ConfigureAwait(false)) is null) return; var str = ""; if (beforeVch?.Guild == afterVch?.Guild) { str = GetText(logChannel.Guild, "log_vc_moved", usr.Username, beforeVch?.Name, afterVch?.Name); } else if (beforeVch is null) { str = GetText(logChannel.Guild, "log_vc_joined", usr.Username, afterVch.Name); } else if (afterVch is null) { str = GetText(logChannel.Guild, "log_vc_left", usr.Username, beforeVch.Name); } var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); toDelete.DeleteAfter(5); } catch { // ignored } }); return Task.CompletedTask; } private void MuteCommands_UserMuted(IGuildUser usr, IUser mod, MuteType muteType, string reason) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.UserMutedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted) .ConfigureAwait(false)) is null) return; var mutes = ""; var mutedLocalized = GetText(logChannel.Guild, "muted_sn"); switch (muteType) { case MuteType.Voice: mutes = "🔇 " + GetText(logChannel.Guild, "xmuted_voice", mutedLocalized, mod.ToString()); break; case MuteType.Chat: mutes = "🔇 " + GetText(logChannel.Guild, "xmuted_text", mutedLocalized, mod.ToString()); break; case MuteType.All: mutes = "🔇 " + GetText(logChannel.Guild, "xmuted_text_and_voice", mutedLocalized, mod.ToString()); break; } var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText(CurrentTime(usr.Guild))) .WithOkColor(); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); } private void MuteCommands_UserUnmuted(IGuildUser usr, IUser mod, MuteType muteType, string reason) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.UserMutedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted) .ConfigureAwait(false)) is null) return; var mutes = ""; var unmutedLocalized = GetText(logChannel.Guild, "unmuted_sn"); switch (muteType) { case MuteType.Voice: mutes = "🔊 " + GetText(logChannel.Guild, "xmuted_voice", unmutedLocalized, mod.ToString()); break; case MuteType.Chat: mutes = "🔊 " + GetText(logChannel.Guild, "xmuted_text", unmutedLocalized, mod.ToString()); break; case MuteType.All: mutes = "🔊 " + GetText(logChannel.Guild, "xmuted_text_and_voice", unmutedLocalized, mod.ToString()); break; } var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText($"{CurrentTime(usr.Guild)}")) .WithOkColor(); if (!string.IsNullOrWhiteSpace(reason)) embed.WithDescription(reason); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); } public Task TriggeredAntiProtection(PunishmentAction action, ProtectionType protection, params IGuildUser[] users) { var _ = Task.Run(async () => { try { if (users.Length == 0) return; if (!GuildLogSettings.TryGetValue(users.First().Guild.Id, out LogSetting logSetting) || (logSetting.LogOtherId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(users.First().Guild, logSetting, LogType.Other) .ConfigureAwait(false)) is 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; case PunishmentAction.RemoveRoles: punishment = "⛔️ " + GetText(logChannel.Guild, "remove_roles_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(logChannel.Guild))) .WithOkColor(); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); return Task.CompletedTask; } private string GetRoleDeletedKey(ulong roleId) => $"role_deleted_{roleId}"; private Task _client_RoleDeleted(SocketRole socketRole) { Serilog.Log.Information("Role deleted {RoleId}", socketRole.Id); _memoryCache.Set(GetRoleDeletedKey(socketRole.Id), true, TimeSpan.FromMinutes(5)); return Task.CompletedTask; } private bool IsRoleDeleted(ulong roleId) { var isDeleted = _memoryCache.TryGetValue(GetRoleDeletedKey(roleId), out var _); return isDeleted; } private Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting)) return; ITextChannel logChannel; if (logSetting.UserUpdatedId != null && (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated) .ConfigureAwait(false)) != null) { var embed = new EmbedBuilder().WithOkColor() .WithFooter(efb => efb.WithText(CurrentTime(before.Guild))) .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}")); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } 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()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } else if (before.Roles.Count > after.Roles.Count) { await Task.Delay(1000); var diffRoles = before.Roles .Where(r => !after.Roles.Contains(r) && !IsRoleDeleted(r.Id)) .Select(r => r.Name) .ToList(); if (diffRoles.Any()) { embed.WithAuthor(eab => eab.WithName("⚔ " + GetText(logChannel.Guild, "user_role_rem"))) .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } } } } logChannel = null; if (!before.IsBot && logSetting.LogUserPresenceId != null && (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserPresence) .ConfigureAwait(false)) != null) { if (before.Status != after.Status) { var str = "🎭" + Format.Code(PrettyCurrentTime(after.Guild)) + GetText(logChannel.Guild, "user_status_change", "👤" + Format.Bold(after.Username), Format.Bold(after.Status.ToString())); PresenceUpdates.AddOrUpdate(logChannel, new List() {str}, (id, list) => { list.Add(str); return list; }); } else if (before.Activity?.Name != after.Activity?.Name) { var str = $"👾`{PrettyCurrentTime(after.Guild)}`👤__**{after.Username}**__ is now playing **{after.Activity?.Name ?? "-"}**."; PresenceUpdates.AddOrUpdate(logChannel, new List() {str}, (id, list) => { list.Add(str); return list; }); } } } catch { // ignored } }); return Task.CompletedTask; } private Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) { var _ = Task.Run(async () => { try { if (!(cbefore is IGuildChannel before)) return; var after = (IGuildChannel) cafter; if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting) || (logSetting.ChannelUpdatedId is null) || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated) .ConfigureAwait(false)) is null) return; var embed = new EmbedBuilder().WithOkColor() .WithFooter(efb => efb.WithText(CurrentTime(before.Guild))); 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 } }); return Task.CompletedTask; } private Task _client_ChannelDestroyed(IChannel ich) { var _ = Task.Run(async () => { try { if (!(ich is IGuildChannel ch)) return; if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) || (logSetting.ChannelDestroyedId is null) || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed) .ConfigureAwait(false)) is 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(ch.Guild)))).ConfigureAwait(false); } catch { // ignored } }); return Task.CompletedTask; } private Task _client_ChannelCreated(IChannel ich) { var _ = Task.Run(async () => { try { if (!(ich is IGuildChannel ch)) return; if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting) || (logSetting.ChannelCreatedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated) .ConfigureAwait(false)) is 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(ch.Guild)))).ConfigureAwait(false); } catch (Exception) { // ignored } }); return Task.CompletedTask; } private Task _client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) { var _ = Task.Run(async () => { try { if (!(iusr is IGuildUser usr) || usr.IsBot) return; var beforeVch = before.VoiceChannel; var afterVch = after.VoiceChannel; if (beforeVch == afterVch) return; if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.LogVoicePresenceId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresence) .ConfigureAwait(false)) is null) return; string str = null; if (beforeVch?.Guild == afterVch?.Guild) { str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild, "user_vmoved", "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? "")); } else if (beforeVch is null) { str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild, "user_vjoined", "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), Format.Bold(afterVch.Name ?? "")); } else if (afterVch is null) { str = "🎙" + Format.Code(PrettyCurrentTime(usr.Guild)) + GetText(logChannel.Guild, "user_vleft", "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), Format.Bold(beforeVch.Name ?? "")); } if (!string.IsNullOrWhiteSpace(str)) PresenceUpdates.AddOrUpdate(logChannel, new List() {str}, (id, list) => { list.Add(str); return list; }); } catch { // ignored } }); return Task.CompletedTask; } //private Task _client_UserPresenceUpdated(Optional optGuild, SocketUser usr, SocketPresence before, SocketPresence after) //{ // var _ = Task.Run(async () => // { // try // { // var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild; // if (guild is null) // return; // if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) // || (logSetting.LogUserPresenceId is null) // || before.Status == after.Status) // return; // ITextChannel logChannel; // if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) is null) // return; // string str = ""; // if (before.Status != after.Status) // str = "🎭" + Format.Code(PrettyCurrentTime(g)) + // 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 // } // }); // return Task.CompletedTask; //} private Task _client_UserLeft(IGuildUser usr) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.UserLeftId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft) .ConfigureAwait(false)) is null) return; var embed = new EmbedBuilder() .WithOkColor() .WithTitle("❌ " + GetText(logChannel.Guild, "user_left")) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(CurrentTime(usr.Guild))); if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) embed.WithThumbnailUrl(usr.GetAvatarUrl()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); return Task.CompletedTask; } private Task _client_UserJoined(IGuildUser usr) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting) || (logSetting.UserJoinedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined) .ConfigureAwait(false)) is null) return; var embed = new EmbedBuilder() .WithOkColor() .WithTitle("✅ " + GetText(logChannel.Guild, "user_joined")) .WithDescription($"{usr.Mention} `{usr}`") .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .AddField(fb => fb.WithName(GetText(logChannel.Guild, "joined_server")) .WithValue($"{usr.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText(logChannel.Guild, "joined_discord")) .WithValue($"{usr.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) .WithFooter(efb => efb.WithText(CurrentTime(usr.Guild))); if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) embed.WithThumbnailUrl(usr.GetAvatarUrl()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch (Exception) { // ignored } }); return Task.CompletedTask; } private Task _client_UserUnbanned(IUser usr, IGuild guild) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) || (logSetting.UserUnbannedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned) .ConfigureAwait(false)) is null) return; var embed = new EmbedBuilder() .WithOkColor() .WithTitle("♻️ " + GetText(logChannel.Guild, "user_unbanned")) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(CurrentTime(guild))); if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) embed.WithThumbnailUrl(usr.GetAvatarUrl()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch (Exception) { // ignored } }); return Task.CompletedTask; } private Task _client_UserBanned(IUser usr, IGuild guild) { var _ = Task.Run(async () => { try { if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting) || (logSetting.UserBannedId is null)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned).ConfigureAwait(false)) == null) return; var embed = new EmbedBuilder() .WithOkColor() .WithTitle("🚫 " + GetText(logChannel.Guild, "user_banned")) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(CurrentTime(guild))); var avatarUrl = usr.GetAvatarUrl(); if (Uri.IsWellFormedUriString(avatarUrl, UriKind.Absolute)) embed.WithThumbnailUrl(usr.GetAvatarUrl()); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch (Exception) { // ignored } }); return Task.CompletedTask; } private Task _client_MessageDeleted(Cacheable optMsg, ISocketMessageChannel ch) { var _ = Task.Run(async () => { try { var msg = (optMsg.HasValue ? optMsg.Value : null) as IUserMessage; if (msg is null || msg.IsAuthor(_client)) return; if (_ignoreMessageIds.Contains(msg.Id)) return; if (!(ch is ITextChannel channel)) return; if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) || (logSetting.MessageDeletedId is null) || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted) .ConfigureAwait(false)) is null || logChannel.Id == msg.Id) return; var resolvedMessage = msg.Resolve(userHandling: TagHandling.FullName); 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(resolvedMessage) ? "-" : resolvedMessage) .WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(CurrentTime(channel.Guild))); 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) { // ignored } }); return Task.CompletedTask; } private Task _client_MessageUpdated(Cacheable optmsg, SocketMessage imsg2, ISocketMessageChannel ch) { var _ = Task.Run(async () => { try { if (!(imsg2 is IUserMessage after) || after.IsAuthor(_client)) return; var before = (optmsg.HasValue ? optmsg.Value : null) as IUserMessage; if (before is null) return; if (!(ch is ITextChannel channel)) return; if (before.Content == after.Content) return; if (before.Author.IsBot) return; if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting) || (logSetting.MessageUpdatedId is null) || logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id)) return; ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated) .ConfigureAwait(false)) is 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(channel.Guild))); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } catch { // ignored } }); return Task.CompletedTask; } 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 || id == 0) { UnsetLogSetting(guild.Id, logChannelType); return null; } var channel = await guild.GetTextChannelAsync(id.Value).ConfigureAwait(false); if (channel is null) { UnsetLogSetting(guild.Id, logChannelType); return null; } else return channel; } private void UnsetLogSetting(ulong guildId, LogType logChannelType) { using (var uow = _db.GetDbContext()) { var newLogSetting = uow.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.SaveChanges(); } } } } #endif