Rework architecture

This commit is contained in:
Alexey Golub
2020-04-21 21:30:42 +03:00
parent 130c0b6fe2
commit 8685a3d7e3
119 changed files with 1520 additions and 1560 deletions

View File

@@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Threading.Tasks;
using CliFx;
using CliFx.Attributes;
using CliFx.Utilities;
using DiscordChatExporter.Domain.Discord.Models;
using DiscordChatExporter.Domain.Exporting;
namespace DiscordChatExporter.Cli.Commands.Base
{
public abstract class ExportCommandBase : TokenCommandBase
{
[CommandOption("format", 'f', Description = "Output file format.")]
public ExportFormat ExportFormat { get; set; } = ExportFormat.HtmlDark;
[CommandOption("output", 'o', Description = "Output file or directory path.")]
public string OutputPath { get; set; } = Directory.GetCurrentDirectory();
[CommandOption("after", Description = "Limit to messages sent after this date.")]
public DateTimeOffset? After { get; set; }
[CommandOption("before", Description = "Limit to messages sent before this date.")]
public DateTimeOffset? Before { get; set; }
[CommandOption("partition", 'p', Description = "Split output into partitions limited to this number of messages.")]
public int? PartitionLimit { get; set; }
[CommandOption("dateformat", Description = "Date format used in output.")]
public string DateFormat { get; set; } = "dd-MMM-yy hh:mm tt";
protected Exporter GetExporter() => new Exporter(GetDiscordClient());
protected async ValueTask ExportAsync(IConsole console, Guild guild, Channel channel)
{
console.Output.Write($"Exporting channel '{channel.Name}'... ");
var progress = console.CreateProgressTicker();
await GetExporter().ExportChatLogAsync(guild, channel,
OutputPath, ExportFormat, DateFormat, PartitionLimit,
After, Before, progress);
console.Output.WriteLine();
console.Output.WriteLine("Done.");
}
protected async ValueTask ExportAsync(IConsole console, Channel channel)
{
var guild = await GetDiscordClient().GetGuildAsync(channel.GuildId);
await ExportAsync(console, guild, channel);
}
protected async ValueTask ExportAsync(IConsole console, string channelId)
{
var channel = await GetDiscordClient().GetChannelAsync(channelId);
await ExportAsync(console, channel);
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CliFx;
using CliFx.Attributes;
using CliFx.Utilities;
using DiscordChatExporter.Domain.Discord.Models;
using DiscordChatExporter.Domain.Exceptions;
using DiscordChatExporter.Domain.Utilities;
using Gress;
using Tyrrrz.Extensions;
namespace DiscordChatExporter.Cli.Commands.Base
{
public abstract class ExportMultipleCommandBase : ExportCommandBase
{
[CommandOption("parallel", Description = "Export this number of separate channels in parallel.")]
public int ParallelLimit { get; set; } = 1;
protected async ValueTask ExportMultipleAsync(IConsole console, IReadOnlyList<Channel> channels)
{
// This uses a separate route from ExportCommandBase because the progress ticker is not thread-safe
// Ugly code ahead. Will need to refactor.
// Progress
console.Output.Write($"Exporting {channels.Count} channels... ");
var ticker = console.CreateProgressTicker();
// TODO: refactor this after improving Gress
var progressManager = new ProgressManager();
progressManager.PropertyChanged += (sender, args) => ticker.Report(progressManager.Progress);
var operations = progressManager.CreateOperations(channels.Count);
// Export channels
var errors = new List<string>();
var successfulExportCount = 0;
await channels.Zip(operations).ParallelForEachAsync(async tuple =>
{
var (channel, operation) = tuple;
try
{
var guild = await GetDiscordClient().GetGuildAsync(channel.GuildId);
await GetExporter().ExportChatLogAsync(guild, channel,
OutputPath, ExportFormat, DateFormat, PartitionLimit,
After, Before, operation);
Interlocked.Increment(ref successfulExportCount);
}
catch (DiscordChatExporterException ex) when (!ex.IsCritical)
{
errors.Add(ex.Message);
}
finally
{
operation.Dispose();
}
}, ParallelLimit.ClampMin(1));
ticker.Report(1);
console.Output.WriteLine();
foreach (var error in errors)
console.Error.WriteLine(error);
console.Output.WriteLine($"Successfully exported {successfulExportCount} channel(s).");
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
using CliFx;
using CliFx.Attributes;
using DiscordChatExporter.Domain.Discord;
namespace DiscordChatExporter.Cli.Commands.Base
{
public abstract class TokenCommandBase : ICommand
{
[CommandOption("token", 't', IsRequired = true, EnvironmentVariableName = "DISCORD_TOKEN",
Description = "Authorization token.")]
public string TokenValue { get; set; } = "";
[CommandOption("bot", 'b', EnvironmentVariableName = "DISCORD_TOKEN_BOT",
Description = "Whether this authorization token belongs to a bot.")]
public bool IsBotToken { get; set; }
protected AuthToken GetAuthToken() => new AuthToken(IsBotToken ? AuthTokenType.Bot : AuthTokenType.User, TokenValue);
protected DiscordClient GetDiscordClient() => new DiscordClient(GetAuthToken());
public abstract ValueTask ExecuteAsync(IConsole console);
}
}