mirror of
https://github.com/RPCS3/discord-bot.git
synced 2026-01-31 01:25:22 +01:00
Use Result<T> in log parsing handlers
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user