Compare commits

...

6 Commits

Author SHA1 Message Date
Alexey Golub 6fa61bf75a Update version 2019-03-10 14:45:39 +02:00
Alexey Golub 1976e3ad08 [CLI] Don't write progress if output is redirected
FIxes #155
2019-03-10 14:40:08 +02:00
Alexey Golub adbe544c57 Add some margin at the end of the chatlog in HTML 2019-03-07 22:15:21 +02:00
Alexey Golub 6f4d1c3d77 Refactor message grouping 2019-03-07 22:13:00 +02:00
Alexey Golub 33f71c1612 Remove message group limit setting 2019-03-07 21:14:11 +02:00
Alexey Golub 9b78472e05 Update message grouping algorithm to match Discord's
Fixes #152
2019-03-07 21:10:22 +02:00
15 changed files with 78 additions and 107 deletions
+6
View File
@@ -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