Use Result<T> in log parsing handlers

This commit is contained in:
13xforever
2025-12-12 09:11:53 +05:00
parent 1d63572d14
commit 186fda7958
20 changed files with 166 additions and 150 deletions

View File

@@ -72,6 +72,7 @@
<PackageReference Include="NLog" Version="6.0.7" />
<PackageReference Include="NLog.Extensions.Logging" Version="6.1.0" />
<PackageReference Include="NReco.Text.AhoCorasickDoubleArrayTrie" Version="1.1.1" />
<PackageReference Include="Result.Net" Version="1.7.0" />
<PackageReference Include="SharpCompress" Version="0.42.1" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
<PackageReference Include="System.Linq.Async" Version="6.0.3" />

View File

@@ -1,6 +1,7 @@
using System.IO;
using System.IO.Compression;
using System.IO.Pipelines;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
@@ -11,17 +12,17 @@ internal sealed class GzipHandler: IArchiveHandler
public long LogSize { get; private set; }
public long SourcePosition { get; private set; }
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
public Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
{
if (header.Length >= Header.Length)
{
if (header[..Header.Length].SequenceEqual(Header))
return (true, null);
return Result.Success();
}
else if (fileName.EndsWith(".log.gz", StringComparison.InvariantCultureIgnoreCase)
&& !fileName.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase))
return (true, null);
return (false, null);
return Result.Success();
return Result.Failure();
}
public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken)

View File

@@ -1,11 +1,12 @@
using System.IO;
using System.IO.Pipelines;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
public interface IArchiveHandler
{
(bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header);
Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header);
Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken);
long LogSize { get; }
long SourcePosition { get; }

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.IO.Pipelines;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
@@ -8,16 +9,16 @@ internal sealed class PlainTextHandler: IArchiveHandler
public long LogSize { get; private set; }
public long SourcePosition { get; private set; }
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
public Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
{
LogSize = fileSize;
if (fileName.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase))
return (false, null);
return Result.Failure();
if (header.Length > 10 && Encoding.UTF8.GetString(header[..30]).Contains("RPCS3 v"))
return (true, null);
return Result.Success();
return (false, null);
return Result.Failure();
}
public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken)

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.IO.Pipelines;
using ResultNet;
using SharpCompress.Readers.Rar;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
@@ -11,17 +12,17 @@ internal sealed class RarHandler: IArchiveHandler
public long LogSize { get; private set; }
public long SourcePosition { get; private set; }
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
public Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
{
if (header.Length >= Header.Length && header[..Header.Length].SequenceEqual(Header)
|| header.Length == 0 && fileName.EndsWith(".rar", StringComparison.InvariantCultureIgnoreCase))
{
var firstEntry = Encoding.ASCII.GetString(header);
if (!firstEntry.Contains(".log", StringComparison.InvariantCultureIgnoreCase))
return (false, "Archive doesn't contain any logs.");
return (true, null);
return Result.Failure().WithMessage("Archive doesn't contain any logs.");
return Result.Success();
}
return (false, null);
return Result.Failure();
}
public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken)
@@ -30,14 +31,14 @@ internal sealed class RarHandler: IArchiveHandler
{
await using var statsStream = new BufferCopyStream(sourceStream);
using var rarReader = RarReader.Open(statsStream);
while (rarReader.MoveToNextEntry())
while (await rarReader.MoveToNextEntryAsync(cancellationToken).ConfigureAwait(false))
{
if (!rarReader.Entry.IsDirectory
&& rarReader.Entry.Key!.EndsWith(".log", StringComparison.InvariantCultureIgnoreCase)
&& !rarReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase))
{
LogSize = rarReader.Entry.Size;
await using var rarStream = rarReader.OpenEntryStream();
await using var rarStream = await rarReader.OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
int read;
FlushResult flushed;
do

View File

@@ -1,6 +1,7 @@
using System.IO;
using System.IO.Pipelines;
using CompatApiClient.Utils;
using ResultNet;
using SharpCompress.Archives.SevenZip;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
@@ -12,16 +13,16 @@ internal sealed class SevenZipHandler: IArchiveHandler
public long LogSize { get; private set; }
public long SourcePosition { get; private set; }
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
public Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
{
if (header.Length >= Header.Length && header[..Header.Length].SequenceEqual(Header)
|| header.Length == 0 && fileName.EndsWith(".7z", StringComparison.InvariantCultureIgnoreCase))
{
if (fileSize > Config.AttachmentSizeLimit)
return (false, $"Log size is too large for 7z format: {fileSize.AsStorageUnit()} (max allowed is {Config.AttachmentSizeLimit.AsStorageUnit()})");
return (true, null);
return Result.Failure().WithMessage($"Log size is too large for 7z format: {fileSize.AsStorageUnit()} (max allowed is {Config.AttachmentSizeLimit.AsStorageUnit()})");
return Result.Success();
}
return (false, null);
return Result.Failure();
}
public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken)
@@ -33,13 +34,13 @@ internal sealed class SevenZipHandler: IArchiveHandler
fileStream.Seek(0, SeekOrigin.Begin);
using var zipArchive = SevenZipArchive.Open(fileStream);
using var zipReader = zipArchive.ExtractAllEntries();
while (zipReader.MoveToNextEntry())
while (await zipReader.MoveToNextEntryAsync(cancellationToken).ConfigureAwait(false))
if (!zipReader.Entry.IsDirectory
&& zipReader.Entry.Key!.EndsWith(".log", StringComparison.InvariantCultureIgnoreCase)
&& !zipReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase))
{
LogSize = zipReader.Entry.Size;
await using var entryStream = zipReader.OpenEntryStream();
await using var entryStream = await zipReader.OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
int read;
FlushResult flushed;
do

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.IO.Pipelines;
using ResultNet;
using SharpCompress.Readers.Zip;
namespace CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
@@ -11,7 +12,7 @@ internal sealed class ZipHandler: IArchiveHandler
public long LogSize { get; private set; }
public long SourcePosition { get; private set; }
public (bool result, string? reason) CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
public Result CanHandle(string fileName, int fileSize, ReadOnlySpan<byte> header)
{
if (header.Length >= Header.Length && header[..Header.Length].SequenceEqual(Header)
@@ -19,12 +20,10 @@ internal sealed class ZipHandler: IArchiveHandler
{
var firstEntry = Encoding.ASCII.GetString(header);
if (!firstEntry.Contains(".log", StringComparison.InvariantCultureIgnoreCase))
return (false, "Archive doesn't contain any logs.");
return (true, null);
return Result.Failure().WithMessage("Archive doesn't contain any logs.");
return Result.Success();
}
return (false, null);
return Result.Failure();
}
public async Task FillPipeAsync(Stream sourceStream, PipeWriter writer, CancellationToken cancellationToken)
@@ -33,14 +32,14 @@ internal sealed class ZipHandler: IArchiveHandler
{
await using var statsStream = new BufferCopyStream(sourceStream);
using var zipReader = ZipReader.Open(statsStream);
while (zipReader.MoveToNextEntry())
while (await zipReader.MoveToNextEntryAsync(cancellationToken).ConfigureAwait(false))
{
if (!zipReader.Entry.IsDirectory
&& zipReader.Entry.Key!.EndsWith(".log", StringComparison.InvariantCultureIgnoreCase)
&& !zipReader.Entry.Key.Contains("tty.log", StringComparison.InvariantCultureIgnoreCase))
{
LogSize = zipReader.Entry.Size;
await using var zipStream = zipReader.OpenEntryStream();
await using var zipStream = await zipReader.OpenEntryStreamAsync(cancellationToken).ConfigureAwait(false);
int read, totalRead = 0;
FlushResult flushed;
do

View File

@@ -1,6 +1,7 @@
using System.Buffers;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -10,5 +11,5 @@ internal abstract class BaseSourceHandler: ISourceHandler
protected const int SnoopBufferSize = 4096;
internal static readonly ArrayPool<byte> BufferPool = ArrayPool<byte>.Create(SnoopBufferSize, 64);
public abstract Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
public abstract Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
}

View File

@@ -1,12 +1,13 @@
using System.IO.Pipelines;
using System.Net.Http;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
internal sealed class DiscordAttachmentHandler : BaseSourceHandler
{
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
using var client = HttpClientFactory.Create();
foreach (var attachment in message.Attachments)
@@ -20,11 +21,11 @@ internal sealed class DiscordAttachmentHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(attachment.FileName, attachment.FileSize, buf.AsSpan(0, read));
if (canHandle)
return (new DiscordAttachmentSource(attachment, handler, attachment.FileName, attachment.FileSize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(attachment.FileName, attachment.FileSize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new DiscordAttachmentSource(attachment, handler, attachment.FileName, attachment.FileSize));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -37,7 +38,7 @@ internal sealed class DiscordAttachmentHandler : BaseSourceHandler
Config.Log.Error(e, "Error sniffing the rar content");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class DiscordAttachmentSource : ISource

View File

@@ -4,6 +4,7 @@ using System.Net.Http;
using System.Text.RegularExpressions;
using CompatApiClient.Utils;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -13,19 +14,19 @@ internal sealed partial class DropboxHandler : BaseSourceHandler
[GeneratedRegex(@"(?<dropbox_link>(https?://)?(www\.)?dropbox\.com/s/(?<dropbox_id>[^/\s]+)/(?<filename>[^/\?\s])(/dl=[01])?)", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var client = HttpClientFactory.Create();
foreach (Match m in matches)
{
if (m.Groups["dropbox_link"].Value is not { Length: > 0 } lnk
if (m.Groups["dropbox_link"].Value is not { Length: >0 } lnk
|| !Uri.TryCreate(lnk, UriKind.Absolute, out var uri))
continue;
@@ -52,11 +53,11 @@ internal sealed partial class DropboxHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new DropboxSource(uri, handler, filename, filesize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new DropboxSource(uri, handler, filename, filesize));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -70,7 +71,7 @@ internal sealed partial class DropboxHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["dropbox_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class DropboxSource : ISource

View File

@@ -1,6 +1,7 @@
using System.IO;
using System.IO.Pipelines;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -37,11 +38,11 @@ internal class FileSource : ISource
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(Path.GetFileName(path), (int)stream.Length, buf.AsSpan(0, read));
if (canHandle)
var result = handler.CanHandle(Path.GetFileName(path), (int)stream.Length, buf.AsSpan(0, read));
if (result.IsSuccess())
return new FileSource(path, handler);
if (!string.IsNullOrEmpty(reason))
if (result.Message is {Length: >0} reason)
throw new InvalidOperationException(reason);
}
throw new InvalidOperationException("Unknown source type");

View File

@@ -3,6 +3,7 @@ using System.IO.Pipelines;
using System.Net.Http;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -11,14 +12,14 @@ internal sealed partial class GenericLinkHandler : BaseSourceHandler
[GeneratedRegex(@"(?<link>(https?://)?(github\.com/RPCS3/rpcs3|cdn\.discordapp\.com/attachments)/.*/(?<filename>[^/\?\s]+\.(gz|zip|rar|7z|log)))", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var client = HttpClientFactory.Create();
foreach (Match m in matches)
@@ -51,11 +52,11 @@ internal sealed partial class GenericLinkHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new GenericSource(uri, handler, host, filename, filesize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new GenericSource(uri, handler, host, filename, filesize));
else if (result.Message is {Length: > 0})
return result.Cast<ISource>();
}
}
finally
@@ -68,7 +69,7 @@ internal sealed partial class GenericLinkHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class GenericSource : ISource

View File

@@ -6,6 +6,7 @@ using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Download;
using Google.Apis.Drive.v3;
using ResultNet;
using FileMeta = Google.Apis.Drive.v3.Data.File;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -17,17 +18,15 @@ internal sealed partial class GoogleDriveHandler: BaseSourceHandler
private static readonly string[] Scopes = [DriveService.Scope.DriveReadonly];
private static readonly string ApplicationName = "RPCS3 Compatibility Bot 2.0";
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (string.IsNullOrEmpty(Config.GoogleApiCredentials))
return (null, null);
if (message.Content is not {Length: >0}
|| Config.GoogleApiCredentials is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
var client = GetClient();
foreach (Match m in matches)
@@ -55,11 +54,11 @@ internal sealed partial class GoogleDriveHandler: BaseSourceHandler
var read = (int)progress.BytesDownloaded;
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(fileMeta.Name, (int)fileMeta.Size, buf.AsSpan(0, read));
if (canHandle)
return (new GoogleDriveSource(client, fileInfoRequest, fileMeta, handler), null);
else if (!string.IsNullOrEmpty(reason))
return(null, reason);
var result = handler.CanHandle(fileMeta.Name, (int)fileMeta.Size, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new GoogleDriveSource(client, fileInfoRequest, fileMeta, handler));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -72,7 +71,7 @@ internal sealed partial class GoogleDriveHandler: BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["gdrive_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private static DriveService GetClient(string? json = null)

View File

@@ -1,11 +1,12 @@
using System.IO.Pipelines;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
public interface ISourceHandler
{
Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers);
}
public interface ISource: IDisposable

View File

@@ -3,6 +3,7 @@ using System.Net.Http;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using MediafireClient;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -13,14 +14,14 @@ internal sealed partial class MediafireHandler : BaseSourceHandler
private static partial Regex ExternalLink();
private static readonly Client Client = new();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var client = HttpClientFactory.Create();
foreach (Match m in matches)
@@ -37,7 +38,7 @@ internal sealed partial class MediafireHandler : BaseSourceHandler
Config.Log.Debug($"Trying to get download link for {webLink}…");
var directLink = await Client.GetDirectDownloadLinkAsync(webLink, Config.Cts.Token).ConfigureAwait(false);
if (directLink is null)
return (null, null);
return Result.Failure<ISource>();
Config.Log.Debug($"Trying to get content size for {directLink}…");
using (var request = new HttpRequestMessage(HttpMethod.Head, directLink))
@@ -57,11 +58,11 @@ internal sealed partial class MediafireHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new MediafireSource(directLink, handler, filename, filesize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new MediafireSource(directLink, handler, filename, filesize));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
Config.Log.Debug("MediaFire Response:\n" + Encoding.UTF8.GetString(buf, 0, read));
}
@@ -75,7 +76,7 @@ internal sealed partial class MediafireHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["mediafire_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class MediafireSource : ISource

View File

@@ -2,6 +2,7 @@
using System.Text.RegularExpressions;
using CG.Web.MegaApiClient;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -13,14 +14,14 @@ internal sealed partial class MegaHandler : BaseSourceHandler
private static partial Regex ExternalLink();
private static readonly IProgress<double> Doodad = new Progress<double>(_ => { });
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
var client = new MegaApiClient();
await client.LoginAnonymousAsync();
@@ -28,7 +29,7 @@ internal sealed partial class MegaHandler : BaseSourceHandler
{
try
{
if (m.Groups["mega_link"].Value is not { Length: > 0 } lnk
if (m.Groups["mega_link"].Value is not { Length: >0 } lnk
|| !Uri.TryCreate(lnk, UriKind.Absolute, out var uri))
continue;
@@ -43,11 +44,11 @@ internal sealed partial class MegaHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(node.Name, (int)node.Size, buf.AsSpan(0, read));
if (canHandle)
return (new MegaSource(client, uri, node, handler), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(node.Name, (int)node.Size, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new MegaSource(client, uri, node, handler));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -60,7 +61,7 @@ internal sealed partial class MegaHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["mega_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class MegaSource : ISource

View File

@@ -3,6 +3,7 @@ using System.Net.Http;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using OneDriveClient;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -12,14 +13,14 @@ internal sealed partial class OneDriveSourceHandler : BaseSourceHandler
private static partial Regex ExternalLink();
private static readonly Client Client = new();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var httpClient = HttpClientFactory.Create();
foreach (Match m in matches)
@@ -43,11 +44,11 @@ internal sealed partial class OneDriveSourceHandler : BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new OneDriveSource(uri, handler, filename, filesize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new OneDriveSource(uri, handler, filename, filesize));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -65,7 +66,7 @@ internal sealed partial class OneDriveSourceHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["mega_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}

View File

@@ -2,6 +2,7 @@
using System.Net.Http;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -10,14 +11,14 @@ internal sealed partial class PastebinHandler : BaseSourceHandler
[GeneratedRegex(@"(?<pastebin_link>(https?://)pastebin.com/(raw/)?(?<pastebin_id>[^/>\s]+))", DefaultOptions)]
private static partial Regex ExternalLink();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var client = HttpClientFactory.Create();
foreach (Match m in matches)
@@ -37,11 +38,11 @@ internal sealed partial class PastebinHandler : BaseSourceHandler
var filesize = stream.CanSeek ? (int)stream.Length : 0;
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new PastebinSource(uri, filename, filesize, handler), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new PastebinSource(uri, filename, filesize, handler));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -54,7 +55,7 @@ internal sealed partial class PastebinHandler : BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["mega_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class PastebinSource : ISource

View File

@@ -2,6 +2,7 @@
using System.Net.Http;
using System.Text.RegularExpressions;
using CompatBot.EventHandlers.LogParsing.ArchiveHandlers;
using ResultNet;
using YandexDiskClient;
namespace CompatBot.EventHandlers.LogParsing.SourceHandlers;
@@ -12,14 +13,14 @@ internal sealed partial class YandexDiskHandler: BaseSourceHandler
private static partial Regex ExternalLink();
private static readonly Client Client = new();
public override async Task<(ISource? source, string? failReason)> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
public override async Task<Result<ISource>> FindHandlerAsync(DiscordMessage message, ICollection<IArchiveHandler> handlers)
{
if (string.IsNullOrEmpty(message.Content))
return (null, null);
if (message.Content is not {Length: >0})
return Result.Failure<ISource>();
var matches = ExternalLink().Matches(message.Content);
if (matches.Count == 0)
return (null, null);
if (matches is [])
return Result.Failure<ISource>();
using var client = HttpClientFactory.Create();
foreach (Match m in matches)
@@ -34,12 +35,12 @@ internal sealed partial class YandexDiskHandler: BaseSourceHandler
var filesize = -1;
var resourceInfo = await Client.GetResourceInfoAsync(webLink, Config.Cts.Token).ConfigureAwait(false);
if (string.IsNullOrEmpty(resourceInfo?.File))
return (null, null);
if (resourceInfo is not {File.Length: >0})
return Result.Failure<ISource>();
if (resourceInfo.Size.HasValue)
filesize = resourceInfo.Size.Value;
if (!string.IsNullOrEmpty(resourceInfo.Name))
if (resourceInfo.Name is {Length: >0})
filename = resourceInfo.Name;
await using var stream = await client.GetStreamAsync(resourceInfo.File).ConfigureAwait(false);
@@ -49,11 +50,11 @@ internal sealed partial class YandexDiskHandler: BaseSourceHandler
var read = await stream.ReadBytesAsync(buf).ConfigureAwait(false);
foreach (var handler in handlers)
{
var (canHandle, reason) = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (canHandle)
return (new YaDiskSource(resourceInfo.File, handler, filename, filesize), null);
else if (!string.IsNullOrEmpty(reason))
return (null, reason);
var result = handler.CanHandle(filename, filesize, buf.AsSpan(0, read));
if (result.IsSuccess())
return Result.Success<ISource>(new YaDiskSource(resourceInfo.File, handler, filename, filesize));
else if (result.Message is {Length: >0})
return result.Cast<ISource>();
}
}
finally
@@ -67,7 +68,7 @@ internal sealed partial class YandexDiskHandler: BaseSourceHandler
Config.Log.Warn(e, $"Error sniffing {m.Groups["yadisk_link"].Value}");
}
}
return (null, null);
return Result.Failure<ISource>();
}
private sealed class YaDiskSource : ISource

View File

@@ -14,6 +14,7 @@ using CompatBot.EventHandlers.LogParsing.SourceHandlers;
using CompatBot.Utils.Extensions;
using CompatBot.Utils.ResultFormatters;
using Microsoft.Extensions.Caching.Memory;
using ResultNet;
namespace CompatBot.EventHandlers;
@@ -85,19 +86,19 @@ public static class LogParsingHandler
.ToAsyncEnumerable()
.SelectAwait(async h => await h.FindHandlerAsync(message, ArchiveHandlers).ConfigureAwait(false))
.ToList();
using var source = possibleHandlers.FirstOrDefault(h => h.source != null).source;
var fail = possibleHandlers.FirstOrDefault(h => !string.IsNullOrEmpty(h.failReason)).failReason;
foreach (var (s, _) in possibleHandlers)
using var source = possibleHandlers.FirstOrDefault(h => h.IsSuccess())?.Data;
var fail = possibleHandlers.FirstOrDefault(h => h is {Message.Length: >0})?.Message;
foreach (var h in possibleHandlers)
{
if (ReferenceEquals(s, source))
if (ReferenceEquals(h.Data, source))
continue;
s?.Dispose();
h.Data?.Dispose();
}
var isSpamChannel = channel.IsSpamChannel();
var isHelpChannel = channel.IsHelpChannel();
if (source != null)
if (source is not null)
{
if (!QueueLimiter.Wait(0))
{
@@ -133,10 +134,10 @@ public static class LogParsingHandler
).ConfigureAwait(false);
result ??= tmpResult;
tries++;
} while ((tmpResult == null || tmpResult.Error == LogParseState.ErrorCode.UnknownError) &&
} while ((tmpResult is null || tmpResult.Error == LogParseState.ErrorCode.UnknownError) &&
!combinedTokenSource.IsCancellationRequested && tries < 3);
}
if (result == null)
if (result is null)
{
botMsg = await botMsg.UpdateOrCreateMessageAsync(channel, embed: (await new DiscordEmbedBuilder
{
@@ -243,7 +244,7 @@ public static class LogParsingHandler
}
else
{
if (result.SelectedFilter != null)
if (result.SelectedFilter is not null)
{
var ignoreFlags = FilterAction.IssueWarning | FilterAction.SendMessage | FilterAction.ShowExplain;
await ContentFilter.PerformFilterActions(client, message, result.SelectedFilter,
@@ -288,7 +289,7 @@ public static class LogParsingHandler
}
botMsg = await botMsg.UpdateOrCreateMessageAsync(channel,
//requester == null ? null : $"Analyzed log from {client.GetMember(channel.Guild, message.Author)?.GetUsernameWithNickname()} by request from {requester.Mention}:",
//requester is 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);
}
@@ -388,9 +389,9 @@ public static class LogParsingHandler
if (result.FilterTriggers.Any())
{
var (f, c) = result.FilterTriggers.Values.FirstOrDefault(ft => ft.filter.Actions.HasFlag(FilterAction.IssueWarning));
if (f == null)
if (f is null)
(f, c) = result.FilterTriggers.Values.FirstOrDefault(ft => ft.filter.Actions.HasFlag(FilterAction.RemoveContent));
if (f == null)
if (f is null)
(f, c) = result.FilterTriggers.Values.FirstOrDefault();
result.SelectedFilter = f;
result.SelectedFilterContext = c;