Refactor message grouping from data layer to render layer

This commit is contained in:
Oleksii Holub
2018-11-01 18:16:23 +02:00
parent 23116b776b
commit 47f0561c71
18 changed files with 124 additions and 189 deletions

View File

@@ -1,51 +0,0 @@
using System;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Models;
namespace DiscordChatExporter.Core.Services
{
public class ChatLogService : IChatLogService
{
private readonly IDataService _dataService;
private readonly IMessageGroupService _messageGroupService;
public ChatLogService(IDataService dataService, IMessageGroupService messageGroupService)
{
_dataService = dataService;
_messageGroupService = messageGroupService;
}
public async Task<ChatLog> GetChatLogAsync(AuthToken token, Guild guild, Channel channel,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null)
{
// Get messages
var messages = await _dataService.GetChannelMessagesAsync(token, channel.Id, from, to, progress);
// Group messages
var messageGroups = _messageGroupService.GroupMessages(messages);
// Get total message count
var totalMessageCount = messages.Count;
// Get mentionables
var mentionables = await _dataService.GetMentionablesAsync(token, guild.Id, messages);
return new ChatLog(guild, channel, from, to, messageGroups, totalMessageCount, mentionables);
}
public async Task<ChatLog> GetChatLogAsync(AuthToken token, string channelId,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null)
{
// Get channel
var channel = await _dataService.GetChannelAsync(token, channelId);
// Get guild
var guild = channel.GuildId == Guild.DirectMessages.Id
? Guild.DirectMessages
: await _dataService.GetGuildAsync(token, channel.GuildId);
// Get the chat log
return await GetChatLogAsync(token, guild, channel, from, to, progress);
}
}
}

View File

@@ -214,6 +214,33 @@ namespace DiscordChatExporter.Core.Services
return new Mentionables(users, channels, roles);
}
public async Task<ChatLog> GetChatLogAsync(AuthToken token, Guild guild, Channel channel,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null)
{
// Get messages
var messages = await GetChannelMessagesAsync(token, channel.Id, from, to, progress);
// Get mentionables
var mentionables = await GetMentionablesAsync(token, guild.Id, messages);
return new ChatLog(guild, channel, from, to, messages, mentionables);
}
public async Task<ChatLog> GetChatLogAsync(AuthToken token, string channelId,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null)
{
// Get channel
var channel = await GetChannelAsync(token, channelId);
// Get guild
var guild = channel.GuildId == Guild.DirectMessages.Id
? Guild.DirectMessages
: await GetGuildAsync(token, channel.GuildId);
// Get the chat log
return await GetChatLogAsync(token, guild, channel, from, to, progress);
}
public void Dispose()
{
_httpClient.Dispose();

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using DiscordChatExporter.Core.Models;
namespace DiscordChatExporter.Core.Services
{
public partial class ExportService
{
private class MessageGroup
{
public User Author { get; }
public DateTime Timestamp { get; }
public IReadOnlyList<Message> Messages { get; }
public MessageGroup(User author, DateTime timestamp, IReadOnlyList<Message> messages)
{
Author = author;
Timestamp = timestamp;
Messages = messages;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
@@ -18,12 +19,55 @@ 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)
public TemplateModel(ExportFormat format, ChatLog log, string dateFormat, int messageGroupLimit)
{
_format = format;
_log = log;
_dateFormat = dateFormat;
_messageGroupLimit = messageGroupLimit;
}
private IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages)
{
// Group adjacent messages by timestamp and author
var groupBuffer = new List<Message>();
foreach (var message in messages)
{
var groupFirst = groupBuffer.FirstOrDefault();
// Group break condition
var breakCondition =
groupFirst != null &&
(
message.Author.Id != groupFirst.Author.Id ||
(message.Timestamp - groupFirst.Timestamp).TotalHours > 1 ||
message.Timestamp.Hour != groupFirst.Timestamp.Hour ||
groupBuffer.Count >= _messageGroupLimit
);
// If condition is true - flush buffer
if (breakCondition)
{
var group = new MessageGroup(groupFirst.Author, groupFirst.Timestamp, groupBuffer.ToArray());
groupBuffer.Clear();
yield return group;
}
// Add message to buffer
groupBuffer.Add(message);
}
// Add what's remaining in buffer
if (groupBuffer.Any())
{
var groupFirst = groupBuffer.First();
var group = new MessageGroup(groupFirst.Author, groupFirst.Timestamp, groupBuffer.ToArray());
yield return group;
}
}
private string HtmlEncode(string str) => WebUtility.HtmlEncode(str);
@@ -310,6 +354,7 @@ namespace DiscordChatExporter.Core.Services
scriptObject.SetValue("Model", _log, true);
// Import functions
scriptObject.Import(nameof(GroupMessages), new Func<IEnumerable<Message>, IEnumerable<MessageGroup>>(GroupMessages));
scriptObject.Import(nameof(Format), new Func<IFormattable, string, string>(Format));
scriptObject.Import(nameof(FormatDate), new Func<DateTime, string>(FormatDate));
scriptObject.Import(nameof(FormatFileSize), new Func<long, string>(FormatFileSize));

View File

@@ -35,7 +35,9 @@ namespace DiscordChatExporter.Core.Services
};
// Create template model
var templateModel = new TemplateModel(format, chatLog, _settingsService.DateFormat);
var templateModel = new TemplateModel(format, chatLog,
_settingsService.DateFormat, _settingsService.MessageGroupLimit);
context.PushGlobal(templateModel.GetScriptObject());
// Create directory

View File

@@ -1,15 +0,0 @@
using System;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Models;
namespace DiscordChatExporter.Core.Services
{
public interface IChatLogService
{
Task<ChatLog> GetChatLogAsync(AuthToken token, Guild guild, Channel channel,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null);
Task<ChatLog> GetChatLogAsync(AuthToken token, string channelId,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null);
}
}

View File

@@ -24,5 +24,11 @@ namespace DiscordChatExporter.Core.Services
Task<Mentionables> GetMentionablesAsync(AuthToken token, string guildId,
IEnumerable<Message> messages);
Task<ChatLog> GetChatLogAsync(AuthToken token, Guild guild, Channel channel,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null);
Task<ChatLog> GetChatLogAsync(AuthToken token, string channelId,
DateTime? from = null, DateTime? to = null, IProgress<double> progress = null);
}
}

View File

@@ -1,10 +0,0 @@
using System.Collections.Generic;
using DiscordChatExporter.Core.Models;
namespace DiscordChatExporter.Core.Services
{
public interface IMessageGroupService
{
IReadOnlyList<MessageGroup> GroupMessages(IEnumerable<Message> messages);
}
}

View File

@@ -1,59 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using DiscordChatExporter.Core.Models;
namespace DiscordChatExporter.Core.Services
{
public class MessageGroupService : IMessageGroupService
{
private readonly ISettingsService _settingsService;
public MessageGroupService(ISettingsService settingsService)
{
_settingsService = settingsService;
}
public IReadOnlyList<MessageGroup> GroupMessages(IEnumerable<Message> messages)
{
var result = new List<MessageGroup>();
// Group adjacent messages by timestamp and author
var groupBuffer = new List<Message>();
foreach (var message in messages)
{
var groupFirst = groupBuffer.FirstOrDefault();
// Group break condition
var breakCondition =
groupFirst != null &&
(
message.Author.Id != groupFirst.Author.Id ||
(message.Timestamp - groupFirst.Timestamp).TotalHours > 1 ||
message.Timestamp.Hour != groupFirst.Timestamp.Hour ||
groupBuffer.Count >= _settingsService.MessageGroupLimit
);
// If condition is true - flush buffer
if (breakCondition)
{
var group = new MessageGroup(groupFirst.Author, groupFirst.Timestamp, groupBuffer.ToArray());
result.Add(group);
groupBuffer.Clear();
}
// Add message to buffer
groupBuffer.Add(message);
}
// Add what's remaining in buffer
if (groupBuffer.Any())
{
var groupFirst = groupBuffer.First();
var group = new MessageGroup(groupFirst.Author, groupFirst.Timestamp, groupBuffer.ToArray());
result.Add(group);
}
return result;
}
}
}