mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-25 04:04:22 -04:00
Refactor message grouping out and add a limit setting
This commit is contained in:
parent
69184a74fe
commit
7b9de314ab
13 changed files with 111 additions and 59 deletions
|
@ -14,6 +14,7 @@ namespace DiscordChatExporter
|
||||||
// Services
|
// Services
|
||||||
SimpleIoc.Default.Register<IDataService, DataService>();
|
SimpleIoc.Default.Register<IDataService, DataService>();
|
||||||
SimpleIoc.Default.Register<IExportService, ExportService>();
|
SimpleIoc.Default.Register<IExportService, ExportService>();
|
||||||
|
SimpleIoc.Default.Register<IMessageGroupService, MessageGroupService>();
|
||||||
SimpleIoc.Default.Register<ISettingsService, SettingsService>();
|
SimpleIoc.Default.Register<ISettingsService, SettingsService>();
|
||||||
|
|
||||||
// View models
|
// View models
|
||||||
|
|
|
@ -91,6 +91,8 @@
|
||||||
<Compile Include="Models\AttachmentType.cs" />
|
<Compile Include="Models\AttachmentType.cs" />
|
||||||
<Compile Include="Models\ChannelChatLog.cs" />
|
<Compile Include="Models\ChannelChatLog.cs" />
|
||||||
<Compile Include="Models\ChannelType.cs" />
|
<Compile Include="Models\ChannelType.cs" />
|
||||||
|
<Compile Include="Services\IMessageGroupService.cs" />
|
||||||
|
<Compile Include="Services\MessageGroupService.cs" />
|
||||||
<Compile Include="ViewModels\ErrorViewModel.cs" />
|
<Compile Include="ViewModels\ErrorViewModel.cs" />
|
||||||
<Compile Include="ViewModels\IErrorViewModel.cs" />
|
<Compile Include="ViewModels\IErrorViewModel.cs" />
|
||||||
<Compile Include="ViewModels\ISettingsViewModel.cs" />
|
<Compile Include="ViewModels\ISettingsViewModel.cs" />
|
||||||
|
|
|
@ -9,13 +9,13 @@ namespace DiscordChatExporter.Models
|
||||||
|
|
||||||
public Channel Channel { get; }
|
public Channel Channel { get; }
|
||||||
|
|
||||||
public IReadOnlyList<Message> Messages { get; }
|
public IReadOnlyList<MessageGroup> MessageGroups { get; }
|
||||||
|
|
||||||
public ChannelChatLog(Guild guild, Channel channel, IEnumerable<Message> messages)
|
public ChannelChatLog(Guild guild, Channel channel, IEnumerable<MessageGroup> messageGroups)
|
||||||
{
|
{
|
||||||
Guild = guild;
|
Guild = guild;
|
||||||
Channel = channel;
|
Channel = channel;
|
||||||
Messages = messages.ToArray();
|
MessageGroups = messageGroups.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -19,7 +17,7 @@ namespace DiscordChatExporter.Services
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Export(string filePath, ChannelChatLog channelChatLog, Theme theme)
|
public void Export(string filePath, ChannelChatLog log, Theme theme)
|
||||||
{
|
{
|
||||||
var doc = GetTemplate();
|
var doc = GetTemplate();
|
||||||
var style = GetStyle(theme);
|
var style = GetStyle(theme);
|
||||||
|
@ -31,25 +29,24 @@ namespace DiscordChatExporter.Services
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
var titleHtml = doc.DocumentNode.Element("html").Element("head").Element("title");
|
var titleHtml = doc.DocumentNode.Element("html").Element("head").Element("title");
|
||||||
titleHtml.InnerHtml = $"{channelChatLog.Guild.Name} - {channelChatLog.Channel.Name}";
|
titleHtml.InnerHtml = $"{log.Guild.Name} - {log.Channel.Name}";
|
||||||
|
|
||||||
// Info
|
// Info
|
||||||
var infoHtml = doc.GetElementbyId("info");
|
var infoHtml = doc.GetElementbyId("info");
|
||||||
var infoLeftHtml = infoHtml.AppendChild(HtmlNode.CreateNode("<div class=\"info-left\"></div>"));
|
var infoLeftHtml = infoHtml.AppendChild(HtmlNode.CreateNode("<div class=\"info-left\"></div>"));
|
||||||
infoLeftHtml.AppendChild(HtmlNode.CreateNode(
|
infoLeftHtml.AppendChild(HtmlNode.CreateNode(
|
||||||
$"<img class=\"guild-icon\" src=\"{channelChatLog.Guild.IconUrl}\" />"));
|
$"<img class=\"guild-icon\" src=\"{log.Guild.IconUrl}\" />"));
|
||||||
var infoRightHtml = infoHtml.AppendChild(HtmlNode.CreateNode("<div class=\"info-right\"></div>"));
|
var infoRightHtml = infoHtml.AppendChild(HtmlNode.CreateNode("<div class=\"info-right\"></div>"));
|
||||||
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
||||||
$"<div class=\"guild-name\">{channelChatLog.Guild.Name}</div>"));
|
$"<div class=\"guild-name\">{log.Guild.Name}</div>"));
|
||||||
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
||||||
$"<div class=\"channel-name\">{channelChatLog.Channel.Name}</div>"));
|
$"<div class=\"channel-name\">{log.Channel.Name}</div>"));
|
||||||
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
infoRightHtml.AppendChild(HtmlNode.CreateNode(
|
||||||
$"<div class=\"misc\">{channelChatLog.Messages.Count:N0} messages</div>"));
|
$"<div class=\"misc\">{log.MessageGroups.Count:N0} messages</div>"));
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
var logHtml = doc.GetElementbyId("log");
|
var logHtml = doc.GetElementbyId("log");
|
||||||
var messageGroups = GroupMessages(channelChatLog.Messages);
|
foreach (var messageGroup in log.MessageGroups)
|
||||||
foreach (var messageGroup in messageGroups)
|
|
||||||
{
|
{
|
||||||
// Container
|
// Container
|
||||||
var messageHtml = logHtml.AppendChild(HtmlNode.CreateNode("<div class=\"msg\"></div>"));
|
var messageHtml = logHtml.AppendChild(HtmlNode.CreateNode("<div class=\"msg\"></div>"));
|
||||||
|
@ -171,48 +168,6 @@ namespace DiscordChatExporter.Services
|
||||||
return $"{size:0.#} {units[unit]}";
|
return $"{size:0.#} {units[unit]}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<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
|
|
||||||
);
|
|
||||||
|
|
||||||
// If condition is true - flush buffer
|
|
||||||
if (breakCondition)
|
|
||||||
{
|
|
||||||
var group = new MessageGroup(groupFirst.Author, groupFirst.TimeStamp, groupBuffer);
|
|
||||||
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);
|
|
||||||
result.Add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatMessageContent(string content)
|
private static string FormatMessageContent(string content)
|
||||||
{
|
{
|
||||||
// Encode HTML
|
// Encode HTML
|
||||||
|
|
|
@ -4,6 +4,6 @@ namespace DiscordChatExporter.Services
|
||||||
{
|
{
|
||||||
public interface IExportService
|
public interface IExportService
|
||||||
{
|
{
|
||||||
void Export(string filePath, ChannelChatLog channelChatLog, Theme theme);
|
void Export(string filePath, ChannelChatLog log, Theme theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
10
DiscordChatExporter/Services/IMessageGroupService.cs
Normal file
10
DiscordChatExporter/Services/IMessageGroupService.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DiscordChatExporter.Models;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Services
|
||||||
|
{
|
||||||
|
public interface IMessageGroupService
|
||||||
|
{
|
||||||
|
IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ namespace DiscordChatExporter.Services
|
||||||
string Token { get; set; }
|
string Token { get; set; }
|
||||||
Theme Theme { get; set; }
|
Theme Theme { get; set; }
|
||||||
string DateFormat { get; set; }
|
string DateFormat { get; set; }
|
||||||
|
int MessageGroupLimit { get; set; }
|
||||||
|
|
||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
|
|
60
DiscordChatExporter/Services/MessageGroupService.cs
Normal file
60
DiscordChatExporter/Services/MessageGroupService.cs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using DiscordChatExporter.Models;
|
||||||
|
|
||||||
|
namespace DiscordChatExporter.Services
|
||||||
|
{
|
||||||
|
public class MessageGroupService : IMessageGroupService
|
||||||
|
{
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
|
||||||
|
public MessageGroupService(ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_settingsService = settingsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<MessageGroup> GroupMessages(IEnumerable<Message> messages)
|
||||||
|
{
|
||||||
|
var groupLimit = _settingsService.MessageGroupLimit;
|
||||||
|
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 >= groupLimit
|
||||||
|
);
|
||||||
|
|
||||||
|
// If condition is true - flush buffer
|
||||||
|
if (breakCondition)
|
||||||
|
{
|
||||||
|
var group = new MessageGroup(groupFirst.Author, groupFirst.TimeStamp, groupBuffer);
|
||||||
|
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);
|
||||||
|
result.Add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ namespace DiscordChatExporter.Services
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public Theme Theme { get; set; }
|
public Theme Theme { get; set; }
|
||||||
public string DateFormat { get; set; } = "dd-MMM-yy hh:mm";
|
public string DateFormat { get; set; } = "dd-MMM-yy hh:mm";
|
||||||
|
public int MessageGroupLimit { get; set; } = 20;
|
||||||
|
|
||||||
public SettingsService()
|
public SettingsService()
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,5 +7,7 @@ namespace DiscordChatExporter.ViewModels
|
||||||
{
|
{
|
||||||
IReadOnlyList<Theme> AvailableThemes { get; }
|
IReadOnlyList<Theme> AvailableThemes { get; }
|
||||||
Theme Theme { get; set; }
|
Theme Theme { get; set; }
|
||||||
|
string DateFormat { get; set; }
|
||||||
|
int MessageGroupLimit { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ namespace DiscordChatExporter.ViewModels
|
||||||
{
|
{
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IDataService _dataService;
|
private readonly IDataService _dataService;
|
||||||
|
private readonly IMessageGroupService _messageGroupService;
|
||||||
private readonly IExportService _exportService;
|
private readonly IExportService _exportService;
|
||||||
|
|
||||||
private readonly Dictionary<Guild, IReadOnlyList<Channel>> _guildChannelsMap;
|
private readonly Dictionary<Guild, IReadOnlyList<Channel>> _guildChannelsMap;
|
||||||
|
@ -85,10 +86,12 @@ namespace DiscordChatExporter.ViewModels
|
||||||
public RelayCommand ShowSettingsCommand { get; }
|
public RelayCommand ShowSettingsCommand { get; }
|
||||||
public RelayCommand ShowAboutCommand { get; }
|
public RelayCommand ShowAboutCommand { get; }
|
||||||
|
|
||||||
public MainViewModel(ISettingsService settingsService, IDataService dataService, IExportService exportService)
|
public MainViewModel(ISettingsService settingsService, IDataService dataService,
|
||||||
|
IMessageGroupService messageGroupService, IExportService exportService)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_dataService = dataService;
|
_dataService = dataService;
|
||||||
|
_messageGroupService = messageGroupService;
|
||||||
_exportService = exportService;
|
_exportService = exportService;
|
||||||
|
|
||||||
_guildChannelsMap = new Dictionary<Guild, IReadOnlyList<Channel>>();
|
_guildChannelsMap = new Dictionary<Guild, IReadOnlyList<Channel>>();
|
||||||
|
@ -166,8 +169,11 @@ namespace DiscordChatExporter.ViewModels
|
||||||
// Get messages
|
// Get messages
|
||||||
var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id);
|
var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id);
|
||||||
|
|
||||||
|
// Group them
|
||||||
|
var messageGroups = _messageGroupService.GroupMessages(messages);
|
||||||
|
|
||||||
// Create log
|
// Create log
|
||||||
var chatLog = new ChannelChatLog(SelectedGuild, channel, messages);
|
var chatLog = new ChannelChatLog(SelectedGuild, channel, messageGroups);
|
||||||
|
|
||||||
// Export
|
// Export
|
||||||
_exportService.Export(sfd.FileName, chatLog, _settingsService.Theme);
|
_exportService.Export(sfd.FileName, chatLog, _settingsService.Theme);
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||||
using DiscordChatExporter.Models;
|
using DiscordChatExporter.Models;
|
||||||
using DiscordChatExporter.Services;
|
using DiscordChatExporter.Services;
|
||||||
using GalaSoft.MvvmLight;
|
using GalaSoft.MvvmLight;
|
||||||
|
using Tyrrrz.Extensions;
|
||||||
|
|
||||||
namespace DiscordChatExporter.ViewModels
|
namespace DiscordChatExporter.ViewModels
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,12 @@ namespace DiscordChatExporter.ViewModels
|
||||||
set => _settingsService.DateFormat = value;
|
set => _settingsService.DateFormat = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int MessageGroupLimit
|
||||||
|
{
|
||||||
|
get => _settingsService.MessageGroupLimit;
|
||||||
|
set => _settingsService.MessageGroupLimit = value.ClampMin(0);
|
||||||
|
}
|
||||||
|
|
||||||
public SettingsViewModel(ISettingsService settingsService)
|
public SettingsViewModel(ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
|
|
@ -23,6 +23,13 @@ UserControl "DiscordChatExporter.Views.SettingsDialog" {
|
||||||
Text: bind DateFormat
|
Text: bind DateFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group limit
|
||||||
|
TextBox {
|
||||||
|
Margin: "16 8 16 8"
|
||||||
|
HintAssist.Hint: "Message group limit"
|
||||||
|
HintAssist.IsFloating: true
|
||||||
|
Text: bind MessageGroupLimit
|
||||||
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
Button {
|
Button {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue