diff --git a/DiscordChatExporter.Core/Internal/AssemblyHelper.cs b/DiscordChatExporter.Core/Internal/Extensions.cs
similarity index 67%
rename from DiscordChatExporter.Core/Internal/AssemblyHelper.cs
rename to DiscordChatExporter.Core/Internal/Extensions.cs
index 053518f..048f680 100644
--- a/DiscordChatExporter.Core/Internal/AssemblyHelper.cs
+++ b/DiscordChatExporter.Core/Internal/Extensions.cs
@@ -4,14 +4,13 @@ using System.Resources;
namespace DiscordChatExporter.Core.Internal
{
- internal static class AssemblyHelper
+ internal static class Extensions
{
- public static string GetResourceString(string resourcePath)
+ public static string GetManifestResourceString(this Assembly assembly, string resourceName)
{
- var assembly = Assembly.GetExecutingAssembly();
- var stream = assembly.GetManifestResourceStream(resourcePath);
+ var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
- throw new MissingManifestResourceException($"Could not find resource [{resourcePath}].");
+ throw new MissingManifestResourceException($"Could not find resource [{resourceName}].");
using (stream)
using (var reader = new StreamReader(stream))
diff --git a/DiscordChatExporter.Core/Models/User.cs b/DiscordChatExporter.Core/Models/User.cs
index 25591e9..eec1579 100644
--- a/DiscordChatExporter.Core/Models/User.cs
+++ b/DiscordChatExporter.Core/Models/User.cs
@@ -1,5 +1,4 @@
-using System.Globalization;
-using Tyrrrz.Extensions;
+using Tyrrrz.Extensions;
namespace DiscordChatExporter.Core.Models
{
@@ -11,11 +10,11 @@ namespace DiscordChatExporter.Core.Models
public string Name { get; }
- public string FullyQualifiedName => $"{Name}#{Discriminator:0000}";
+ public string FullName => $"{Name}#{Discriminator:0000}";
public string AvatarHash { get; }
- public string DefaultAvatarHash => (Discriminator % 5).ToString(CultureInfo.InvariantCulture);
+ public string DefaultAvatarHash => $"{Discriminator % 5}";
public string AvatarUrl => AvatarHash.IsNotBlank()
? $"https://cdn.discordapp.com/avatars/{Id}/{AvatarHash}.png"
@@ -31,7 +30,7 @@ namespace DiscordChatExporter.Core.Models
public override string ToString()
{
- return FullyQualifiedName;
+ return FullName;
}
}
}
\ No newline at end of file
diff --git a/DiscordChatExporter.Core/Services/ExportService.Html.cs b/DiscordChatExporter.Core/Services/ExportService.Html.cs
new file mode 100644
index 0000000..4913f11
--- /dev/null
+++ b/DiscordChatExporter.Core/Services/ExportService.Html.cs
@@ -0,0 +1,208 @@
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using DiscordChatExporter.Core.Models;
+using Tyrrrz.Extensions;
+
+namespace DiscordChatExporter.Core.Services
+{
+ public partial class ExportService
+ {
+ private string FormatMessageContentHtml(Message message)
+ {
+ // A lot of these regexes were inspired by or taken from MarkdownSharp
+
+ var content = message.Content;
+
+ // HTML-encode content
+ content = HtmlEncode(content);
+
+ // Encode multiline codeblocks (```text```)
+ content = Regex.Replace(content,
+ @"```+(?:[^`]*?\n)?([^`]+)\n?```+",
+ m => $"\x1AM{Base64Encode(m.Groups[1].Value)}\x1AM");
+
+ // Encode inline codeblocks (`text`)
+ content = Regex.Replace(content,
+ @"`([^`]+)`",
+ m => $"\x1AI{Base64Encode(m.Groups[1].Value)}\x1AI");
+
+ // Encode URLs
+ content = Regex.Replace(content,
+ @"((https?|ftp)://[-a-zA-Z0-9+&@#/%?=~_|!:,\.\[\]\(\);]*[-a-zA-Z0-9+&@#/%=~_|\[\])])(?=$|\W)",
+ m => $"\x1AL{Base64Encode(m.Groups[1].Value)}\x1AL");
+
+ // Process bold (**text**)
+ content = Regex.Replace(content, @"(\*\*)(?=\S)(.+?[*_]*)(?<=\S)\1", "$2");
+
+ // Process underline (__text__)
+ content = Regex.Replace(content, @"(__)(?=\S)(.+?)(?<=\S)\1", "$2");
+
+ // Process italic (*text* or _text_)
+ content = Regex.Replace(content, @"(\*|_)(?=\S)(.+?)(?<=\S)\1", "$2");
+
+ // Process strike through (~~text~~)
+ content = Regex.Replace(content, @"(~~)(?=\S)(.+?)(?<=\S)\1", "$2");
+
+ // Decode and process multiline codeblocks
+ content = Regex.Replace(content, "\x1AM(.*?)\x1AM",
+ m => $"
{Base64Decode(m.Groups[1].Value)}
");
+
+ // Decode and process inline codeblocks
+ content = Regex.Replace(content, "\x1AI(.*?)\x1AI",
+ m => $"{Base64Decode(m.Groups[1].Value)}");
+
+ // Decode and process URLs
+ content = Regex.Replace(content, "\x1AL(.*?)\x1AL",
+ m => $"{Base64Decode(m.Groups[1].Value)}");
+
+ // New lines
+ content = content.Replace("\n", "
");
+
+ // Meta mentions (@everyone)
+ content = content.Replace("@everyone", "@everyone");
+
+ // Meta mentions (@here)
+ content = content.Replace("@here", "@here");
+
+ // User mentions (<@id> and <@!id>)
+ foreach (var mentionedUser in message.MentionedUsers)
+ {
+ content = Regex.Replace(content, $"<@!?{mentionedUser.Id}>",
+ $"" +
+ $"@{HtmlEncode(mentionedUser.Name)}" +
+ "");
+ }
+
+ // Role mentions (<@&id>)
+ foreach (var mentionedRole in message.MentionedRoles)
+ {
+ content = content.Replace($"<@&{mentionedRole.Id}>",
+ "" +
+ $"@{HtmlEncode(mentionedRole.Name)}" +
+ "");
+ }
+
+ // Channel mentions (<#id>)
+ foreach (var mentionedChannel in message.MentionedChannels)
+ {
+ content = content.Replace($"<#{mentionedChannel.Id}>",
+ "" +
+ $"#{HtmlEncode(mentionedChannel.Name)}" +
+ "");
+ }
+
+ // Custom emojis (<:name:id>)
+ content = Regex.Replace(content, "<(:.*?:)(\\d*)>",
+ "");
+
+ return content;
+ }
+
+ private async Task ExportAsHtmlAsync(ChannelChatLog log, TextWriter output, string css)
+ {
+ // Generation info
+ await output.WriteLineAsync("");
+
+ // Html start
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync("");
+
+ // HEAD
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync($"{log.Guild.Name} - {log.Channel.Name}");
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync($"");
+ await output.WriteLineAsync("");
+
+ // Body start
+ await output.WriteLineAsync("");
+
+ // Guild and channel info
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync("
");
+ await output.WriteLineAsync($"
");
+ await output.WriteLineAsync("
"); // info-left
+ await output.WriteLineAsync("
");
+ await output.WriteLineAsync($"
{log.Guild.Name}
");
+ await output.WriteLineAsync($"
{log.Channel.Name}
");
+ await output.WriteLineAsync($"
{log.Channel.Topic}
");
+ await output.WriteLineAsync(
+ $"
{log.TotalMessageCount:N0} messages
");
+ await output.WriteLineAsync("
"); // info-right
+ await output.WriteLineAsync("
"); // info
+
+ // Chat log
+ await output.WriteLineAsync("");
+ foreach (var group in log.MessageGroups)
+ {
+ await output.WriteLineAsync("
");
+ await output.WriteLineAsync("
");
+ await output.WriteLineAsync($"
");
+ await output.WriteLineAsync("
");
+
+ await output.WriteLineAsync("
");
+ await output.WriteAsync(
+ $"
");
+ await output.WriteAsync(HtmlEncode(group.Author.Name));
+ await output.WriteLineAsync("");
+ var timeStampFormatted = HtmlEncode(group.TimeStamp.ToString(_settingsService.DateFormat));
+ await output.WriteLineAsync($"
{timeStampFormatted}");
+
+ // Messages
+ foreach (var message in group.Messages)
+ {
+ // Content
+ if (message.Content.IsNotBlank())
+ {
+ await output.WriteLineAsync("
");
+ var contentFormatted = FormatMessageContentHtml(message);
+ await output.WriteAsync(contentFormatted);
+
+ // Edited timestamp
+ if (message.EditedTimeStamp != null)
+ {
+ var editedTimeStampFormatted =
+ HtmlEncode(message.EditedTimeStamp.Value.ToString(_settingsService.DateFormat));
+ await output.WriteAsync(
+ $"(edited)");
+ }
+
+ await output.WriteLineAsync("
"); // msg-content
+ }
+
+ // Attachments
+ foreach (var attachment in message.Attachments)
+ {
+ if (attachment.Type == AttachmentType.Image)
+ {
+ await output.WriteLineAsync("
");
+ }
+ else
+ {
+ await output.WriteLineAsync("
");
+ }
+ }
+ }
+
+ await output.WriteLineAsync("
"); // msg-right
+ await output.WriteLineAsync("
"); // msg
+ }
+
+ await output.WriteLineAsync("
"); // log
+
+ await output.WriteLineAsync("");
+ await output.WriteLineAsync("");
+ }
+ }
+}
\ No newline at end of file
diff --git a/DiscordChatExporter.Core/Services/ExportService.PlainText.cs b/DiscordChatExporter.Core/Services/ExportService.PlainText.cs
new file mode 100644
index 0000000..af0734a
--- /dev/null
+++ b/DiscordChatExporter.Core/Services/ExportService.PlainText.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using DiscordChatExporter.Core.Models;
+using Tyrrrz.Extensions;
+
+namespace DiscordChatExporter.Core.Services
+{
+ public partial class ExportService
+ {
+ private string FormatMessageContentPlainText(Message message)
+ {
+ var content = message.Content;
+
+ // New lines
+ content = content.Replace("\n", Environment.NewLine);
+
+ // User mentions (<@id> and <@!id>)
+ foreach (var mentionedUser in message.MentionedUsers)
+ content = Regex.Replace(content, $"<@!?{mentionedUser.Id}>", $"@{mentionedUser}");
+
+ // Role mentions (<@&id>)
+ foreach (var mentionedRole in message.MentionedRoles)
+ content = content.Replace($"<@&{mentionedRole.Id}>", $"@{mentionedRole.Name}");
+
+ // Channel mentions (<#id>)
+ foreach (var mentionedChannel in message.MentionedChannels)
+ content = content.Replace($"<#{mentionedChannel.Id}>", $"#{mentionedChannel.Name}");
+
+ // Custom emojis (<:name:id>)
+ content = Regex.Replace(content, "<(:.*?:)\\d*>", "$1");
+
+ return content;
+ }
+
+ private async Task ExportAsPlainTextAsync(ChannelChatLog log, TextWriter output)
+ {
+ // Generation info
+ await output.WriteLineAsync("https://github.com/Tyrrrz/DiscordChatExporter");
+ await output.WriteLineAsync();
+
+ // Guild and channel info
+ await output.WriteLineAsync('='.Repeat(48));
+ await output.WriteLineAsync($"Guild: {log.Guild.Name}");
+ await output.WriteLineAsync($"Channel: {log.Channel.Name}");
+ await output.WriteLineAsync($"Topic: {log.Channel.Topic}");
+ await output.WriteLineAsync($"Messages: {log.TotalMessageCount:N0}");
+ await output.WriteLineAsync('='.Repeat(48));
+ await output.WriteLineAsync();
+
+ // Chat log
+ foreach (var group in log.MessageGroups)
+ {
+ var timeStampFormatted = group.TimeStamp.ToString(_settingsService.DateFormat);
+ await output.WriteLineAsync($"{group.Author.FullName} [{timeStampFormatted}]");
+
+ // Messages
+ foreach (var message in group.Messages)
+ {
+ // Content
+ if (message.Content.IsNotBlank())
+ {
+ var contentFormatted = FormatMessageContentPlainText(message);
+ await output.WriteLineAsync(contentFormatted);
+ }
+
+ // Attachments
+ foreach (var attachment in message.Attachments)
+ {
+ await output.WriteLineAsync(attachment.Url);
+ }
+ }
+
+ await output.WriteLineAsync();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DiscordChatExporter.Core/Services/ExportService.cs b/DiscordChatExporter.Core/Services/ExportService.cs
index 16ae1d0..da7dba1 100644
--- a/DiscordChatExporter.Core/Services/ExportService.cs
+++ b/DiscordChatExporter.Core/Services/ExportService.cs
@@ -1,8 +1,7 @@
using System;
using System.IO;
using System.Net;
-using System.Text;
-using System.Text.RegularExpressions;
+using System.Reflection;
using System.Threading.Tasks;
using DiscordChatExporter.Core.Internal;
using DiscordChatExporter.Core.Models;
@@ -19,190 +18,49 @@ namespace DiscordChatExporter.Core.Services
_settingsService = settingsService;
}
- private async Task ExportAsTextAsync(string filePath, ChannelChatLog log)
+ public async Task ExportAsync(ExportFormat format, string filePath, ChannelChatLog log)
{
- using (var writer = new StreamWriter(filePath, false, Encoding.UTF8, 128 * 1024))
+ using (var output = File.CreateText(filePath))
{
- // Generation info
- await writer.WriteLineAsync("https://github.com/Tyrrrz/DiscordChatExporter");
- await writer.WriteLineAsync();
-
- // Guild and channel info
- await writer.WriteLineAsync('='.Repeat(48));
- await writer.WriteLineAsync($"Guild: {log.Guild.Name}");
- await writer.WriteLineAsync($"Channel: {log.Channel.Name}");
- await writer.WriteLineAsync($"Topic: {log.Channel.Topic}");
- await writer.WriteLineAsync($"Messages: {log.TotalMessageCount:N0}");
- await writer.WriteLineAsync('='.Repeat(48));
- await writer.WriteLineAsync();
-
- // Chat log
- foreach (var group in log.MessageGroups)
+ if (format == ExportFormat.PlainText)
{
- var timeStampFormatted = group.TimeStamp.ToString(_settingsService.DateFormat);
- await writer.WriteLineAsync($"{group.Author.FullyQualifiedName} [{timeStampFormatted}]");
-
- // Messages
- foreach (var message in group.Messages)
- {
- // Content
- if (message.Content.IsNotBlank())
- {
- var contentFormatted = FormatMessageContentText(message);
- await writer.WriteLineAsync(contentFormatted);
- }
-
- // Attachments
- foreach (var attachment in message.Attachments)
- {
- await writer.WriteLineAsync(attachment.Url);
- }
- }
-
- await writer.WriteLineAsync();
+ await ExportAsPlainTextAsync(log, output);
}
- }
- }
-
- private async Task ExportAsHtmlAsync(string filePath, ChannelChatLog log, string css)
- {
- using (var writer = new StreamWriter(filePath, false, Encoding.UTF8, 128 * 1024))
- {
- // Generation info
- await writer.WriteLineAsync("");
-
- // Html start
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync("");
-
- // HEAD
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync($"{log.Guild.Name} - {log.Channel.Name}");
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync($"");
- await writer.WriteLineAsync("");
-
- // Body start
- await writer.WriteLineAsync("");
-
- // Guild and channel info
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync("
");
- await writer.WriteLineAsync($"
");
- await writer.WriteLineAsync("
"); // info-left
- await writer.WriteLineAsync("
");
- await writer.WriteLineAsync($"
{log.Guild.Name}
");
- await writer.WriteLineAsync($"
{log.Channel.Name}
");
- await writer.WriteLineAsync($"
{log.Channel.Topic}
");
- await writer.WriteLineAsync(
- $"
{log.TotalMessageCount:N0} messages
");
- await writer.WriteLineAsync("
"); // info-right
- await writer.WriteLineAsync("
"); // info
- // Chat log
- await writer.WriteLineAsync("");
- foreach (var group in log.MessageGroups)
+ else if (format == ExportFormat.HtmlDark)
{
- await writer.WriteLineAsync("
");
- await writer.WriteLineAsync("
");
- await writer.WriteLineAsync($"
");
- await writer.WriteLineAsync("
");
-
- await writer.WriteLineAsync("
");
- await writer.WriteAsync(
- $"
");
- await writer.WriteAsync(HtmlEncode(group.Author.Name));
- await writer.WriteLineAsync("");
- var timeStampFormatted = HtmlEncode(group.TimeStamp.ToString(_settingsService.DateFormat));
- await writer.WriteLineAsync($"
{timeStampFormatted}");
-
- // Messages
- foreach (var message in group.Messages)
- {
- // Content
- if (message.Content.IsNotBlank())
- {
- await writer.WriteLineAsync("
");
- var contentFormatted = FormatMessageContentHtml(message);
- await writer.WriteAsync(contentFormatted);
-
- // Edited timestamp
- if (message.EditedTimeStamp != null)
- {
- var editedTimeStampFormatted =
- HtmlEncode(message.EditedTimeStamp.Value.ToString(_settingsService.DateFormat));
- await writer.WriteAsync(
- $"(edited)");
- }
-
- await writer.WriteLineAsync("
"); // msg-content
- }
-
- // Attachments
- foreach (var attachment in message.Attachments)
- {
- if (attachment.Type == AttachmentType.Image)
- {
- await writer.WriteLineAsync("
");
- }
- else
- {
- await writer.WriteLineAsync("
");
- }
- }
- }
- await writer.WriteLineAsync("
"); // msg-right
- await writer.WriteLineAsync("
"); // msg
+ var css = Assembly.GetExecutingAssembly()
+ .GetManifestResourceString("DiscordChatExporter.Core.Resources.ExportService.DarkTheme.css");
+ await ExportAsHtmlAsync(log, output, css);
}
- await writer.WriteLineAsync("
"); // log
- await writer.WriteLineAsync("");
- await writer.WriteLineAsync("");
- }
- }
+ else if (format == ExportFormat.HtmlLight)
+ {
+ var css = Assembly.GetExecutingAssembly()
+ .GetManifestResourceString("DiscordChatExporter.Core.Resources.ExportService.LightTheme.css");
+ await ExportAsHtmlAsync(log, output, css);
+ }
- public Task ExportAsync(ExportFormat format, string filePath, ChannelChatLog log)
- {
- if (format == ExportFormat.PlainText)
- {
- return ExportAsTextAsync(filePath, log);
- }
- if (format == ExportFormat.HtmlDark)
- {
- var css = AssemblyHelper.GetResourceString("DiscordChatExporter.Core.Resources.ExportService.DarkTheme.css");
- return ExportAsHtmlAsync(filePath, log, css);
+ else throw new ArgumentOutOfRangeException(nameof(format));
}
- if (format == ExportFormat.HtmlLight)
- {
- var css = AssemblyHelper.GetResourceString("DiscordChatExporter.Core.Resources.ExportService.LightTheme.css");
- return ExportAsHtmlAsync(filePath, log, css);
- }
-
- throw new ArgumentOutOfRangeException(nameof(format));
}
}
public partial class ExportService
{
- private static string HtmlEncode(string str)
+ private static string Base64Encode(string str)
{
- return WebUtility.HtmlEncode(str);
+ return str.GetBytes().ToBase64();
}
- private static string HtmlEncode(object obj)
+ private static string Base64Decode(string str)
+ {
+ return str.FromBase64().GetString();
+ }
+
+ private static string HtmlEncode(string str)
{
- return WebUtility.HtmlEncode(obj.ToString());
+ return WebUtility.HtmlEncode(str);
}
private static string FormatFileSize(long fileSize)
@@ -219,104 +77,5 @@ namespace DiscordChatExporter.Core.Services
return $"{size:0.#} {units[unit]}";
}
-
- public static string FormatMessageContentText(Message message)
- {
- var content = message.Content;
-
- // New lines
- content = content.Replace("\n", Environment.NewLine);
-
- // User mentions (<@id> and <@!id>)
- foreach (var mentionedUser in message.MentionedUsers)
- content = Regex.Replace(content, $"<@!?{mentionedUser.Id}>", $"@{mentionedUser}");
-
- // Role mentions (<@&id>)
- foreach (var mentionedRole in message.MentionedRoles)
- content = content.Replace($"<@&{mentionedRole.Id}>", $"@{mentionedRole.Name}");
-
- // Channel mentions (<#id>)
- foreach (var mentionedChannel in message.MentionedChannels)
- content = content.Replace($"<#{mentionedChannel.Id}>", $"#{mentionedChannel.Name}");
-
- // Custom emojis (<:name:id>)
- content = Regex.Replace(content, "<(:.*?:)\\d*>", "$1");
-
- return content;
- }
-
- private static string FormatMessageContentHtml(Message message)
- {
- var content = message.Content;
-
- // Encode HTML
- content = HtmlEncode(content);
-
- // Pre multiline (```text```)
- content = Regex.Replace(content, "```+(?:[^`]*?\\n)?([^`]+)\\n?```+", "$1
");
-
- // Pre inline (`text`)
- content = Regex.Replace(content, "`([^`]+)`", "$1");
-
- // Bold (**text**)
- content = Regex.Replace(content, "\\*\\*([^\\*]*?)\\*\\*", "$1");
-
- // Italic (*text*)
- content = Regex.Replace(content, "\\*([^\\*]*?)\\*", "$1");
-
- // Underline (__text__)
- content = Regex.Replace(content, "__([^_]*?)__", "$1");
-
- // Italic (_text_)
- content = Regex.Replace(content, "_([^_]*?)_", "$1");
-
- // Strike through (~~text~~)
- content = Regex.Replace(content, "~~([^~]*?)~~", "$1");
-
- // New lines
- content = content.Replace("\n", "
");
-
- // URL links
- content = Regex.Replace(content, "((https?|ftp)://[^\\s/$.?#].[^\\s<>]*)", "$1");
-
- // Meta mentions (@everyone)
- content = content.Replace("@everyone", "@everyone");
-
- // Meta mentions (@here)
- content = content.Replace("@here", "@here");
-
- // User mentions (<@id> and <@!id>)
- foreach (var mentionedUser in message.MentionedUsers)
- {
- content = Regex.Replace(content, $"<@!?{mentionedUser.Id}>",
- $"" +
- $"@{HtmlEncode(mentionedUser.Name)}" +
- "");
- }
-
- // Role mentions (<@&id>)
- foreach (var mentionedRole in message.MentionedRoles)
- {
- content = content.Replace($"<@&{mentionedRole.Id}>",
- "" +
- $"@{HtmlEncode(mentionedRole.Name)}" +
- "");
- }
-
- // Channel mentions (<#id>)
- foreach (var mentionedChannel in message.MentionedChannels)
- {
- content = content.Replace($"<#{mentionedChannel.Id}>",
- "" +
- $"#{HtmlEncode(mentionedChannel.Name)}" +
- "");
- }
-
- // Custom emojis (<:name:id>)
- content = Regex.Replace(content, "<(:.*?:)(\\d*)>",
- "");
-
- return content;
- }
}
}
\ No newline at end of file
diff --git a/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs
index 5f418ce..4d22e98 100644
--- a/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs
+++ b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs
@@ -235,12 +235,12 @@ namespace DiscordChatExporter.Gui.ViewModels
await _exportService.ExportAsync(format, filePath, log);
// Notify completion
- MessengerInstance.Send(new ShowNotificationMessage($"Export completed for channel [{channel.Name}]",
+ MessengerInstance.Send(new ShowNotificationMessage("Export complete",
"OPEN", () => Process.Start(filePath)));
}
catch (HttpErrorStatusCodeException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
{
- MessengerInstance.Send(new ShowNotificationMessage("You don't have access to that channel"));
+ MessengerInstance.Send(new ShowNotificationMessage("You don't have access to this channel"));
}
IsBusy = false;