mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
166 lines
7.2 KiB
C#
166 lines
7.2 KiB
C#
using System.Threading.Channels;
|
|
using CompatApiClient.Utils;
|
|
using CompatBot.Commands;
|
|
using CompatBot.Database;
|
|
using CompatBot.Database.Providers;
|
|
using CompatBot.Ocr;
|
|
using CompatBot.Utils.Extensions;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
|
|
namespace CompatBot.EventHandlers;
|
|
|
|
internal sealed class MediaScreenshotMonitor
|
|
{
|
|
private static readonly Channel<(DiscordMessage msg, string imgUrl)> WorkQueue = Channel.CreateUnbounded<(DiscordMessage msg, string imgUrl)>();
|
|
private static readonly MemoryCache RemovedMessages = new(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromMinutes(10) });
|
|
private static readonly TimeSpan CachedTime = TimeSpan.FromMinutes(5);
|
|
public DiscordClient Client { get; internal set; } = null!;
|
|
public static int MaxQueueLength { get; private set; }
|
|
|
|
public async Task OnMessageCreated(DiscordClient client, MessageCreatedEventArgs evt)
|
|
{
|
|
if (!OcrProvider.IsAvailable)
|
|
return;
|
|
|
|
if (evt.Message is not {} message)
|
|
return;
|
|
|
|
// if (!Config.Moderation.OcrChannels.Contains(evt.Channel.Id))
|
|
// return;
|
|
|
|
if (message.Author.IsBotSafeCheck())
|
|
return;
|
|
|
|
#if !DEBUG
|
|
if (await message.Author.IsSmartlistedAsync(client).ConfigureAwait(false))
|
|
return;
|
|
#endif
|
|
|
|
EnqueueOcrTask(evt.Message);
|
|
}
|
|
|
|
public static void EnqueueOcrTask(DiscordMessage message)
|
|
{
|
|
if (!message.Attachments.Any() && !message.Embeds.Any())
|
|
return;
|
|
|
|
var images = Vision.GetImageAttachments(message)
|
|
.Concat(Vision.GetImagesFromEmbeds(message))
|
|
.ToList();
|
|
foreach (var url in images)
|
|
{
|
|
if (!WorkQueue.Writer.TryWrite((message, url)))
|
|
Config.Log.Warn($"Failed to create a new text recognition task for message {message.JumpLink} / {url}");
|
|
}
|
|
}
|
|
|
|
public async Task ProcessWorkQueue()
|
|
{
|
|
if (!OcrProvider.IsAvailable)
|
|
return;
|
|
|
|
do
|
|
{
|
|
MaxQueueLength = Math.Max(MaxQueueLength, WorkQueue.Reader.Count);
|
|
var (msg, imgUrl) = await WorkQueue.Reader.ReadAsync(Config.Cts.Token).ConfigureAwait(false);
|
|
if (Config.Cts.IsCancellationRequested)
|
|
return;
|
|
|
|
if (RemovedMessages.TryGetValue(msg.Id, out bool removed) && removed)
|
|
continue;
|
|
|
|
try
|
|
{
|
|
var signature = GetSignature(msg);
|
|
var prefix = $"[{msg.Id % 100:00}]";
|
|
if (RemovedMessages.TryGetValue(signature, out (Piracystring hit, DiscordMessage msg) previousItem))
|
|
{
|
|
Config.Log.Debug($"{prefix} OCR result of message {msg.JumpLink} from user {msg.Author?.Username} ({msg.Author?.Id}):");
|
|
var ocrTextBuf = new StringBuilder($"OCR result of message <{msg.JumpLink}>:").AppendLine();
|
|
FilterAction suppressFlags = 0;
|
|
if ("media".Equals(msg.Channel?.Name))
|
|
{
|
|
suppressFlags = FilterAction.SendMessage | FilterAction.ShowExplain;
|
|
}
|
|
await ContentFilter.PerformFilterActions(
|
|
Client,
|
|
msg,
|
|
previousItem.hit,
|
|
suppressFlags,
|
|
$"Matched to previously removed message from {previousItem.msg.Author?.Mention}: {previousItem.msg.JumpLink}",
|
|
"🖼 Screenshot of an undesirable content",
|
|
"Screenshot of an undesirable content"
|
|
).ConfigureAwait(false);
|
|
if (previousItem.hit.Actions.HasFlag(FilterAction.RemoveContent))
|
|
RemovedMessages.Set(msg.Id, true, CachedTime);
|
|
}
|
|
else if (await OcrProvider.GetTextAsync(imgUrl, Config.Cts.Token).ConfigureAwait(false) is ({ Length: > 0 } result, var confidence))
|
|
{
|
|
var cnt = true;
|
|
var duplicates = new HashSet<string>();
|
|
Config.Log.Debug($"""
|
|
{prefix} OCR result of message {msg.JumpLink} from user {msg.Author?.Username} ({msg.Author?.Id}) ({confidence * 100:0.00}%):
|
|
{result}
|
|
"""
|
|
);
|
|
var ocrTextBuf = new StringBuilder($"OCR result of message <{msg.JumpLink}> ({confidence * 100:0.00}%):").AppendLine()
|
|
.AppendLine(result.Sanitize());
|
|
if (cnt
|
|
&& confidence > 0.65
|
|
&& await ContentFilter.FindTriggerAsync(FilterContext.Chat, result).ConfigureAwait(false) is Piracystring hit
|
|
&& duplicates.Add(hit.String))
|
|
{
|
|
FilterAction suppressFlags = 0;
|
|
if ("media".Equals(msg.Channel?.Name))
|
|
{
|
|
suppressFlags = FilterAction.SendMessage | FilterAction.ShowExplain;
|
|
}
|
|
await ContentFilter.PerformFilterActions(
|
|
Client,
|
|
msg,
|
|
hit,
|
|
suppressFlags,
|
|
result,
|
|
"🖼 Screenshot of an undesirable content",
|
|
"Screenshot of an undesirable content"
|
|
).ConfigureAwait(false);
|
|
if (hit.Actions.HasFlag(FilterAction.RemoveContent))
|
|
{
|
|
RemovedMessages.Set(msg.Id, true, CachedTime);
|
|
if (signature is { Length: >0})
|
|
RemovedMessages.Set(signature, (hit, msg), CachedTime);
|
|
}
|
|
cnt &= !hit.Actions.HasFlag(FilterAction.RemoveContent) && !hit.Actions.HasFlag(FilterAction.IssueWarning);
|
|
}
|
|
var ocrText = ocrTextBuf.ToString();
|
|
var hasVkDiagInfo = ocrText.Contains("Vulkan Diagnostics Tool v")
|
|
|| ocrText.Contains("VkDiag Version:");
|
|
if (!cnt || hasVkDiagInfo)
|
|
{
|
|
try
|
|
{
|
|
var botSpamCh = await Client.GetChannelAsync(Config.ThumbnailSpamId).ConfigureAwait(false);
|
|
await botSpamCh.SendAutosplitMessageAsync(ocrTextBuf, blockStart: "", blockEnd: "").ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Config.Log.Warn(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Config.Log.Warn(e);
|
|
}
|
|
} while (!Config.Cts.IsCancellationRequested);
|
|
}
|
|
|
|
private static string GetSignature(DiscordMessage msg)
|
|
{
|
|
var result = msg.Content ?? "";
|
|
foreach (var att in msg.Attachments)
|
|
result += $"📎 {att.FileName} ({att.FileSize})\n";
|
|
return result.TrimEnd();
|
|
}
|
|
} |