public static async Task <bool> IsClean(DiscordClient client, DiscordMessage message) { if (message.Channel.IsPrivate) { return(true); } if (message.Author.IsBot) { return(true); } if (message.Author.IsWhitelisted(client, message.Channel.Guild)) { return(true); } if (string.IsNullOrEmpty(message.Content)) { return(true); } string trigger = null; var severity = ReportSeverity.Low; try { trigger = await PiracyStringProvider.FindTriggerAsync(message.Content); if (trigger == null) { return(true); } await message.Channel.DeleteMessageAsync(message, $"Mention of piracy trigger '{trigger}'").ConfigureAwait(false); } catch (Exception e) { Config.Log.Warn(e, $"Couldn't delete message in {message.Channel.Name}"); severity = ReportSeverity.High; } try { var rules = await client.GetChannelAsync(Config.BotRulesChannelId).ConfigureAwait(false); await Task.WhenAll( message.Channel.SendMessageAsync($"{message.Author.Mention} Please follow the {rules.Mention} and do not discuss piracy on this server. Repeated offence may result in a ban."), client.ReportAsync("Mention of piracy", message, trigger, message.Content, severity), Warnings.AddAsync(client, message, message.Author.Id, message.Author.Username, client.CurrentUser, "Mention of piracy", message.Content.Sanitize()) ).ConfigureAwait(false); } catch (Exception e) { Config.Log.Warn(e, $"Couldn't finish piracy trigger actions for a message in {message.Channel.Name}"); } return(false); }
public static async Task <bool> CheckMessageForInvitesAsync(DiscordClient client, DiscordMessage message) { if (message.Channel.IsPrivate) { return(true); } if (message.Author.IsBotSafeCheck()) { return(true); } #if !DEBUG if (message.Author.IsWhitelisted(client, message.Channel.Guild)) { return(true); } #endif if (message.Reactions.Any(r => r.Emoji == Config.Reactions.Moderated && r.IsMe)) { return(true); } var(hasInvalidResults, attemptedWorkaround, invites) = await client.GetInvitesAsync(message.Content, message.Author).ConfigureAwait(false); if (!hasInvalidResults && invites.Count == 0) { return(true); } if (hasInvalidResults) { try { DeletedMessagesMonitor.RemovedByBotCache.Set(message.Id, true, DeletedMessagesMonitor.CacheRetainTime); await message.DeleteAsync("Not a white-listed discord invite link").ConfigureAwait(false); await client.ReportAsync("🛃 An unapproved discord invite", message, "In invalid or expired invite", null, ReportSeverity.Low).ConfigureAwait(false); await message.Channel.SendMessageAsync($"{message.Author.Mention} please refrain from posting invites that were not approved by a moderator, especially expired or invalid.").ConfigureAwait(false); } catch (Exception e) { Config.Log.Warn(e); await client.ReportAsync("🛃 An unapproved discord invite", message, "In invalid or expired invite", null, ReportSeverity.Medium).ConfigureAwait(false); await message.ReactWithAsync(Config.Reactions.Moderated, $"{message.Author.Mention} please remove this expired or invalid invite, and refrain from posting it again until you have received an approval from a moderator.", true ).ConfigureAwait(false); } return(false); } foreach (var invite in invites) { if (!await InviteWhitelistProvider.IsWhitelistedAsync(invite).ConfigureAwait(false)) { if (!InviteCodeCache.TryGetValue(message.Author.Id, out HashSet <string> recentInvites)) { recentInvites = new HashSet <string>(); } var circumventionAttempt = !recentInvites.Add(invite.Code) && attemptedWorkaround; //do not flip, must add to cache always InviteCodeCache.Set(message.Author.Id, recentInvites, CacheDuration); var removed = false; try { DeletedMessagesMonitor.RemovedByBotCache.Set(message.Id, true, DeletedMessagesMonitor.CacheRetainTime); await message.DeleteAsync("Not a white-listed discord invite").ConfigureAwait(false); removed = true; } catch (Exception e) { Config.Log.Warn(e); } var codeResolveMsg = $"Invite {invite.Code} was resolved to the {invite.Guild?.Name} server"; var reportMsg = codeResolveMsg; string userMsg; if (circumventionAttempt) { reportMsg += "\nAlso tried to workaround filter despite being asked not to do so."; userMsg = $"{message.Author.Mention} you have been asked nicely to not post invites to this unapproved discord server before."; } else { userMsg = $"{message.Author.Mention} invites to other servers must be whitelisted first.\n"; if (removed) { userMsg += "Please refrain from posting it again until you have received an approval from a moderator."; } else { userMsg += "Please remove it and refrain from posting it again until you have received an approval from a moderator."; } } await client.ReportAsync("🛃 An unapproved discord invite", message, reportMsg, null, ReportSeverity.Low).ConfigureAwait(false); await message.Channel.SendMessageAsync(userMsg).ConfigureAwait(false); if (circumventionAttempt) { await Warnings.AddAsync(client, message, message.Author.Id, message.Author.Username, client.CurrentUser, "Attempted to circumvent discord invite filter", codeResolveMsg); } return(false); } } return(true); }
public static async void EnqueueLogProcessing(DiscordClient client, DiscordChannel channel, DiscordMessage message, DiscordMember requester = null, bool checkExternalLinks = false) { try { if (!QueueLimiter.Wait(0)) { await channel.SendMessageAsync("Log processing is rate limited, try again a bit later").ConfigureAwait(false); return; } bool parsedLog = false; var startTime = Stopwatch.StartNew(); DiscordMessage botMsg = null; try { var possibleHandlers = sourceHandlers.Select(h => h.FindHandlerAsync(message, archiveHandlers).ConfigureAwait(false).GetAwaiter().GetResult()).ToList(); var source = possibleHandlers.FirstOrDefault(h => h.source != null).source; var fail = possibleHandlers.FirstOrDefault(h => !string.IsNullOrEmpty(h.failReason)).failReason; if (source != null) { Config.Log.Debug($">>>>>>> {message.Id % 100} Parsing log '{source.FileName}' from {message.Author.Username}#{message.Author.Discriminator} ({message.Author.Id}) using {source.GetType().Name} ({source.SourceFileSize} bytes)..."); var analyzingProgressEmbed = GetAnalyzingMsgEmbed(client); botMsg = await channel.SendMessageAsync(embed : analyzingProgressEmbed.AddAuthor(client, message, source)).ConfigureAwait(false); parsedLog = true; LogParseState result = null; using (var timeout = new CancellationTokenSource(Config.LogParsingTimeout)) { using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, Config.Cts.Token); var tries = 0; do { result = await ParseLogAsync( source, async() => botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed : analyzingProgressEmbed.AddAuthor(client, message, source)).ConfigureAwait(false), combinedTokenSource.Token ).ConfigureAwait(false); tries++; } while (result == null && !combinedTokenSource.IsCancellationRequested && tries < 3); } if (result == null) { botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed : new DiscordEmbedBuilder { Description = "Log analysis failed, most likely cause is a truncated/invalid log.\n" + "Please run the game again and re-upload a new copy.", Color = Config.Colors.LogResultFailed, } .AddAuthor(client, message, source) .Build() ).ConfigureAwait(false); } else { result.ParsingTime = startTime.Elapsed; try { if (result.Error == LogParseState.ErrorCode.PiracyDetected) { var yarr = client.GetEmoji(":piratethink:", "☠"); result.ReadBytes = 0; if (message.Author.IsWhitelisted(client, channel.Guild)) { var piracyWarning = await result.AsEmbedAsync(client, message, source).ConfigureAwait(false); piracyWarning = piracyWarning.WithDescription("Please remove the log and issue warning to the original author of the log"); botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed : piracyWarning).ConfigureAwait(false); await client.ReportAsync(yarr + " Pirated Release (whitelisted by role)", message, result.SelectedFilter?.String, result.SelectedFilterContext, ReportSeverity.Low).ConfigureAwait(false); } else { var severity = ReportSeverity.Low; try { await message.DeleteAsync("Piracy detected in log").ConfigureAwait(false); } catch (Exception e) { severity = ReportSeverity.High; Config.Log.Warn(e, $"Unable to delete message in {channel.Name}"); } try { /* * botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, * $"{message.Author.Mention}, please read carefully:", * embed: await result.AsEmbedAsync(client, message, source).ConfigureAwait(false) * ).ConfigureAwait(false); */ botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, $"{message.Author.Mention}, please read carefully:\n" + "🏴☠️ **Pirated content detected** 🏴☠️\n" + "__You are being denied further support until you legally dump the game__.\n" + "Please note that the RPCS3 community and its developers do not support piracy.\n" + "Most of the issues with pirated dumps occur due to them being modified in some way " + "that prevent them from working on RPCS3.\n" + "If you need help obtaining valid working dump of the game you own, please read the quickstart guide at <https://rpcs3.net/quickstart>" ).ConfigureAwait(false); } catch (Exception e) { Config.Log.Error(e, "Failed to send piracy warning"); } try { await client.ReportAsync(yarr + " Pirated Release", message, result.SelectedFilter?.String, result.SelectedFilterContext, severity).ConfigureAwait(false); } catch (Exception e) { Config.Log.Error(e, "Failed to send piracy report"); } if (!(message.Channel.IsPrivate || (message.Channel.Name?.Contains("spam") ?? true))) { await Warnings.AddAsync(client, message, message.Author.Id, message.Author.Username, client.CurrentUser, "Pirated Release", $"{result.SelectedFilter?.String} - {result.SelectedFilterContext?.Sanitize()}"); } } } else { if (result.SelectedFilter != null) { await ContentFilter.PerformFilterActions(client, message, result.SelectedFilter, FilterAction.IssueWarning | FilterAction.SendMessage, result.SelectedFilterContext).ConfigureAwait(false); } botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, requester == null?null : $"Analyzed log from {client.GetMember(channel.Guild, message.Author)?.GetUsernameWithNickname()} by request from {requester.Mention}:", embed : await result.AsEmbedAsync(client, message, source).ConfigureAwait(false) ).ConfigureAwait(false); } } catch (Exception e) { Config.Log.Error(e, "Sending log results failed"); } } return; } else if (!string.IsNullOrEmpty(fail) && ("help".Equals(channel.Name, StringComparison.InvariantCultureIgnoreCase) || LimitedToSpamChannel.IsSpamChannel(channel))) { await channel.SendMessageAsync($"{message.Author.Mention} {fail}").ConfigureAwait(false); return; } if (!"help".Equals(channel.Name, StringComparison.InvariantCultureIgnoreCase)) { return; } var potentialLogExtension = message.Attachments.Select(a => Path.GetExtension(a.FileName).ToUpperInvariant().TrimStart('.')).FirstOrDefault(); switch (potentialLogExtension) { case "TXT": { await channel.SendMessageAsync($"{message.Author.Mention} Please upload the full RPCS3.log.gz (or RPCS3.log with a zip/rar icon) file after closing the emulator instead of copying the logs from RPCS3's interface, as it doesn't contain all the required information.").ConfigureAwait(false); return; } } if (string.IsNullOrEmpty(message.Content)) { return; } var linkStart = message.Content.IndexOf("http"); if (linkStart > -1) { var link = message.Content[linkStart..].Split(linkSeparator, 2)[0]; if (link.Contains(".log", StringComparison.InvariantCultureIgnoreCase) || link.Contains("rpcs3.zip", StringComparison.CurrentCultureIgnoreCase)) { await channel.SendMessageAsync("If you intended to upload a log file please re-upload it directly to discord").ConfigureAwait(false); } }
public static async Task PerformFilterActions(DiscordClient client, DiscordMessage message, Piracystring trigger, FilterAction ignoreFlags = 0, string triggerContext = null, string infraction = null, string warningReason = null) { if (trigger == null) { return; } var severity = ReportSeverity.Low; var completedActions = new List <FilterAction>(); if (trigger.Actions.HasFlag(FilterAction.RemoveContent) && !ignoreFlags.HasFlag(FilterAction.RemoveContent)) { try { DeletedMessagesMonitor.RemovedByBotCache.Set(message.Id, true, DeletedMessagesMonitor.CacheRetainTime); await message.Channel.DeleteMessageAsync(message, $"Removed according to filter '{trigger}'").ConfigureAwait(false); completedActions.Add(FilterAction.RemoveContent); } catch (Exception e) { Config.Log.Warn(e); severity = ReportSeverity.High; } try { var author = client.GetMember(message.Author); Config.Log.Debug($"Removed message from {author.GetMentionWithNickname()} in #{message.Channel.Name}: {message.Content}"); } catch (Exception e) { Config.Log.Warn(e); } } if (trigger.Actions.HasFlag(FilterAction.IssueWarning) && !ignoreFlags.HasFlag(FilterAction.IssueWarning)) { try { await Warnings.AddAsync(client, message, message.Author.Id, message.Author.Username, client.CurrentUser, warningReason ?? "Mention of piracy", message.Content.Sanitize()).ConfigureAwait(false); completedActions.Add(FilterAction.IssueWarning); } catch (Exception e) { Config.Log.Warn(e, $"Couldn't issue warning in #{message.Channel.Name}"); } } if (trigger.Actions.HasFlag(FilterAction.SendMessage) && !ignoreFlags.HasFlag(FilterAction.SendMessage)) { try { var msgContent = trigger.CustomMessage; if (string.IsNullOrEmpty(msgContent)) { var rules = await client.GetChannelAsync(Config.BotRulesChannelId).ConfigureAwait(false); msgContent = $"Please follow the {rules.Mention} and do not discuss piracy on this server. Repeated offence may result in a ban."; } await message.Channel.SendMessageAsync($"{message.Author.Mention} {msgContent}").ConfigureAwait(false); completedActions.Add(FilterAction.SendMessage); } catch (Exception e) { Config.Log.Warn(e, $"Failed to send message in #{message.Channel.Name}"); } } if (trigger.Actions.HasFlag(FilterAction.ShowExplain) && !ignoreFlags.HasFlag(FilterAction.ShowExplain)) { var result = await Explain.LookupTerm(trigger.ExplainTerm).ConfigureAwait(false); await Explain.SendExplanation(result, trigger.ExplainTerm, message).ConfigureAwait(false); } var actionList = ""; foreach (FilterAction fa in Enum.GetValues(typeof(FilterAction))) { if (trigger.Actions.HasFlag(fa) && !ignoreFlags.HasFlag(fa)) { actionList += (completedActions.Contains(fa) ? "✅" : "❌") + " " + fa + ' '; } } try { if (!trigger.Actions.HasFlag(FilterAction.MuteModQueue) && !ignoreFlags.HasFlag(FilterAction.MuteModQueue)) { await client.ReportAsync(infraction ?? "🤬 Content filter hit", message, trigger.String, triggerContext ?? message.Content, severity, actionList).ConfigureAwait(false); } } catch (Exception e) { Config.Log.Error(e, "Failed to report content filter hit"); } }
public static async void BackgroundProcessor(MessageCreateEventArgs args) { try { var message = args.Message; if (!QueueLimiter.Wait(0)) { await args.Channel.SendMessageAsync("Log processing is rate limited, try again a bit later").ConfigureAwait(false); return; } bool parsedLog = false; var startTime = Stopwatch.StartNew(); try { foreach (var attachment in message.Attachments.Where(a => a.FileSize < Config.AttachmentSizeLimit && !a.FileName.EndsWith("tty.log", StringComparison.InvariantCultureIgnoreCase))) { foreach (var handler in handlers) { if (await handler.CanHandleAsync(attachment).ConfigureAwait(false)) { await args.Channel.TriggerTypingAsync().ConfigureAwait(false); Config.Log.Debug($">>>>>>> {message.Id % 100} Parsing log from attachment {attachment.FileName} ({attachment.FileSize})..."); parsedLog = true; LogParseState result = null; try { var pipe = new Pipe(); var fillPipeTask = handler.FillPipeAsync(attachment, pipe.Writer); result = await LogParser.ReadPipeAsync(pipe.Reader).ConfigureAwait(false); await fillPipeTask.ConfigureAwait(false); } catch (Exception e) { Config.Log.Error(e, "Log parsing failed"); } if (result == null) { await args.Channel.SendMessageAsync("Log analysis failed, most likely cause is a truncated/invalid log. Please run the game again and reupload the new copy.").ConfigureAwait(false); } else { try { if (result.Error == LogParseState.ErrorCode.PiracyDetected) { if (args.Author.IsWhitelisted(args.Client, args.Guild)) { await Task.WhenAll( args.Channel.SendMessageAsync("I see wha' ye did thar ☠"), args.Client.ReportAsync("Pirated Release (whitelisted by role)", args.Message, result.PiracyTrigger, result.PiracyContext, ReportSeverity.Low) ).ConfigureAwait(false); } else { var severity = ReportSeverity.Low; try { await message.DeleteAsync("Piracy detected in log").ConfigureAwait(false); } catch (Exception e) { severity = ReportSeverity.High; Config.Log.Warn(e, $"Unable to delete message in {args.Channel.Name}"); } await args.Channel.SendMessageAsync(embed : await result.AsEmbedAsync(args.Client, args.Message).ConfigureAwait(false)).ConfigureAwait(false); await Task.WhenAll( args.Client.ReportAsync("Pirated Release", args.Message, result.PiracyTrigger, result.PiracyContext, severity), Warnings.AddAsync(args.Client, args.Message, args.Message.Author.Id, args.Message.Author.Username, args.Client.CurrentUser, "Pirated Release", $"{message.Content.Sanitize()} - {result.PiracyTrigger}") ); } } else { await args.Channel.SendMessageAsync(embed : await result.AsEmbedAsync(args.Client, args.Message).ConfigureAwait(false)).ConfigureAwait(false); } } catch (Exception e) { Config.Log.Error(e, "Sending log results failed"); } } return; } } } if (!"help".Equals(args.Channel.Name, StringComparison.InvariantCultureIgnoreCase)) { return; } var potentialLogExtension = message.Attachments.Select(a => Path.GetExtension(a.FileName).ToUpperInvariant().TrimStart('.')).FirstOrDefault(); switch (potentialLogExtension) { case "TXT": { await args.Channel.SendMessageAsync($"{message.Author.Mention} please do not copy/paste logs from UI, they do not contain all the required information; ask if you do not know how to upload full log file").ConfigureAwait(false); return; } } if (string.IsNullOrEmpty(message.Content)) { return; } var linkStart = message.Content.IndexOf("http"); if (linkStart > -1) { var link = message.Content.Substring(linkStart).Split(linkSeparator, 2)[0]; if (link.Contains(".log", StringComparison.InvariantCultureIgnoreCase) || link.Contains("rpcs3.zip", StringComparison.CurrentCultureIgnoreCase)) { await args.Channel.SendMessageAsync("If you intended to upload a log file please re-upload it directly to discord").ConfigureAwait(false); } } } finally { QueueLimiter.Release(); if (parsedLog) { Config.Log.Debug($"<<<<<<< {message.Id % 100} Finished parsing in {startTime.Elapsed}"); } } } catch (Exception e) { Config.Log.Error(e, "Error parsing log"); } }