mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-25 04:04:22 -04:00
Migrate DiscordChatExporter.Cli to CliFx
This commit is contained in:
parent
117d1b7e4a
commit
58ba99e0c6
29 changed files with 419 additions and 546 deletions
28
DiscordChatExporter.Cli/Commands/ExportChannelCommand.cs
Normal file
28
DiscordChatExporter.Cli/Commands/ExportChannelCommand.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("export", Description = "Export a channel.")]
|
||||||
|
public class ExportChannelCommand : ExportCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("channel", 'c', IsRequired = true, Description= "Channel ID.")]
|
||||||
|
public string ChannelId { get; set; }
|
||||||
|
|
||||||
|
public ExportChannelCommand(SettingsService settingsService, DataService dataService, ExportService exportService)
|
||||||
|
: base(settingsService, dataService, exportService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get channel
|
||||||
|
var channel = await DataService.GetChannelAsync(GetToken(), ChannelId);
|
||||||
|
|
||||||
|
// Export
|
||||||
|
await ExportChannelAsync(console, channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
DiscordChatExporter.Cli/Commands/ExportCommandBase.cs
Normal file
78
DiscordChatExporter.Cli/Commands/ExportCommandBase.cs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Cli.Internal;
|
||||||
|
using DiscordChatExporter.Core.Models;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
using DiscordChatExporter.Core.Services.Helpers;
|
||||||
|
using Tyrrrz.Extensions;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
public abstract class ExportCommandBase : TokenCommandBase
|
||||||
|
{
|
||||||
|
protected SettingsService SettingsService { get; }
|
||||||
|
|
||||||
|
protected ExportService ExportService { get; }
|
||||||
|
|
||||||
|
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
protected ExportCommandBase(SettingsService settingsService, DataService dataService, ExportService exportService)
|
||||||
|
: base(dataService)
|
||||||
|
{
|
||||||
|
SettingsService = settingsService;
|
||||||
|
ExportService = exportService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ExportChannelAsync(IConsole console, Channel channel)
|
||||||
|
{
|
||||||
|
// Configure settings
|
||||||
|
if (!DateFormat.IsNullOrWhiteSpace())
|
||||||
|
SettingsService.DateFormat = DateFormat;
|
||||||
|
|
||||||
|
console.Output.Write($"Exporting channel [{channel.Name}]... ");
|
||||||
|
using (var progress = new InlineProgress(console))
|
||||||
|
{
|
||||||
|
// Get chat log
|
||||||
|
var chatLog = await DataService.GetChatLogAsync(GetToken(), channel, After, Before, progress);
|
||||||
|
|
||||||
|
// Generate file path if not set or is a directory
|
||||||
|
var filePath = OutputPath;
|
||||||
|
if (filePath.IsNullOrWhiteSpace() || ExportHelper.IsDirectoryPath(filePath))
|
||||||
|
{
|
||||||
|
// Generate default file name
|
||||||
|
var fileName = ExportHelper.GetDefaultExportFileName(ExportFormat, chatLog.Guild,
|
||||||
|
chatLog.Channel, After, Before);
|
||||||
|
|
||||||
|
// Combine paths
|
||||||
|
filePath = Path.Combine(filePath ?? "", fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export
|
||||||
|
await ExportService.ExportChatLogAsync(chatLog, filePath, ExportFormat, PartitionLimit);
|
||||||
|
|
||||||
|
// Report successful completion
|
||||||
|
progress.ReportCompletion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
using DiscordChatExporter.Core.Services.Exceptions;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("exportdm", Description = "Export all direct message channels.")]
|
||||||
|
public class ExportDirectMessagesCommand : ExportCommandBase
|
||||||
|
{
|
||||||
|
public ExportDirectMessagesCommand(SettingsService settingsService, DataService dataService, ExportService exportService)
|
||||||
|
: base(settingsService, dataService, exportService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get channels
|
||||||
|
var channels = await DataService.GetDirectMessageChannelsAsync(GetToken());
|
||||||
|
|
||||||
|
// Order channels
|
||||||
|
channels = channels.OrderBy(c => c.Name).ToArray();
|
||||||
|
|
||||||
|
// Loop through channels
|
||||||
|
foreach (var channel in channels)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExportChannelAsync(console, channel);
|
||||||
|
}
|
||||||
|
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
console.Error.WriteLine("You don't have access to this channel.");
|
||||||
|
}
|
||||||
|
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
console.Error.WriteLine("This channel doesn't exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs
Normal file
49
DiscordChatExporter.Cli/Commands/ExportGuildCommand.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Models;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
using DiscordChatExporter.Core.Services.Exceptions;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("exportguild", Description = "Export all channels within specified guild.")]
|
||||||
|
public class ExportGuildCommand : ExportCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("guild", 'g', IsRequired = true, Description = "Guild ID.")]
|
||||||
|
public string GuildId { get; set; }
|
||||||
|
|
||||||
|
public ExportGuildCommand(SettingsService settingsService, DataService dataService, ExportService exportService)
|
||||||
|
: base(settingsService, dataService, exportService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get channels
|
||||||
|
var channels = await DataService.GetGuildChannelsAsync(GetToken(), GuildId);
|
||||||
|
|
||||||
|
// Filter and order channels
|
||||||
|
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat).OrderBy(c => c.Name).ToArray();
|
||||||
|
|
||||||
|
// Loop through channels
|
||||||
|
foreach (var channel in channels)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExportChannelAsync(console, channel);
|
||||||
|
}
|
||||||
|
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
console.Error.WriteLine("You don't have access to this channel.");
|
||||||
|
}
|
||||||
|
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
console.Error.WriteLine("This channel doesn't exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs
Normal file
34
DiscordChatExporter.Cli/Commands/GetChannelsCommand.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Models;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("channels", Description = "Get the list of channels in the given guild.")]
|
||||||
|
public class GetChannelsCommand : TokenCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("guild", 'g', IsRequired = true, Description = "Guild ID.")]
|
||||||
|
public string GuildId { get; set; }
|
||||||
|
|
||||||
|
public GetChannelsCommand(DataService dataService)
|
||||||
|
: base(dataService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get channels
|
||||||
|
var channels = await DataService.GetGuildChannelsAsync(GetToken(), GuildId);
|
||||||
|
|
||||||
|
// Filter and order channels
|
||||||
|
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat).OrderBy(c => c.Name).ToArray();
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
foreach (var channel in channels)
|
||||||
|
console.Output.WriteLine($"{channel.Id} | {channel.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("dm", Description = "Get the list of direct message channels.")]
|
||||||
|
public class GetDirectMessageChannelsCommand : TokenCommandBase
|
||||||
|
{
|
||||||
|
public GetDirectMessageChannelsCommand(DataService dataService)
|
||||||
|
: base(dataService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get channels
|
||||||
|
var channels = await DataService.GetDirectMessageChannelsAsync(GetToken());
|
||||||
|
|
||||||
|
// Order channels
|
||||||
|
channels = channels.OrderBy(c => c.Name).ToArray();
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
foreach (var channel in channels)
|
||||||
|
console.Output.WriteLine($"{channel.Id} | {channel.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs
Normal file
30
DiscordChatExporter.Cli/Commands/GetGuildsCommand.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("guilds", Description = "Get the list of accessible guilds.")]
|
||||||
|
public class GetGuildsCommand : TokenCommandBase
|
||||||
|
{
|
||||||
|
public GetGuildsCommand(DataService dataService)
|
||||||
|
: base(dataService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
// Get guilds
|
||||||
|
var guilds = await DataService.GetUserGuildsAsync(GetToken());
|
||||||
|
|
||||||
|
// Order guilds
|
||||||
|
guilds = guilds.OrderBy(g => g.Name).ToArray();
|
||||||
|
|
||||||
|
// Print result
|
||||||
|
foreach (var guild in guilds)
|
||||||
|
console.Output.WriteLine($"{guild.Id} | {guild.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
DiscordChatExporter.Cli/Commands/GuideCommand.cs
Normal file
53
DiscordChatExporter.Cli/Commands/GuideCommand.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
[Command("guide", Description = "Explains how to obtain token, guild or channel ID.")]
|
||||||
|
public class GuideCommand : ICommand
|
||||||
|
{
|
||||||
|
public Task ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
console.WithForegroundColor(ConsoleColor.White, () => console.Output.WriteLine("To get user token:"));
|
||||||
|
console.Output.WriteLine(" 1. Open Discord");
|
||||||
|
console.Output.WriteLine(" 2. Press Ctrl+Shift+I to show developer tools");
|
||||||
|
console.Output.WriteLine(" 3. Navigate to the Application tab");
|
||||||
|
console.Output.WriteLine(" 4. Select \"Local Storage\" > \"https://discordapp.com\" on the left");
|
||||||
|
console.Output.WriteLine(" 5. Press Ctrl+R to reload");
|
||||||
|
console.Output.WriteLine(" 6. Find \"token\" at the bottom and copy the value");
|
||||||
|
console.Output.WriteLine();
|
||||||
|
|
||||||
|
console.WithForegroundColor(ConsoleColor.White, () => console.Output.WriteLine("To get bot token:"));
|
||||||
|
console.Output.WriteLine(" 1. Go to Discord developer portal");
|
||||||
|
console.Output.WriteLine(" 2. Open your application's settings");
|
||||||
|
console.Output.WriteLine(" 3. Navigate to the Bot section on the left");
|
||||||
|
console.Output.WriteLine(" 4. Under Token click Copy");
|
||||||
|
console.Output.WriteLine();
|
||||||
|
|
||||||
|
console.WithForegroundColor(ConsoleColor.White, () => console.Output.WriteLine("To get guild ID or guild channel ID:"));
|
||||||
|
console.Output.WriteLine(" 1. Open Discord");
|
||||||
|
console.Output.WriteLine(" 2. Open Settings");
|
||||||
|
console.Output.WriteLine(" 3. Go to Appearance section");
|
||||||
|
console.Output.WriteLine(" 4. Enable Developer Mode");
|
||||||
|
console.Output.WriteLine(" 5. Right click on the desired guild or channel and click Copy ID");
|
||||||
|
console.Output.WriteLine();
|
||||||
|
|
||||||
|
console.WithForegroundColor(ConsoleColor.White, () => console.Output.WriteLine("To get direct message channel ID:"));
|
||||||
|
console.Output.WriteLine(" 1. Open Discord");
|
||||||
|
console.Output.WriteLine(" 2. Open the desired direct message channel");
|
||||||
|
console.Output.WriteLine(" 3. Press Ctrl+Shift+I to show developer tools");
|
||||||
|
console.Output.WriteLine(" 4. Navigate to the Console tab");
|
||||||
|
console.Output.WriteLine(" 5. Type \"window.location.href\" and press Enter");
|
||||||
|
console.Output.WriteLine(" 6. Copy the first long sequence of numbers inside the URL");
|
||||||
|
console.Output.WriteLine();
|
||||||
|
|
||||||
|
console.Output.WriteLine("If you still have unanswered questions, check out the wiki:");
|
||||||
|
console.Output.WriteLine("https://github.com/Tyrrrz/DiscordChatExporter/wiki");
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
DiscordChatExporter.Cli/Commands/TokenCommandBase.cs
Normal file
29
DiscordChatExporter.Cli/Commands/TokenCommandBase.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Services;
|
||||||
|
using DiscordChatExporter.Core.Models;
|
||||||
|
using DiscordChatExporter.Core.Services;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Cli.Commands
|
||||||
|
{
|
||||||
|
public abstract class TokenCommandBase : ICommand
|
||||||
|
{
|
||||||
|
protected DataService DataService { get; }
|
||||||
|
|
||||||
|
[CommandOption("token", 't', IsRequired = true, Description = "Authorization token.")]
|
||||||
|
public string TokenValue { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("bot", 'b', Description = "Whether this authorization token belongs to a bot.")]
|
||||||
|
public bool IsBotToken { get; set; }
|
||||||
|
|
||||||
|
protected TokenCommandBase(DataService dataService)
|
||||||
|
{
|
||||||
|
DataService = dataService;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AuthToken GetToken() => new AuthToken(IsBotToken ? AuthTokenType.Bot : AuthTokenType.User, TokenValue);
|
||||||
|
|
||||||
|
public abstract Task ExecuteAsync(IConsole console);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
using StyletIoC;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli
|
|
||||||
{
|
|
||||||
public static class Container
|
|
||||||
{
|
|
||||||
public static IContainer Instance { get; }
|
|
||||||
|
|
||||||
static Container()
|
|
||||||
{
|
|
||||||
var builder = new StyletIoCBuilder();
|
|
||||||
|
|
||||||
// Autobind the .Services assembly
|
|
||||||
builder.Autobind(typeof(DataService).Assembly);
|
|
||||||
|
|
||||||
// Bind settings as singleton
|
|
||||||
builder.Bind<SettingsService>().ToSelf().InSingletonScope();
|
|
||||||
|
|
||||||
// Set instance
|
|
||||||
Instance = builder.BuildContainer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.3.0" />
|
<PackageReference Include="CliFx" Version="0.0.3" />
|
||||||
<PackageReference Include="Stylet" Version="1.2.0" />
|
<PackageReference Include="Stylet" Version="1.2.0" />
|
||||||
<PackageReference Include="Tyrrrz.Extensions" Version="1.6.2" />
|
<PackageReference Include="Tyrrrz.Extensions" Version="1.6.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
using System;
|
using System;
|
||||||
|
using CliFx.Services;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Internal
|
namespace DiscordChatExporter.Cli.Internal
|
||||||
{
|
{
|
||||||
internal class InlineProgress : IProgress<double>, IDisposable
|
internal class InlineProgress : IProgress<double>, IDisposable
|
||||||
{
|
{
|
||||||
private readonly int _posX;
|
private readonly IConsole _console;
|
||||||
private readonly int _posY;
|
|
||||||
|
|
||||||
|
private string _lastOutput = "";
|
||||||
private bool _isCompleted;
|
private bool _isCompleted;
|
||||||
|
|
||||||
public InlineProgress()
|
public InlineProgress(IConsole console)
|
||||||
{
|
{
|
||||||
// If output is not redirected - save initial cursor position
|
_console = console;
|
||||||
if (!Console.IsOutputRedirected)
|
}
|
||||||
{
|
|
||||||
_posX = Console.CursorLeft;
|
private void ResetCursorPosition()
|
||||||
_posY = Console.CursorTop;
|
{
|
||||||
}
|
foreach (var c in _lastOutput)
|
||||||
|
_console.Output.Write('\b');
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Report(double progress)
|
public void Report(double progress)
|
||||||
{
|
{
|
||||||
// If output is not redirected - reset cursor position and write progress
|
// If output is not redirected - reset cursor position and write progress
|
||||||
if (!Console.IsOutputRedirected)
|
if (!_console.IsOutputRedirected)
|
||||||
{
|
{
|
||||||
Console.SetCursorPosition(_posX, _posY);
|
ResetCursorPosition();
|
||||||
Console.WriteLine($"{progress:P1}");
|
_console.Output.Write(_lastOutput = $"{progress:P1}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +36,13 @@ namespace DiscordChatExporter.Cli.Internal
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// If output is not redirected - reset cursor position
|
// If output is not redirected - reset cursor position
|
||||||
if (!Console.IsOutputRedirected)
|
if (!_console.IsOutputRedirected)
|
||||||
Console.SetCursorPosition(_posX, _posY);
|
{
|
||||||
|
ResetCursorPosition();
|
||||||
|
}
|
||||||
|
|
||||||
// Inform about completion
|
// Inform about completion
|
||||||
if (_isCompleted)
|
_console.Output.WriteLine(_isCompleted ? "Completed ✓" : "Failed X");
|
||||||
Console.WriteLine("Completed ✓");
|
|
||||||
else
|
|
||||||
Console.WriteLine("Failed X");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,80 +1,35 @@
|
||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using CliFx;
|
||||||
using CommandLine;
|
using DiscordChatExporter.Core.Services;
|
||||||
using DiscordChatExporter.Cli.Verbs;
|
using StyletIoC;
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli
|
namespace DiscordChatExporter.Cli
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private static void PrintTokenHelp()
|
private static IContainer BuildContainer()
|
||||||
{
|
{
|
||||||
Console.WriteLine("# To get user token:");
|
var builder = new StyletIoCBuilder();
|
||||||
Console.WriteLine(" 1. Open Discord");
|
|
||||||
Console.WriteLine(" 2. Press Ctrl+Shift+I to show developer tools");
|
// Autobind the .Services assembly
|
||||||
Console.WriteLine(" 3. Navigate to the Application tab");
|
builder.Autobind(typeof(DataService).Assembly);
|
||||||
Console.WriteLine(" 4. Select \"Local Storage\" > \"https://discordapp.com\" on the left");
|
|
||||||
Console.WriteLine(" 5. Press Ctrl+R to reload");
|
// Bind settings as singleton
|
||||||
Console.WriteLine(" 6. Find \"token\" at the bottom and copy the value");
|
builder.Bind<SettingsService>().ToSelf().InSingletonScope();
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("# To get bot token:");
|
// Set instance
|
||||||
Console.WriteLine(" 1. Go to Discord developer portal");
|
return builder.BuildContainer();
|
||||||
Console.WriteLine(" 2. Open your application's settings");
|
|
||||||
Console.WriteLine(" 3. Navigate to the Bot section on the left");
|
|
||||||
Console.WriteLine(" 4. Under Token click Copy");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("# To get guild ID or guild channel ID:");
|
|
||||||
Console.WriteLine(" 1. Open Discord");
|
|
||||||
Console.WriteLine(" 2. Open Settings");
|
|
||||||
Console.WriteLine(" 3. Go to Appearance section");
|
|
||||||
Console.WriteLine(" 4. Enable Developer Mode");
|
|
||||||
Console.WriteLine(" 5. Right click on the desired guild or channel and click Copy ID");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("# To get direct message channel ID:");
|
|
||||||
Console.WriteLine(" 1. Open Discord");
|
|
||||||
Console.WriteLine(" 2. Open the desired direct message channel");
|
|
||||||
Console.WriteLine(" 3. Press Ctrl+Shift+I to show developer tools");
|
|
||||||
Console.WriteLine(" 4. Navigate to the Console tab");
|
|
||||||
Console.WriteLine(" 5. Type \"window.location.href\" and press Enter");
|
|
||||||
Console.WriteLine(" 6. Copy the first long sequence of numbers inside the URL");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
// Get all verb types
|
var container = BuildContainer();
|
||||||
var verbTypes = new[]
|
|
||||||
{
|
|
||||||
typeof(ExportChannelOptions),
|
|
||||||
typeof(ExportDirectMessagesOptions),
|
|
||||||
typeof(ExportGuildOptions),
|
|
||||||
typeof(GetChannelsOptions),
|
|
||||||
typeof(GetDirectMessageChannelsOptions),
|
|
||||||
typeof(GetGuildsOptions)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse command line arguments
|
return new CliApplicationBuilder()
|
||||||
var parsedArgs = Parser.Default.ParseArguments(args, verbTypes);
|
.AddCommandsFromThisAssembly()
|
||||||
|
.UseCommandFactory(schema => (ICommand) container.Get(schema.Type))
|
||||||
// Execute commands
|
.Build()
|
||||||
parsedArgs.WithParsed<ExportChannelOptions>(o => new ExportChannelVerb(o).Execute());
|
.RunAsync(args);
|
||||||
parsedArgs.WithParsed<ExportDirectMessagesOptions>(o => new ExportDirectMessagesVerb(o).Execute());
|
|
||||||
parsedArgs.WithParsed<ExportGuildOptions>(o => new ExportGuildVerb(o).Execute());
|
|
||||||
parsedArgs.WithParsed<GetChannelsOptions>(o => new GetChannelsVerb(o).Execute());
|
|
||||||
parsedArgs.WithParsed<GetDirectMessageChannelsOptions>(o => new GetDirectMessageChannelsVerb(o).Execute());
|
|
||||||
parsedArgs.WithParsed<GetGuildsOptions>(o => new GetGuildsVerb(o).Execute());
|
|
||||||
|
|
||||||
// Show token help if help requested or no verb specified
|
|
||||||
parsedArgs.WithNotParsed(errs =>
|
|
||||||
{
|
|
||||||
var err = errs.First();
|
|
||||||
|
|
||||||
if (err.Tag == ErrorType.NoVerbSelectedError)
|
|
||||||
PrintTokenHelp();
|
|
||||||
|
|
||||||
if (err.Tag == ErrorType.HelpVerbRequestedError && args.Length == 1)
|
|
||||||
PrintTokenHelp();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Internal;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
using DiscordChatExporter.Core.Services.Helpers;
|
|
||||||
using Tyrrrz.Extensions;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class ExportChannelVerb : Verb<ExportChannelOptions>
|
|
||||||
{
|
|
||||||
public ExportChannelVerb(ExportChannelOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get services
|
|
||||||
var settingsService = Container.Instance.Get<SettingsService>();
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
var exportService = Container.Instance.Get<ExportService>();
|
|
||||||
|
|
||||||
// Configure settings
|
|
||||||
if (!Options.DateFormat.IsNullOrWhiteSpace())
|
|
||||||
settingsService.DateFormat = Options.DateFormat;
|
|
||||||
|
|
||||||
// Track progress
|
|
||||||
Console.Write($"Exporting channel [{Options.ChannelId}]... ");
|
|
||||||
using (var progress = new InlineProgress())
|
|
||||||
{
|
|
||||||
// Get chat log
|
|
||||||
var chatLog = await dataService.GetChatLogAsync(Options.GetToken(), Options.ChannelId,
|
|
||||||
Options.After, Options.Before, progress);
|
|
||||||
|
|
||||||
// Generate file path if not set or is a directory
|
|
||||||
var filePath = Options.OutputPath;
|
|
||||||
if (filePath.IsNullOrWhiteSpace() || ExportHelper.IsDirectoryPath(filePath))
|
|
||||||
{
|
|
||||||
// Generate default file name
|
|
||||||
var fileName = ExportHelper.GetDefaultExportFileName(Options.ExportFormat, chatLog.Guild,
|
|
||||||
chatLog.Channel, Options.After, Options.Before);
|
|
||||||
|
|
||||||
// Combine paths
|
|
||||||
filePath = Path.Combine(filePath ?? "", fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export
|
|
||||||
await exportService.ExportChatLogAsync(chatLog, filePath, Options.ExportFormat, Options.PartitionLimit);
|
|
||||||
|
|
||||||
// Report successful completion
|
|
||||||
progress.ReportCompletion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Internal;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
using DiscordChatExporter.Core.Services.Exceptions;
|
|
||||||
using DiscordChatExporter.Core.Services.Helpers;
|
|
||||||
using Tyrrrz.Extensions;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class ExportDirectMessagesVerb : Verb<ExportDirectMessagesOptions>
|
|
||||||
{
|
|
||||||
public ExportDirectMessagesVerb(ExportDirectMessagesOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get services
|
|
||||||
var settingsService = Container.Instance.Get<SettingsService>();
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
var exportService = Container.Instance.Get<ExportService>();
|
|
||||||
|
|
||||||
// Configure settings
|
|
||||||
if (!Options.DateFormat.IsNullOrWhiteSpace())
|
|
||||||
settingsService.DateFormat = Options.DateFormat;
|
|
||||||
|
|
||||||
// Get channels
|
|
||||||
var channels = await dataService.GetDirectMessageChannelsAsync(Options.GetToken());
|
|
||||||
|
|
||||||
// Order channels
|
|
||||||
channels = channels.OrderBy(c => c.Name).ToArray();
|
|
||||||
|
|
||||||
// Loop through channels
|
|
||||||
foreach (var channel in channels)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Track progress
|
|
||||||
Console.Write($"Exporting channel [{channel.Name}]... ");
|
|
||||||
using (var progress = new InlineProgress())
|
|
||||||
{
|
|
||||||
// Get chat log
|
|
||||||
var chatLog = await dataService.GetChatLogAsync(Options.GetToken(), channel,
|
|
||||||
Options.After, Options.Before, progress);
|
|
||||||
|
|
||||||
// Generate default file name
|
|
||||||
var fileName = ExportHelper.GetDefaultExportFileName(Options.ExportFormat, chatLog.Guild,
|
|
||||||
chatLog.Channel, Options.After, Options.Before);
|
|
||||||
|
|
||||||
// Generate file path
|
|
||||||
var filePath = Path.Combine(Options.OutputPath ?? "", fileName);
|
|
||||||
|
|
||||||
// Export
|
|
||||||
await exportService.ExportChatLogAsync(chatLog, filePath, Options.ExportFormat,
|
|
||||||
Options.PartitionLimit);
|
|
||||||
|
|
||||||
// Report successful completion
|
|
||||||
progress.ReportCompletion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("You don't have access to this channel");
|
|
||||||
}
|
|
||||||
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("This channel doesn't exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Internal;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Models;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
using DiscordChatExporter.Core.Services.Exceptions;
|
|
||||||
using DiscordChatExporter.Core.Services.Helpers;
|
|
||||||
using Tyrrrz.Extensions;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class ExportGuildVerb : Verb<ExportGuildOptions>
|
|
||||||
{
|
|
||||||
public ExportGuildVerb(ExportGuildOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get services
|
|
||||||
var settingsService = Container.Instance.Get<SettingsService>();
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
var exportService = Container.Instance.Get<ExportService>();
|
|
||||||
|
|
||||||
// Configure settings
|
|
||||||
if (!Options.DateFormat.IsNullOrWhiteSpace())
|
|
||||||
settingsService.DateFormat = Options.DateFormat;
|
|
||||||
|
|
||||||
// Get channels
|
|
||||||
var channels = await dataService.GetGuildChannelsAsync(Options.GetToken(), Options.GuildId);
|
|
||||||
|
|
||||||
// Filter and order channels
|
|
||||||
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat).OrderBy(c => c.Name).ToArray();
|
|
||||||
|
|
||||||
// Loop through channels
|
|
||||||
foreach (var channel in channels)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Track progress
|
|
||||||
Console.Write($"Exporting channel [{channel.Name}]... ");
|
|
||||||
using (var progress = new InlineProgress())
|
|
||||||
{
|
|
||||||
// Get chat log
|
|
||||||
var chatLog = await dataService.GetChatLogAsync(Options.GetToken(), channel,
|
|
||||||
Options.After, Options.Before, progress);
|
|
||||||
|
|
||||||
// Generate default file name
|
|
||||||
var fileName = ExportHelper.GetDefaultExportFileName(Options.ExportFormat, chatLog.Guild,
|
|
||||||
chatLog.Channel, Options.After, Options.Before);
|
|
||||||
|
|
||||||
// Generate file path
|
|
||||||
var filePath = Path.Combine(Options.OutputPath ?? "", fileName);
|
|
||||||
|
|
||||||
// Export
|
|
||||||
await exportService.ExportChatLogAsync(chatLog, filePath, Options.ExportFormat,
|
|
||||||
Options.PartitionLimit);
|
|
||||||
|
|
||||||
// Report successful completion
|
|
||||||
progress.ReportCompletion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("You don't have access to this channel");
|
|
||||||
}
|
|
||||||
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine("This channel doesn't exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Models;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class GetChannelsVerb : Verb<GetChannelsOptions>
|
|
||||||
{
|
|
||||||
public GetChannelsVerb(GetChannelsOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get data service
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
|
|
||||||
// Get channels
|
|
||||||
var channels = await dataService.GetGuildChannelsAsync(Options.GetToken(), Options.GuildId);
|
|
||||||
|
|
||||||
// Filter and order channels
|
|
||||||
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat).OrderBy(c => c.Name).ToArray();
|
|
||||||
|
|
||||||
// Print result
|
|
||||||
foreach (var channel in channels)
|
|
||||||
Console.WriteLine($"{channel.Id} | {channel.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class GetDirectMessageChannelsVerb : Verb<GetDirectMessageChannelsOptions>
|
|
||||||
{
|
|
||||||
public GetDirectMessageChannelsVerb(GetDirectMessageChannelsOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get data service
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
|
|
||||||
// Get channels
|
|
||||||
var channels = await dataService.GetDirectMessageChannelsAsync(Options.GetToken());
|
|
||||||
|
|
||||||
// Order channels
|
|
||||||
channels = channels.OrderBy(c => c.Name).ToArray();
|
|
||||||
|
|
||||||
// Print result
|
|
||||||
foreach (var channel in channels)
|
|
||||||
Console.WriteLine($"{channel.Id} | {channel.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordChatExporter.Cli.Verbs.Options;
|
|
||||||
using DiscordChatExporter.Core.Services;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public class GetGuildsVerb : Verb<GetGuildsOptions>
|
|
||||||
{
|
|
||||||
public GetGuildsVerb(GetGuildsOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
// Get data service
|
|
||||||
var dataService = Container.Instance.Get<DataService>();
|
|
||||||
|
|
||||||
// Get guilds
|
|
||||||
var guilds = await dataService.GetUserGuildsAsync(Options.GetToken());
|
|
||||||
|
|
||||||
// Order guilds
|
|
||||||
guilds = guilds.OrderBy(g => g.Name).ToArray();
|
|
||||||
|
|
||||||
// Print result
|
|
||||||
foreach (var guild in guilds)
|
|
||||||
Console.WriteLine($"{guild.Id} | {guild.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("export", HelpText = "Export channel.")]
|
|
||||||
public class ExportChannelOptions : ExportOptions
|
|
||||||
{
|
|
||||||
[Option('c', "channel", Required = true, HelpText = "Channel ID.")]
|
|
||||||
public string ChannelId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("exportdm", HelpText = "Export all direct message channels.")]
|
|
||||||
public class ExportDirectMessagesOptions : ExportOptions
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("exportguild", HelpText = "Export all channels within a given guild.")]
|
|
||||||
public class ExportGuildOptions : ExportOptions
|
|
||||||
{
|
|
||||||
[Option('g', "guild", Required = true, HelpText = "Guild ID.")]
|
|
||||||
public string GuildId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
using System;
|
|
||||||
using CommandLine;
|
|
||||||
using DiscordChatExporter.Core.Models;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
public abstract class ExportOptions : TokenOptions
|
|
||||||
{
|
|
||||||
[Option('f', "format", Default = ExportFormat.HtmlDark, HelpText = "Output file format.")]
|
|
||||||
public ExportFormat ExportFormat { get; set; }
|
|
||||||
|
|
||||||
[Option('o', "output", Default = null, HelpText = "Output file or directory path.")]
|
|
||||||
public string OutputPath { get; set; }
|
|
||||||
|
|
||||||
// HACK: CommandLineParser doesn't support DateTimeOffset
|
|
||||||
[Option("after", Default = null, HelpText = "Limit to messages sent after this date.")]
|
|
||||||
public DateTime? After { get; set; }
|
|
||||||
|
|
||||||
// HACK: CommandLineParser doesn't support DateTimeOffset
|
|
||||||
[Option("before", Default = null, HelpText = "Limit to messages sent before this date.")]
|
|
||||||
public DateTime? Before { get; set; }
|
|
||||||
|
|
||||||
[Option('p', "partition", Default = null, HelpText = "Split output into partitions limited to this number of messages.")]
|
|
||||||
public int? PartitionLimit { get; set; }
|
|
||||||
|
|
||||||
[Option("dateformat", Default = null, HelpText = "Date format used in output.")]
|
|
||||||
public string DateFormat { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("channels", HelpText = "Get the list of channels in the given guild.")]
|
|
||||||
public class GetChannelsOptions : TokenOptions
|
|
||||||
{
|
|
||||||
[Option('g', "guild", Required = true, HelpText = "Guild ID.")]
|
|
||||||
public string GuildId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("dm", HelpText = "Get the list of direct message channels.")]
|
|
||||||
public class GetDirectMessageChannelsOptions : TokenOptions
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
[Verb("guilds", HelpText = "Get the list of accessible guilds.")]
|
|
||||||
public class GetGuildsOptions : TokenOptions
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
using CommandLine;
|
|
||||||
using DiscordChatExporter.Core.Models;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs.Options
|
|
||||||
{
|
|
||||||
public abstract class TokenOptions
|
|
||||||
{
|
|
||||||
[Option('t', "token", Required = true, HelpText = "Authorization token.")]
|
|
||||||
public string TokenValue { get; set; }
|
|
||||||
|
|
||||||
[Option('b', "bot", Default = false, HelpText = "Whether this authorization token belongs to a bot.")]
|
|
||||||
public bool IsBotToken { get; set; }
|
|
||||||
|
|
||||||
public AuthToken GetToken() => new AuthToken(IsBotToken ? AuthTokenType.Bot : AuthTokenType.User, TokenValue);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Cli.Verbs
|
|
||||||
{
|
|
||||||
public abstract class Verb<TOptions>
|
|
||||||
{
|
|
||||||
protected TOptions Options { get; }
|
|
||||||
|
|
||||||
protected Verb(TOptions options)
|
|
||||||
{
|
|
||||||
Options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Task ExecuteAsync();
|
|
||||||
|
|
||||||
public virtual void Execute() => ExecuteAsync().GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -34,8 +34,8 @@ DiscordChatExporter can be used to export message history from a [Discord](https
|
||||||
- [MaterialDesignInXamlToolkit](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit)
|
- [MaterialDesignInXamlToolkit](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit)
|
||||||
- [Newtonsoft.Json](http://www.newtonsoft.com/json)
|
- [Newtonsoft.Json](http://www.newtonsoft.com/json)
|
||||||
- [Scriban](https://github.com/lunet-io/scriban)
|
- [Scriban](https://github.com/lunet-io/scriban)
|
||||||
- [CommandLineParser](https://github.com/commandlineparser/commandline)
|
|
||||||
- [Ookii.Dialogs](https://github.com/caioproiete/ookii-dialogs-wpf)
|
- [Ookii.Dialogs](https://github.com/caioproiete/ookii-dialogs-wpf)
|
||||||
|
- [CliFx](https://github.com/Tyrrrz/CliFx)
|
||||||
- [Failsafe](https://github.com/Tyrrrz/Failsafe)
|
- [Failsafe](https://github.com/Tyrrrz/Failsafe)
|
||||||
- [Gress](https://github.com/Tyrrrz/Gress)
|
- [Gress](https://github.com/Tyrrrz/Gress)
|
||||||
- [Onova](https://github.com/Tyrrrz/Onova)
|
- [Onova](https://github.com/Tyrrrz/Onova)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue