Refactor message grouping

This commit is contained in:
Alexey Golub 2019-03-07 22:13:00 +02:00
parent 33f71c1612
commit 6f4d1c3d77
3 changed files with 49 additions and 64 deletions

View file

@ -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));
}
}

View file

@ -27,45 +27,21 @@ namespace DiscordChatExporter.Core.Services
}
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) =>
{
// Group break condition
var breakCondition =
buffer.Any() &&
(
message.Author.Id != buffer.First().Author.Id || // when author changes
(message.Timestamp - buffer.Last().Timestamp).TotalMinutes > 7 // when more than 7 minutes passed since last message
);
// Break group if the author changed
if (buffer.Last().Author.Id != message.Author.Id)
return false;
// If condition is true - flush buffer
if (breakCondition)
{
var group = new MessageGroup(buffer.First().Author, buffer.First().Timestamp, buffer);
// Break group if last message was more than 7 minutes ago
if ((message.Timestamp - buffer.Last().Timestamp).TotalMinutes > 7)
return false;
// Reset the buffer instead of clearing to avoid mutations on existing references
buffer = new List<Message>();
return true;
}).Select(g => new MessageGroup(g.First().Author, g.First().Timestamp, g));
yield return group;
}
// Add message to buffer
buffer.Add(message);
}
// Add what's remaining in buffer
if (buffer.Any())
{
var group = new MessageGroup(buffer.First().Author, buffer.First().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);

View file

@ -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;
@ -82,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)
{
@ -121,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);
}
}