mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-07-07 22:54:38 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6fa61bf75a | |||
| 1976e3ad08 | |||
| adbe544c57 | |||
| 6f4d1c3d77 | |||
| 33f71c1612 | |||
| 9b78472e05 |
@@ -1,3 +1,9 @@
|
||||
### v2.10.2 (10-Mar-2019)
|
||||
|
||||
- [HTML] Updated message grouping algorithm to make it the same as in Discord. Removed "message group limit" setting and parameter.
|
||||
- [HTML] Added small margin at the end of the chatlog so it doesn't look like it was truncated.
|
||||
- [CLI] Fixed an issue where the app would crash if stdout was redirected. Progress will not be reported in such cases.
|
||||
|
||||
### v2.10.1 (06-Mar-2019)
|
||||
|
||||
- [HTML] Fixed an issue where multiple emojis on a single line would get rendered as one emoji.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
<Version>2.10.1</Version>
|
||||
<Version>2.10.2</Version>
|
||||
<Company>Tyrrrz</Company>
|
||||
<Copyright>Copyright (c) Alexey Golub</Copyright>
|
||||
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
||||
|
||||
@@ -9,19 +9,31 @@ namespace DiscordChatExporter.Cli.Internal
|
||||
|
||||
public InlineProgress()
|
||||
{
|
||||
_posX = Console.CursorLeft;
|
||||
_posY = Console.CursorTop;
|
||||
// If output is not redirected - save initial cursor position
|
||||
if (!Console.IsOutputRedirected)
|
||||
{
|
||||
_posX = Console.CursorLeft;
|
||||
_posY = Console.CursorTop;
|
||||
}
|
||||
}
|
||||
|
||||
public void Report(double progress)
|
||||
{
|
||||
Console.SetCursorPosition(_posX, _posY);
|
||||
Console.WriteLine($"{progress:P1}");
|
||||
// If output is not redirected - reset cursor position and write progress
|
||||
if (!Console.IsOutputRedirected)
|
||||
{
|
||||
Console.SetCursorPosition(_posX, _posY);
|
||||
Console.WriteLine($"{progress:P1}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Console.SetCursorPosition(_posX, _posY);
|
||||
// If output is not redirected - reset cursor position
|
||||
if (!Console.IsOutputRedirected)
|
||||
Console.SetCursorPosition(_posX, _posY);
|
||||
|
||||
// Inform about completion
|
||||
Console.WriteLine("Completed ✓");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ namespace DiscordChatExporter.Cli.Verbs
|
||||
// Configure settings
|
||||
if (Options.DateFormat.IsNotBlank())
|
||||
settingsService.DateFormat = Options.DateFormat;
|
||||
if (Options.MessageGroupLimit > 0)
|
||||
settingsService.MessageGroupLimit = Options.MessageGroupLimit;
|
||||
|
||||
// Track progress
|
||||
Console.Write($"Exporting channel [{Options.ChannelId}]... ");
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace DiscordChatExporter.Cli.Verbs
|
||||
// Configure settings
|
||||
if (Options.DateFormat.IsNotBlank())
|
||||
settingsService.DateFormat = Options.DateFormat;
|
||||
if (Options.MessageGroupLimit > 0)
|
||||
settingsService.MessageGroupLimit = Options.MessageGroupLimit;
|
||||
|
||||
// Get channels
|
||||
var channels = await dataService.GetDirectMessageChannelsAsync(Options.GetToken());
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace DiscordChatExporter.Cli.Verbs
|
||||
// Configure settings
|
||||
if (Options.DateFormat.IsNotBlank())
|
||||
settingsService.DateFormat = Options.DateFormat;
|
||||
if (Options.MessageGroupLimit > 0)
|
||||
settingsService.MessageGroupLimit = Options.MessageGroupLimit;
|
||||
|
||||
// Get channels
|
||||
var channels = await dataService.GetGuildChannelsAsync(Options.GetToken(), Options.GuildId);
|
||||
|
||||
@@ -23,8 +23,5 @@ namespace DiscordChatExporter.Cli.Verbs.Options
|
||||
|
||||
[Option("dateformat", Default = null, HelpText = "Date format used in output.")]
|
||||
public string DateFormat { get; set; }
|
||||
|
||||
[Option("grouplimit", Default = 0, HelpText = "Message group limit.")]
|
||||
public int MessageGroupLimit { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace DiscordChatExporter.Core.Internal
|
||||
@@ -17,5 +19,34 @@ namespace DiscordChatExporter.Core.Internal
|
||||
public static Color ResetAlpha(this Color color) => Color.FromArgb(1, color);
|
||||
|
||||
public static string HtmlEncode(this string value) => WebUtility.HtmlEncode(value);
|
||||
|
||||
public static IEnumerable<IReadOnlyList<T>> GroupAdjacentWhile<T>(this IEnumerable<T> source,
|
||||
Func<IReadOnlyList<T>, T, bool> groupPredicate)
|
||||
{
|
||||
// Create buffer
|
||||
var buffer = new List<T>();
|
||||
|
||||
// Enumerate source
|
||||
foreach (var element in source)
|
||||
{
|
||||
// If buffer is not empty and group predicate failed - yield and flush buffer
|
||||
if (buffer.Any() && !groupPredicate(buffer, element))
|
||||
{
|
||||
yield return buffer;
|
||||
buffer = new List<T>(); // new instance to reset reference
|
||||
}
|
||||
|
||||
// Add element to buffer
|
||||
buffer.Add(element);
|
||||
}
|
||||
|
||||
// If buffer still has something after the source has been enumerated - yield
|
||||
if (buffer.Any())
|
||||
yield return buffer;
|
||||
}
|
||||
|
||||
public static IEnumerable<IReadOnlyList<T>> GroupAdjacentWhile<T>(this IEnumerable<T> source,
|
||||
Func<IReadOnlyList<T>, bool> groupPredicate)
|
||||
=> source.GroupAdjacentWhile((buffer, _) => groupPredicate(buffer));
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,7 @@ img {
|
||||
|
||||
.chatlog {
|
||||
max-width: 100%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.chatlog__message-group {
|
||||
|
||||
@@ -18,62 +18,30 @@ namespace DiscordChatExporter.Core.Services
|
||||
private readonly ExportFormat _format;
|
||||
private readonly ChatLog _log;
|
||||
private readonly string _dateFormat;
|
||||
private readonly int _messageGroupLimit;
|
||||
|
||||
public TemplateModel(ExportFormat format, ChatLog log, string dateFormat, int messageGroupLimit)
|
||||
public TemplateModel(ExportFormat format, ChatLog log, string dateFormat)
|
||||
{
|
||||
_format = format;
|
||||
_log = log;
|
||||
_dateFormat = dateFormat;
|
||||
_messageGroupLimit = messageGroupLimit;
|
||||
}
|
||||
|
||||
private IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages)
|
||||
{
|
||||
// Group adjacent messages by timestamp and author
|
||||
var buffer = new List<Message>();
|
||||
foreach (var message in messages)
|
||||
=> messages.GroupAdjacentWhile((buffer, message) =>
|
||||
{
|
||||
// Get first message of the group
|
||||
var groupFirstMessage = buffer.FirstOrDefault();
|
||||
// Break group if the author changed
|
||||
if (buffer.Last().Author.Id != message.Author.Id)
|
||||
return false;
|
||||
|
||||
// Group break condition
|
||||
var breakCondition =
|
||||
groupFirstMessage != null &&
|
||||
(
|
||||
message.Author.Id != groupFirstMessage.Author.Id || // when author changes
|
||||
(message.Timestamp - groupFirstMessage.Timestamp).TotalHours > 1 || // when difference in timestamps is an hour or more
|
||||
message.Timestamp.Hour != groupFirstMessage.Timestamp.Hour || // when the timestamp's hour changes
|
||||
buffer.Count >= _messageGroupLimit // when group is full
|
||||
);
|
||||
// Break group if last message was more than 7 minutes ago
|
||||
if ((message.Timestamp - buffer.Last().Timestamp).TotalMinutes > 7)
|
||||
return false;
|
||||
|
||||
// If condition is true - flush buffer
|
||||
if (breakCondition)
|
||||
{
|
||||
var group = new MessageGroup(groupFirstMessage.Author, groupFirstMessage.Timestamp, buffer);
|
||||
return true;
|
||||
}).Select(g => new MessageGroup(g.First().Author, g.First().Timestamp, g));
|
||||
|
||||
// Reset the buffer instead of clearing to avoid mutations on existing references
|
||||
buffer = new List<Message>();
|
||||
|
||||
yield return group;
|
||||
}
|
||||
|
||||
// Add message to buffer
|
||||
buffer.Add(message);
|
||||
}
|
||||
|
||||
// Add what's remaining in buffer
|
||||
if (buffer.Any())
|
||||
{
|
||||
var groupFirstMessage = buffer.First();
|
||||
var group = new MessageGroup(groupFirstMessage.Author, groupFirstMessage.Timestamp, buffer);
|
||||
|
||||
yield return group;
|
||||
}
|
||||
}
|
||||
|
||||
private string Format(IFormattable obj, string format) =>
|
||||
obj.ToString(format, CultureInfo.InvariantCulture);
|
||||
private string Format(IFormattable obj, string format)
|
||||
=> obj.ToString(format, CultureInfo.InvariantCulture);
|
||||
|
||||
private string FormatDate(DateTime dateTime) => Format(dateTime, _dateFormat);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DiscordChatExporter.Core.Internal;
|
||||
using DiscordChatExporter.Core.Models;
|
||||
using Scriban;
|
||||
using Scriban.Runtime;
|
||||
@@ -36,8 +38,7 @@ namespace DiscordChatExporter.Core.Services
|
||||
};
|
||||
|
||||
// Create template model
|
||||
var templateModel = new TemplateModel(format, chatLog,
|
||||
_settingsService.DateFormat, _settingsService.MessageGroupLimit);
|
||||
var templateModel = new TemplateModel(format, chatLog, _settingsService.DateFormat);
|
||||
|
||||
context.PushGlobal(templateModel.GetScriptObject());
|
||||
|
||||
@@ -83,34 +84,6 @@ namespace DiscordChatExporter.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
private IReadOnlyList<ChatLog> SplitIntoPartitions(ChatLog chatLog, int partitionLimit)
|
||||
{
|
||||
var result = new List<ChatLog>();
|
||||
|
||||
// Loop through all messages with an increment of partition limit
|
||||
for (var i = 0; i < chatLog.Messages.Count; i += partitionLimit)
|
||||
{
|
||||
// Calculate how many messages left in total
|
||||
var remainingMessageCount = chatLog.Messages.Count - i;
|
||||
|
||||
// Decide how many messages are going into this partition
|
||||
// Each partition will have the same number of messages except the last one that might have fewer (all remaining messages)
|
||||
var partitionMessageCount = partitionLimit.ClampMax(remainingMessageCount);
|
||||
|
||||
// Get messages that belong to this partition
|
||||
var partitionMessages = new List<Message>();
|
||||
for (var j = i; j < i + partitionMessageCount; j++)
|
||||
partitionMessages.Add(chatLog.Messages[j]);
|
||||
|
||||
// Create a partition and add to list
|
||||
var partition = new ChatLog(chatLog.Guild, chatLog.Channel, chatLog.From, chatLog.To, partitionMessages,
|
||||
chatLog.Mentionables);
|
||||
result.Add(partition);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ExportChatLog(ChatLog chatLog, string filePath, ExportFormat format,
|
||||
int? partitionLimit = null)
|
||||
{
|
||||
@@ -122,7 +95,11 @@ namespace DiscordChatExporter.Core.Services
|
||||
// Otherwise split into partitions and export separately
|
||||
else
|
||||
{
|
||||
var partitions = SplitIntoPartitions(chatLog, partitionLimit.Value);
|
||||
// Create partitions by grouping up to X adjacent messages into separate chat logs
|
||||
var partitions = chatLog.Messages.GroupAdjacentWhile(g => g.Count < partitionLimit.Value)
|
||||
.Select(g => new ChatLog(chatLog.Guild, chatLog.Channel, chatLog.From, chatLog.To, g, chatLog.Mentionables))
|
||||
.ToArray();
|
||||
|
||||
ExportChatLogPartitioned(partitions, filePath, format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace DiscordChatExporter.Core.Services
|
||||
public bool IsAutoUpdateEnabled { get; set; } = true;
|
||||
|
||||
public string DateFormat { get; set; } = "dd-MMM-yy hh:mm tt";
|
||||
public int MessageGroupLimit { get; set; } = 20;
|
||||
|
||||
public AuthToken LastToken { get; set; }
|
||||
public ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark;
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
[assembly: AssemblyTitle("DiscordChatExporter")]
|
||||
[assembly: AssemblyCompany("Tyrrrz")]
|
||||
[assembly: AssemblyCopyright("Copyright (c) Alexey Golub")]
|
||||
[assembly: AssemblyVersion("2.10.1")]
|
||||
[assembly: AssemblyFileVersion("2.10.1")]
|
||||
[assembly: AssemblyVersion("2.10.2")]
|
||||
[assembly: AssemblyFileVersion("2.10.2")]
|
||||
@@ -1,6 +1,5 @@
|
||||
using DiscordChatExporter.Core.Services;
|
||||
using DiscordChatExporter.Gui.ViewModels.Framework;
|
||||
using Tyrrrz.Extensions;
|
||||
|
||||
namespace DiscordChatExporter.Gui.ViewModels.Dialogs
|
||||
{
|
||||
@@ -20,12 +19,6 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs
|
||||
set => _settingsService.DateFormat = value;
|
||||
}
|
||||
|
||||
public int MessageGroupLimit
|
||||
{
|
||||
get => _settingsService.MessageGroupLimit;
|
||||
set => _settingsService.MessageGroupLimit = value.ClampMin(0);
|
||||
}
|
||||
|
||||
public SettingsViewModel(SettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
|
||||
@@ -25,13 +25,6 @@
|
||||
materialDesign:HintAssist.IsFloating="True"
|
||||
Text="{Binding DateFormat}" />
|
||||
|
||||
<!-- Message group limit -->
|
||||
<TextBox
|
||||
Margin="16,8"
|
||||
materialDesign:HintAssist.Hint="Message group limit"
|
||||
materialDesign:HintAssist.IsFloating="True"
|
||||
Text="{Binding MessageGroupLimit}" />
|
||||
|
||||
<!-- Auto-updates -->
|
||||
<DockPanel LastChildFill="False">
|
||||
<TextBlock
|
||||
|
||||
Reference in New Issue
Block a user