mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2025-05-22 10:55:15 -04:00
Convert several types to records
This commit is contained in:
parent
2393a6a472
commit
7c88a21543
38 changed files with 182 additions and 826 deletions
|
@ -1,9 +0,0 @@
|
||||||
// ReSharper disable CheckNamespace
|
|
||||||
// TODO: remove after moving to .NET 5
|
|
||||||
|
|
||||||
namespace System.Runtime.CompilerServices
|
|
||||||
{
|
|
||||||
internal static class IsExternalInit
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
// ReSharper disable CheckNamespace
|
|
||||||
// TODO: remove after moving to .NET 5
|
|
||||||
|
|
||||||
namespace System.Runtime.CompilerServices
|
|
||||||
{
|
|
||||||
internal static class IsExternalInit
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +1,13 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord
|
namespace DiscordChatExporter.Core.Discord
|
||||||
{
|
{
|
||||||
public class AuthToken
|
public record AuthToken(AuthTokenKind Kind, string Value)
|
||||||
{
|
{
|
||||||
public AuthTokenKind Kind { get; }
|
|
||||||
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public AuthToken(AuthTokenKind kind, string value)
|
|
||||||
{
|
|
||||||
Kind = kind;
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthenticationHeaderValue GetAuthenticationHeader() => Kind switch
|
public AuthenticationHeaderValue GetAuthenticationHeader() => Kind switch
|
||||||
{
|
{
|
||||||
AuthTokenKind.Bot => new AuthenticationHeaderValue("Bot", Value),
|
AuthTokenKind.Bot => new AuthenticationHeaderValue("Bot", Value),
|
||||||
_ => new AuthenticationHeaderValue(Value)
|
_ => new AuthenticationHeaderValue(Value)
|
||||||
};
|
};
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
|
@ -10,20 +9,16 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#attachment-object
|
// https://discord.com/developers/docs/resources/channel#attachment-object
|
||||||
public partial class Attachment : IHasId
|
public partial record Attachment(
|
||||||
|
Snowflake Id,
|
||||||
|
string Url,
|
||||||
|
string FileName,
|
||||||
|
int? Width,
|
||||||
|
int? Height,
|
||||||
|
FileSize FileSize) : IHasId
|
||||||
{
|
{
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public string Url { get; }
|
|
||||||
|
|
||||||
public string FileName { get; }
|
|
||||||
|
|
||||||
public string FileExtension => Path.GetExtension(FileName);
|
public string FileExtension => Path.GetExtension(FileName);
|
||||||
|
|
||||||
public int? Width { get; }
|
|
||||||
|
|
||||||
public int? Height { get; }
|
|
||||||
|
|
||||||
public bool IsImage => FileFormat.IsImage(FileExtension);
|
public bool IsImage => FileFormat.IsImage(FileExtension);
|
||||||
|
|
||||||
public bool IsVideo => FileFormat.IsVideo(FileExtension);
|
public bool IsVideo => FileFormat.IsVideo(FileExtension);
|
||||||
|
@ -31,30 +26,9 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
public bool IsAudio => FileFormat.IsAudio(FileExtension);
|
public bool IsAudio => FileFormat.IsAudio(FileExtension);
|
||||||
|
|
||||||
public bool IsSpoiler => FileName.StartsWith("SPOILER_", StringComparison.Ordinal);
|
public bool IsSpoiler => FileName.StartsWith("SPOILER_", StringComparison.Ordinal);
|
||||||
|
|
||||||
public FileSize FileSize { get; }
|
|
||||||
|
|
||||||
public Attachment(
|
|
||||||
Snowflake id,
|
|
||||||
string url,
|
|
||||||
string fileName,
|
|
||||||
int? width,
|
|
||||||
int? height,
|
|
||||||
FileSize fileSize)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Url = url;
|
|
||||||
FileName = fileName;
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
FileSize = fileSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => FileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Attachment
|
public partial record Attachment
|
||||||
{
|
{
|
||||||
public static Attachment Parse(JsonElement json)
|
public static Attachment Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
@ -9,12 +8,15 @@ using Tyrrrz.Extensions;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#channel-object
|
// https://discord.com/developers/docs/resources/channel#channel-object
|
||||||
public partial class Channel : IHasId
|
public partial record Channel(
|
||||||
|
Snowflake Id,
|
||||||
|
ChannelKind Kind,
|
||||||
|
Snowflake GuildId,
|
||||||
|
ChannelCategory Category,
|
||||||
|
string Name,
|
||||||
|
int? Position,
|
||||||
|
string? Topic) : IHasId
|
||||||
{
|
{
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public ChannelKind Kind { get; }
|
|
||||||
|
|
||||||
public bool IsTextChannel => Kind is
|
public bool IsTextChannel => Kind is
|
||||||
ChannelKind.GuildTextChat or
|
ChannelKind.GuildTextChat or
|
||||||
ChannelKind.DirectTextChat or
|
ChannelKind.DirectTextChat or
|
||||||
|
@ -23,40 +25,9 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
ChannelKind.GuildStore;
|
ChannelKind.GuildStore;
|
||||||
|
|
||||||
public bool IsVoiceChannel => !IsTextChannel;
|
public bool IsVoiceChannel => !IsTextChannel;
|
||||||
|
|
||||||
public Snowflake GuildId { get; }
|
|
||||||
|
|
||||||
public ChannelCategory Category { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public int? Position { get; }
|
|
||||||
|
|
||||||
public string? Topic { get; }
|
|
||||||
|
|
||||||
public Channel(
|
|
||||||
Snowflake id,
|
|
||||||
ChannelKind kind,
|
|
||||||
Snowflake guildId,
|
|
||||||
ChannelCategory category,
|
|
||||||
string name,
|
|
||||||
int? position,
|
|
||||||
string? topic)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Kind = kind;
|
|
||||||
GuildId = guildId;
|
|
||||||
Category = category;
|
|
||||||
Name = name;
|
|
||||||
Position = position;
|
|
||||||
Topic = topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Channel
|
public partial record Channel
|
||||||
{
|
{
|
||||||
private static ChannelCategory GetFallbackCategory(ChannelKind channelKind) => new(
|
private static ChannelCategory GetFallbackCategory(ChannelKind channelKind) => new(
|
||||||
Snowflake.Zero,
|
Snowflake.Zero,
|
||||||
|
@ -77,13 +48,14 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
var id = json.GetProperty("id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse);
|
var id = json.GetProperty("id").GetNonWhiteSpaceString().Pipe(Snowflake.Parse);
|
||||||
var guildId = json.GetPropertyOrNull("guild_id")?.GetNonWhiteSpaceString().Pipe(Snowflake.Parse);
|
var guildId = json.GetPropertyOrNull("guild_id")?.GetNonWhiteSpaceString().Pipe(Snowflake.Parse);
|
||||||
var topic = json.GetPropertyOrNull("topic")?.GetStringOrNull();
|
var topic = json.GetPropertyOrNull("topic")?.GetStringOrNull();
|
||||||
var kind = (ChannelKind) json.GetProperty("type").GetInt32();
|
var kind = (ChannelKind)json.GetProperty("type").GetInt32();
|
||||||
|
|
||||||
var name =
|
var name =
|
||||||
// Guild channel
|
// Guild channel
|
||||||
json.GetPropertyOrNull("name")?.GetStringOrNull() ??
|
json.GetPropertyOrNull("name")?.GetStringOrNull() ??
|
||||||
// DM channel
|
// DM channel
|
||||||
json.GetPropertyOrNull("recipients")?.EnumerateArray().Select(User.Parse).Select(u => u.Name).JoinToString(", ") ??
|
json.GetPropertyOrNull("recipients")?.EnumerateArray().Select(User.Parse).Select(u => u.Name)
|
||||||
|
.JoinToString(", ") ??
|
||||||
// Fallback
|
// Fallback
|
||||||
id.ToString();
|
id.ToString();
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,11 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Text.Json;
|
||||||
using System.Text.Json;
|
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
public partial class ChannelCategory : IHasId
|
public record ChannelCategory(Snowflake Id, string Name, int? Position) : IHasId
|
||||||
{
|
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public int? Position { get; }
|
|
||||||
|
|
||||||
public ChannelCategory(Snowflake id, string name, int? position)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
Position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class ChannelCategory
|
|
||||||
{
|
{
|
||||||
public static ChannelCategory Unknown { get; } = new(Snowflake.Zero, "<unknown category>", 0);
|
public static ChannelCategory Unknown { get; } = new(Snowflake.Zero, "<unknown category>", 0);
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,12 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Common
|
namespace DiscordChatExporter.Core.Discord.Data.Common
|
||||||
{
|
{
|
||||||
// Loosely based on https://github.com/omar/ByteSize (MIT license)
|
// Loosely based on https://github.com/omar/ByteSize (MIT license)
|
||||||
public readonly partial struct FileSize
|
public readonly partial record struct FileSize(long TotalBytes)
|
||||||
{
|
{
|
||||||
public long TotalBytes { get; }
|
|
||||||
|
|
||||||
public double TotalKiloBytes => TotalBytes / 1024.0;
|
public double TotalKiloBytes => TotalBytes / 1024.0;
|
||||||
public double TotalMegaBytes => TotalKiloBytes / 1024.0;
|
public double TotalMegaBytes => TotalKiloBytes / 1024.0;
|
||||||
public double TotalGigaBytes => TotalMegaBytes / 1024.0;
|
public double TotalGigaBytes => TotalMegaBytes / 1024.0;
|
||||||
|
|
||||||
public FileSize(long bytes) => TotalBytes = bytes;
|
|
||||||
|
|
||||||
private double GetLargestWholeNumberValue()
|
private double GetLargestWholeNumberValue()
|
||||||
{
|
{
|
||||||
if (Math.Abs(TotalGigaBytes) >= 1)
|
if (Math.Abs(TotalGigaBytes) >= 1)
|
||||||
|
@ -46,7 +42,7 @@ namespace DiscordChatExporter.Core.Discord.Data.Common
|
||||||
public override string ToString() => $"{GetLargestWholeNumberValue():0.##} {GetLargestWholeNumberSymbol()}";
|
public override string ToString() => $"{GetLargestWholeNumberValue():0.##} {GetLargestWholeNumberSymbol()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial struct FileSize
|
public partial record struct FileSize
|
||||||
{
|
{
|
||||||
public static FileSize FromBytes(long bytes) => new(bytes);
|
public static FileSize FromBytes(long bytes) => new(bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,12 @@
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Common
|
namespace DiscordChatExporter.Core.Discord.Data.Common
|
||||||
{
|
{
|
||||||
public partial class IdBasedEqualityComparer : IEqualityComparer<IHasId>
|
public class IdBasedEqualityComparer : IEqualityComparer<IHasId>
|
||||||
{
|
{
|
||||||
|
public static IdBasedEqualityComparer Instance { get; } = new();
|
||||||
|
|
||||||
public bool Equals(IHasId? x, IHasId? y) => x?.Id == y?.Id;
|
public bool Equals(IHasId? x, IHasId? y) => x?.Id == y?.Id;
|
||||||
|
|
||||||
public int GetHashCode(IHasId obj) => obj.Id.GetHashCode();
|
public int GetHashCode(IHasId obj) => obj.Id.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class IdBasedEqualityComparer
|
|
||||||
{
|
|
||||||
public static IdBasedEqualityComparer Instance { get; } = new();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
@ -10,63 +9,29 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#embed-object
|
// https://discord.com/developers/docs/resources/channel#embed-object
|
||||||
public partial class Embed
|
public partial record Embed(
|
||||||
|
string? Title,
|
||||||
|
string? Url,
|
||||||
|
DateTimeOffset? Timestamp,
|
||||||
|
Color? Color,
|
||||||
|
EmbedAuthor? Author,
|
||||||
|
string? Description,
|
||||||
|
IReadOnlyList<EmbedField> Fields,
|
||||||
|
EmbedImage? Thumbnail,
|
||||||
|
EmbedImage? Image,
|
||||||
|
EmbedFooter? Footer)
|
||||||
{
|
{
|
||||||
public string? Title { get; }
|
public PlainImageEmbedProjection? TryGetPlainImage() =>
|
||||||
|
PlainImageEmbedProjection.TryResolve(this);
|
||||||
|
|
||||||
public string? Url { get; }
|
public SpotifyTrackEmbedProjection? TryGetSpotifyTrack() =>
|
||||||
|
SpotifyTrackEmbedProjection.TryResolve(this);
|
||||||
|
|
||||||
public DateTimeOffset? Timestamp { get; }
|
public YouTubeVideoEmbedProjection? TryGetYouTubeVideo() =>
|
||||||
|
YouTubeVideoEmbedProjection.TryResolve(this);
|
||||||
public Color? Color { get; }
|
|
||||||
|
|
||||||
public EmbedAuthor? Author { get; }
|
|
||||||
|
|
||||||
public string? Description { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<EmbedField> Fields { get; }
|
|
||||||
|
|
||||||
public EmbedImage? Thumbnail { get; }
|
|
||||||
|
|
||||||
public EmbedImage? Image { get; }
|
|
||||||
|
|
||||||
public EmbedFooter? Footer { get; }
|
|
||||||
|
|
||||||
public Embed(
|
|
||||||
string? title,
|
|
||||||
string? url,
|
|
||||||
DateTimeOffset? timestamp,
|
|
||||||
Color? color,
|
|
||||||
EmbedAuthor? author,
|
|
||||||
string? description,
|
|
||||||
IReadOnlyList<EmbedField> fields,
|
|
||||||
EmbedImage? thumbnail,
|
|
||||||
EmbedImage? image,
|
|
||||||
EmbedFooter? footer)
|
|
||||||
{
|
|
||||||
Title = title;
|
|
||||||
Url = url;
|
|
||||||
Timestamp = timestamp;
|
|
||||||
Color = color;
|
|
||||||
Author = author;
|
|
||||||
Description = description;
|
|
||||||
Fields = fields;
|
|
||||||
Thumbnail = thumbnail;
|
|
||||||
Image = image;
|
|
||||||
Footer = footer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlainImageEmbedProjection? TryGetPlainImage() => PlainImageEmbedProjection.TryResolve(this);
|
|
||||||
|
|
||||||
public SpotifyTrackEmbedProjection? TryGetSpotifyTrack() => SpotifyTrackEmbedProjection.TryResolve(this);
|
|
||||||
|
|
||||||
public YouTubeVideoEmbedProjection? TryGetYouTubeVideo() => YouTubeVideoEmbedProjection.TryResolve(this);
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Title ?? "<untitled embed>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Embed
|
public partial record Embed
|
||||||
{
|
{
|
||||||
public static Embed Parse(JsonElement json)
|
public static Embed Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,33 +1,14 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure
|
// https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure
|
||||||
public partial class EmbedAuthor
|
public record EmbedAuthor(
|
||||||
{
|
string? Name,
|
||||||
public string? Name { get; }
|
string? Url,
|
||||||
|
string? IconUrl,
|
||||||
public string? Url { get; }
|
string? IconProxyUrl)
|
||||||
|
|
||||||
public string? IconUrl { get; }
|
|
||||||
|
|
||||||
public string? IconProxyUrl { get; }
|
|
||||||
|
|
||||||
public EmbedAuthor(string? name, string? url, string? iconUrl, string? iconProxyUrl)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Url = url;
|
|
||||||
IconUrl = iconUrl;
|
|
||||||
IconProxyUrl = iconProxyUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name ?? "<unnamed author>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EmbedAuthor
|
|
||||||
{
|
{
|
||||||
public static EmbedAuthor Parse(JsonElement json)
|
public static EmbedAuthor Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
@ -6,26 +5,10 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure
|
// https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure
|
||||||
public partial class EmbedField
|
public record EmbedField(
|
||||||
{
|
string Name,
|
||||||
public string Name { get; }
|
string Value,
|
||||||
|
bool IsInline)
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public bool IsInline { get; }
|
|
||||||
|
|
||||||
public EmbedField(string name, string value, bool isInline)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Value = value;
|
|
||||||
IsInline = isInline;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"{Name} | {Value}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EmbedField
|
|
||||||
{
|
{
|
||||||
public static EmbedField Parse(JsonElement json)
|
public static EmbedField Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
@ -6,26 +5,10 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure
|
// https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure
|
||||||
public partial class EmbedFooter
|
public record EmbedFooter(
|
||||||
{
|
string Text,
|
||||||
public string Text { get; }
|
string? IconUrl,
|
||||||
|
string? IconProxyUrl)
|
||||||
public string? IconUrl { get; }
|
|
||||||
|
|
||||||
public string? IconProxyUrl { get; }
|
|
||||||
|
|
||||||
public EmbedFooter(string text, string? iconUrl, string? iconProxyUrl)
|
|
||||||
{
|
|
||||||
Text = text;
|
|
||||||
IconUrl = iconUrl;
|
|
||||||
IconProxyUrl = iconProxyUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EmbedFooter
|
|
||||||
{
|
{
|
||||||
public static EmbedFooter Parse(JsonElement json)
|
public static EmbedFooter Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,26 +4,11 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure
|
// https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure
|
||||||
public partial class EmbedImage
|
public record EmbedImage(
|
||||||
{
|
string? Url,
|
||||||
public string? Url { get; }
|
string? ProxyUrl,
|
||||||
|
int? Width,
|
||||||
public string? ProxyUrl { get; }
|
int? Height)
|
||||||
|
|
||||||
public int? Width { get; }
|
|
||||||
|
|
||||||
public int? Height { get; }
|
|
||||||
|
|
||||||
public EmbedImage(string? url, string? proxyUrl, int? width, int? height)
|
|
||||||
{
|
|
||||||
Url = url;
|
|
||||||
ProxyUrl = proxyUrl;
|
|
||||||
Height = height;
|
|
||||||
Width = width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class EmbedImage
|
|
||||||
{
|
{
|
||||||
public static EmbedImage Parse(JsonElement json)
|
public static EmbedImage Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using DiscordChatExporter.Core.Utils;
|
using DiscordChatExporter.Core.Utils;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
public partial class PlainImageEmbedProjection
|
public record PlainImageEmbedProjection(string Url)
|
||||||
{
|
|
||||||
public string Url { get; }
|
|
||||||
|
|
||||||
public PlainImageEmbedProjection(string url) => Url = url;
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class PlainImageEmbedProjection
|
|
||||||
{
|
{
|
||||||
public static PlainImageEmbedProjection? TryResolve(Embed embed)
|
public static PlainImageEmbedProjection? TryResolve(Embed embed)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Text.RegularExpressions;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
public partial class SpotifyTrackEmbedProjection
|
public partial record SpotifyTrackEmbedProjection(string TrackId)
|
||||||
{
|
{
|
||||||
public string TrackId { get; }
|
|
||||||
|
|
||||||
public string Url => $"https://open.spotify.com/embed/track/{TrackId}";
|
public string Url => $"https://open.spotify.com/embed/track/{TrackId}";
|
||||||
|
|
||||||
public SpotifyTrackEmbedProjection(string trackId) => TrackId = trackId;
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SpotifyTrackEmbedProjection
|
public partial record SpotifyTrackEmbedProjection
|
||||||
{
|
{
|
||||||
private static string? TryParseTrackId(string embedUrl)
|
private static string? TryParseTrackId(string embedUrl)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Text.RegularExpressions;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
namespace DiscordChatExporter.Core.Discord.Data.Embeds
|
||||||
{
|
{
|
||||||
public partial class YouTubeVideoEmbedProjection
|
public partial record YouTubeVideoEmbedProjection(string VideoId)
|
||||||
{
|
{
|
||||||
public string VideoId { get; }
|
|
||||||
|
|
||||||
public string Url => $"https://www.youtube.com/embed/{VideoId}";
|
public string Url => $"https://www.youtube.com/embed/{VideoId}";
|
||||||
|
|
||||||
public YouTubeVideoEmbedProjection(string videoId) => VideoId = videoId;
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class YouTubeVideoEmbedProjection
|
public partial record YouTubeVideoEmbedProjection
|
||||||
{
|
{
|
||||||
// Adapted from YoutubeExplode
|
// Adapted from YoutubeExplode
|
||||||
// https://github.com/Tyrrrz/YoutubeExplode/blob/5be164be20019783913f76fcc98f18c65aebe9f0/YoutubeExplode/Videos/VideoId.cs#L34-L64
|
// https://github.com/Tyrrrz/YoutubeExplode/blob/5be164be20019783913f76fcc98f18c65aebe9f0/YoutubeExplode/Videos/VideoId.cs#L34-L64
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Utils;
|
using DiscordChatExporter.Core.Utils;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
@ -9,36 +8,21 @@ using Tyrrrz.Extensions;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/emoji#emoji-object
|
// https://discord.com/developers/docs/resources/emoji#emoji-object
|
||||||
public partial class Emoji
|
public partial record Emoji(
|
||||||
{
|
|
||||||
// Only present on custom emoji
|
// Only present on custom emoji
|
||||||
public string? Id { get; }
|
string? Id,
|
||||||
|
|
||||||
// Name of custom emoji (e.g. LUL) or actual representation of standard emoji (e.g. 🙂)
|
// Name of custom emoji (e.g. LUL) or actual representation of standard emoji (e.g. 🙂)
|
||||||
public string Name { get; }
|
string Name,
|
||||||
|
bool IsAnimated,
|
||||||
|
string ImageUrl)
|
||||||
|
{
|
||||||
// Name of custom emoji (e.g. LUL) or name of standard emoji (e.g. slight_smile)
|
// Name of custom emoji (e.g. LUL) or name of standard emoji (e.g. slight_smile)
|
||||||
public string Code => !string.IsNullOrWhiteSpace(Id)
|
public string Code => !string.IsNullOrWhiteSpace(Id)
|
||||||
? Name
|
? Name
|
||||||
: EmojiIndex.TryGetCode(Name) ?? Name;
|
: EmojiIndex.TryGetCode(Name) ?? Name;
|
||||||
|
|
||||||
public bool IsAnimated { get; }
|
|
||||||
|
|
||||||
public string ImageUrl { get; }
|
|
||||||
|
|
||||||
public Emoji(string? id, string name, bool isAnimated, string imageUrl)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
IsAnimated = isAnimated;
|
|
||||||
ImageUrl = imageUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Emoji
|
public partial record Emoji
|
||||||
{
|
{
|
||||||
private static string GetTwemojiName(string name) => name
|
private static string GetTwemojiName(string name) => name
|
||||||
.GetRunes()
|
.GetRunes()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Text.Json;
|
||||||
using System.Text.Json;
|
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
@ -7,26 +6,7 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/guild#guild-object
|
// https://discord.com/developers/docs/resources/guild#guild-object
|
||||||
public partial class Guild : IHasId
|
public record Guild(Snowflake Id, string Name, string IconUrl) : IHasId
|
||||||
{
|
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public string IconUrl { get; }
|
|
||||||
|
|
||||||
public Guild(Snowflake id, string name, string iconUrl)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
IconUrl = iconUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Guild
|
|
||||||
{
|
{
|
||||||
public static Guild DirectMessages { get; } = new(
|
public static Guild DirectMessages { get; } = new(
|
||||||
Snowflake.Zero,
|
Snowflake.Zero,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
|
@ -10,28 +9,15 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/guild#guild-member-object
|
// https://discord.com/developers/docs/resources/guild#guild-member-object
|
||||||
public partial class Member : IHasId
|
public partial record Member(
|
||||||
|
User User,
|
||||||
|
string Nick,
|
||||||
|
IReadOnlyList<Snowflake> RoleIds) : IHasId
|
||||||
{
|
{
|
||||||
public Snowflake Id => User.Id;
|
public Snowflake Id => User.Id;
|
||||||
|
|
||||||
public User User { get; }
|
|
||||||
|
|
||||||
public string Nick { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<Snowflake> RoleIds { get; }
|
|
||||||
|
|
||||||
public Member(User user, string nick, IReadOnlyList<Snowflake> roleIds)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
Nick = nick;
|
|
||||||
RoleIds = roleIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Nick;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Member
|
public partial record Member
|
||||||
{
|
{
|
||||||
public static Member CreateForUser(User user) => new(
|
public static Member CreateForUser(User user) => new(
|
||||||
user,
|
user,
|
||||||
|
@ -44,9 +30,12 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
var user = json.GetProperty("user").Pipe(User.Parse);
|
var user = json.GetProperty("user").Pipe(User.Parse);
|
||||||
var nick = json.GetPropertyOrNull("nick")?.GetStringOrNull();
|
var nick = json.GetPropertyOrNull("nick")?.GetStringOrNull();
|
||||||
|
|
||||||
var roleIds =
|
var roleIds = json
|
||||||
json.GetPropertyOrNull("roles")?.EnumerateArray().Select(j => j.GetNonWhiteSpaceString()).Select(Snowflake.Parse).ToArray() ??
|
.GetPropertyOrNull("roles")?
|
||||||
Array.Empty<Snowflake>();
|
.EnumerateArray()
|
||||||
|
.Select(j => j.GetNonWhiteSpaceString())
|
||||||
|
.Select(Snowflake.Parse)
|
||||||
|
.ToArray() ?? Array.Empty<Snowflake>();
|
||||||
|
|
||||||
return new Member(
|
return new Member(
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
|
@ -11,73 +10,21 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#message-object
|
// https://discord.com/developers/docs/resources/channel#message-object
|
||||||
public partial class Message : IHasId
|
public record Message(
|
||||||
{
|
Snowflake Id,
|
||||||
public Snowflake Id { get; }
|
MessageKind Kind,
|
||||||
|
User Author,
|
||||||
public MessageKind Kind { get; }
|
DateTimeOffset Timestamp,
|
||||||
|
DateTimeOffset? EditedTimestamp,
|
||||||
public User Author { get; }
|
DateTimeOffset? CallEndedTimestamp,
|
||||||
|
bool IsPinned,
|
||||||
public DateTimeOffset Timestamp { get; }
|
string Content,
|
||||||
|
IReadOnlyList<Attachment> Attachments,
|
||||||
public DateTimeOffset? EditedTimestamp { get; }
|
IReadOnlyList<Embed> Embeds,
|
||||||
|
IReadOnlyList<Reaction> Reactions,
|
||||||
public DateTimeOffset? CallEndedTimestamp { get; }
|
IReadOnlyList<User> MentionedUsers,
|
||||||
|
MessageReference? Reference,
|
||||||
public bool IsPinned { get; }
|
Message? ReferencedMessage) : IHasId
|
||||||
|
|
||||||
public string Content { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<Attachment> Attachments { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<Embed> Embeds { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<Reaction> Reactions { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<User> MentionedUsers { get; }
|
|
||||||
|
|
||||||
public MessageReference? Reference { get; }
|
|
||||||
|
|
||||||
public Message? ReferencedMessage { get; }
|
|
||||||
|
|
||||||
public Message(
|
|
||||||
Snowflake id,
|
|
||||||
MessageKind kind,
|
|
||||||
User author,
|
|
||||||
DateTimeOffset timestamp,
|
|
||||||
DateTimeOffset? editedTimestamp,
|
|
||||||
DateTimeOffset? callEndedTimestamp,
|
|
||||||
bool isPinned,
|
|
||||||
string content,
|
|
||||||
IReadOnlyList<Attachment> attachments,
|
|
||||||
IReadOnlyList<Embed> embeds,
|
|
||||||
IReadOnlyList<Reaction> reactions,
|
|
||||||
IReadOnlyList<User> mentionedUsers,
|
|
||||||
MessageReference? messageReference,
|
|
||||||
Message? referencedMessage)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Kind = kind;
|
|
||||||
Author = author;
|
|
||||||
Timestamp = timestamp;
|
|
||||||
EditedTimestamp = editedTimestamp;
|
|
||||||
CallEndedTimestamp = callEndedTimestamp;
|
|
||||||
IsPinned = isPinned;
|
|
||||||
Content = content;
|
|
||||||
Attachments = attachments;
|
|
||||||
Embeds = embeds;
|
|
||||||
Reactions = reactions;
|
|
||||||
MentionedUsers = mentionedUsers;
|
|
||||||
Reference = messageReference;
|
|
||||||
ReferencedMessage = referencedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Message
|
|
||||||
{
|
{
|
||||||
public static Message Parse(JsonElement json)
|
public static Message Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
@ -85,8 +32,9 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
var author = json.GetProperty("author").Pipe(User.Parse);
|
var author = json.GetProperty("author").Pipe(User.Parse);
|
||||||
var timestamp = json.GetProperty("timestamp").GetDateTimeOffset();
|
var timestamp = json.GetProperty("timestamp").GetDateTimeOffset();
|
||||||
var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffset();
|
var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffset();
|
||||||
var callEndedTimestamp = json.GetPropertyOrNull("call")?.GetPropertyOrNull("ended_timestamp")?.GetDateTimeOffset();
|
var callEndedTimestamp = json.GetPropertyOrNull("call")?.GetPropertyOrNull("ended_timestamp")
|
||||||
var kind = (MessageKind) json.GetProperty("type").GetInt32();
|
?.GetDateTimeOffset();
|
||||||
|
var kind = (MessageKind)json.GetProperty("type").GetInt32();
|
||||||
var isPinned = json.GetPropertyOrNull("pinned")?.GetBoolean() ?? false;
|
var isPinned = json.GetPropertyOrNull("pinned")?.GetBoolean() ?? false;
|
||||||
var messageReference = json.GetPropertyOrNull("message_reference")?.Pipe(MessageReference.Parse);
|
var messageReference = json.GetPropertyOrNull("message_reference")?.Pipe(MessageReference.Parse);
|
||||||
var referencedMessage = json.GetPropertyOrNull("referenced_message")?.Pipe(Parse);
|
var referencedMessage = json.GetPropertyOrNull("referenced_message")?.Pipe(Parse);
|
||||||
|
@ -96,7 +44,7 @@ namespace DiscordChatExporter.Core.Discord.Data
|
||||||
MessageKind.RecipientAdd => "Added a recipient.",
|
MessageKind.RecipientAdd => "Added a recipient.",
|
||||||
MessageKind.RecipientRemove => "Removed a recipient.",
|
MessageKind.RecipientRemove => "Removed a recipient.",
|
||||||
MessageKind.Call =>
|
MessageKind.Call =>
|
||||||
$"Started a call that lasted {callEndedTimestamp?.Pipe(t => t - timestamp).Pipe(t => (int) t.TotalMinutes) ?? 0} minutes.",
|
$"Started a call that lasted {callEndedTimestamp?.Pipe(t => t - timestamp).Pipe(t => (int)t.TotalMinutes) ?? 0} minutes.",
|
||||||
MessageKind.ChannelNameChange => "Changed the channel name.",
|
MessageKind.ChannelNameChange => "Changed the channel name.",
|
||||||
MessageKind.ChannelIconChange => "Changed the channel icon.",
|
MessageKind.ChannelIconChange => "Changed the channel icon.",
|
||||||
MessageKind.ChannelPinnedMessage => "Pinned a message.",
|
MessageKind.ChannelPinnedMessage => "Pinned a message.",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using JsonExtensions.Reading;
|
using JsonExtensions.Reading;
|
||||||
|
@ -6,26 +5,7 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure
|
// https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure
|
||||||
public partial class MessageReference
|
public record MessageReference(Snowflake? MessageId, Snowflake? ChannelId, Snowflake? GuildId)
|
||||||
{
|
|
||||||
public Snowflake? MessageId { get; }
|
|
||||||
|
|
||||||
public Snowflake? ChannelId { get; }
|
|
||||||
|
|
||||||
public Snowflake? GuildId { get; }
|
|
||||||
|
|
||||||
public MessageReference(Snowflake? messageId, Snowflake? channelId, Snowflake? guildId)
|
|
||||||
{
|
|
||||||
MessageId = messageId;
|
|
||||||
ChannelId = channelId;
|
|
||||||
GuildId = guildId;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => MessageId?.ToString() ?? "<unknown reference>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class MessageReference
|
|
||||||
{
|
{
|
||||||
public static MessageReference Parse(JsonElement json)
|
public static MessageReference Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,27 +1,10 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Text.Json;
|
||||||
using System.Text.Json;
|
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/channel#reaction-object
|
// https://discord.com/developers/docs/resources/channel#reaction-object
|
||||||
public partial class Reaction
|
public record Reaction(Emoji Emoji, int Count)
|
||||||
{
|
|
||||||
public Emoji Emoji { get; }
|
|
||||||
|
|
||||||
public int Count { get; }
|
|
||||||
|
|
||||||
public Reaction(Emoji emoji, int count)
|
|
||||||
{
|
|
||||||
Emoji = emoji;
|
|
||||||
Count = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"{Emoji} ({Count})";
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Reaction
|
|
||||||
{
|
{
|
||||||
public static Reaction Parse(JsonElement json)
|
public static Reaction Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Drawing;
|
||||||
using System.Drawing;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
@ -8,33 +7,7 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/topics/permissions#role-object
|
// https://discord.com/developers/docs/topics/permissions#role-object
|
||||||
public partial class Role : IHasId
|
public record Role(Snowflake Id, string Name, int Position, Color? Color) : IHasId
|
||||||
{
|
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public int Position { get; }
|
|
||||||
|
|
||||||
public Color? Color { get; }
|
|
||||||
|
|
||||||
public Role(
|
|
||||||
Snowflake id,
|
|
||||||
string name,
|
|
||||||
int position,
|
|
||||||
Color? color)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
Position = position;
|
|
||||||
Color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class Role
|
|
||||||
{
|
{
|
||||||
public static Role Parse(JsonElement json)
|
public static Role Parse(JsonElement json)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DiscordChatExporter.Core.Discord.Data.Common;
|
using DiscordChatExporter.Core.Discord.Data.Common;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
|
@ -8,41 +7,19 @@ using JsonExtensions.Reading;
|
||||||
namespace DiscordChatExporter.Core.Discord.Data
|
namespace DiscordChatExporter.Core.Discord.Data
|
||||||
{
|
{
|
||||||
// https://discord.com/developers/docs/resources/user#user-object
|
// https://discord.com/developers/docs/resources/user#user-object
|
||||||
public partial class User : IHasId
|
public partial record User(
|
||||||
|
Snowflake Id,
|
||||||
|
bool IsBot,
|
||||||
|
int Discriminator,
|
||||||
|
string Name,
|
||||||
|
string AvatarUrl) : IHasId
|
||||||
{
|
{
|
||||||
public Snowflake Id { get; }
|
|
||||||
|
|
||||||
public bool IsBot { get; }
|
|
||||||
|
|
||||||
public int Discriminator { get; }
|
|
||||||
|
|
||||||
public string DiscriminatorFormatted => $"{Discriminator:0000}";
|
public string DiscriminatorFormatted => $"{Discriminator:0000}";
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public string FullName => $"{Name}#{DiscriminatorFormatted}";
|
public string FullName => $"{Name}#{DiscriminatorFormatted}";
|
||||||
|
|
||||||
public string AvatarUrl { get; }
|
|
||||||
|
|
||||||
public User(
|
|
||||||
Snowflake id,
|
|
||||||
bool isBot,
|
|
||||||
int discriminator,
|
|
||||||
string name,
|
|
||||||
string avatarUrl)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
IsBot = isBot;
|
|
||||||
Discriminator = discriminator;
|
|
||||||
Name = name;
|
|
||||||
AvatarUrl = avatarUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => FullName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class User
|
public partial record User
|
||||||
{
|
{
|
||||||
private static string GetDefaultAvatarUrl(int discriminator) =>
|
private static string GetDefaultAvatarUrl(int discriminator) =>
|
||||||
$"https://cdn.discordapp.com/embed/avatars/{discriminator % 5}.png";
|
$"https://cdn.discordapp.com/embed/avatars/{discriminator % 5}.png";
|
||||||
|
|
|
@ -5,29 +5,20 @@ using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Discord
|
namespace DiscordChatExporter.Core.Discord
|
||||||
{
|
{
|
||||||
public readonly partial struct Snowflake
|
public readonly record struct Snowflake(ulong Value)
|
||||||
{
|
{
|
||||||
public ulong Value { get; }
|
|
||||||
|
|
||||||
public Snowflake(ulong value) => Value = value;
|
|
||||||
|
|
||||||
public DateTimeOffset ToDate() => DateTimeOffset.FromUnixTimeMilliseconds(
|
public DateTimeOffset ToDate() => DateTimeOffset.FromUnixTimeMilliseconds(
|
||||||
(long) ((Value >> 22) + 1420070400000UL)
|
(long)((Value >> 22) + 1420070400000UL)
|
||||||
).ToLocalTime();
|
).ToLocalTime();
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public override string ToString() => Value.ToString(CultureInfo.InvariantCulture);
|
public override string ToString() => Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct Snowflake
|
|
||||||
{
|
|
||||||
public static Snowflake Zero { get; } = new(0);
|
public static Snowflake Zero { get; } = new(0);
|
||||||
|
|
||||||
public static Snowflake FromDate(DateTimeOffset date)
|
public static Snowflake FromDate(DateTimeOffset date) => new(
|
||||||
{
|
((ulong)date.ToUnixTimeMilliseconds() - 1420070400000UL) << 22
|
||||||
var value = ((ulong) date.ToUnixTimeMilliseconds() - 1420070400000UL) << 22;
|
);
|
||||||
return new Snowflake(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Snowflake? TryParse(string? str, IFormatProvider? formatProvider = null)
|
public static Snowflake? TryParse(string? str, IFormatProvider? formatProvider = null)
|
||||||
{
|
{
|
||||||
|
@ -35,8 +26,7 @@ namespace DiscordChatExporter.Core.Discord
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// As number
|
// As number
|
||||||
if (Regex.IsMatch(str, @"^\d+$") &&
|
if (Regex.IsMatch(str, @"^\d+$") && ulong.TryParse(str, NumberStyles.Number, formatProvider, out var value))
|
||||||
ulong.TryParse(str, NumberStyles.Number, formatProvider, out var value))
|
|
||||||
{
|
{
|
||||||
return new Snowflake(value);
|
return new Snowflake(value);
|
||||||
}
|
}
|
||||||
|
@ -55,19 +45,4 @@ namespace DiscordChatExporter.Core.Discord
|
||||||
|
|
||||||
public static Snowflake Parse(string str) => Parse(str, null);
|
public static Snowflake Parse(string str) => Parse(str, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial struct Snowflake : IComparable<Snowflake>, IEquatable<Snowflake>
|
|
||||||
{
|
|
||||||
public int CompareTo(Snowflake other) => Value.CompareTo(other.Value);
|
|
||||||
|
|
||||||
public bool Equals(Snowflake other) => CompareTo(other) == 0;
|
|
||||||
|
|
||||||
public override bool Equals(object? obj) => obj is Snowflake other && Equals(other);
|
|
||||||
|
|
||||||
public override int GetHashCode() => Value.GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(Snowflake left, Snowflake right) => left.Equals(right);
|
|
||||||
|
|
||||||
public static bool operator !=(Snowflake left, Snowflake right) => !(left == right);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -10,76 +10,35 @@ using DiscordChatExporter.Core.Utils;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Exporting
|
namespace DiscordChatExporter.Core.Exporting
|
||||||
{
|
{
|
||||||
public partial class ExportRequest
|
public partial record ExportRequest(
|
||||||
|
Guild Guild,
|
||||||
|
Channel Channel,
|
||||||
|
string OutputPath,
|
||||||
|
ExportFormat Format,
|
||||||
|
Snowflake? After,
|
||||||
|
Snowflake? Before,
|
||||||
|
PartitionLimit PartitionLimit,
|
||||||
|
MessageFilter MessageFilter,
|
||||||
|
bool ShouldDownloadMedia,
|
||||||
|
bool ShouldReuseMedia,
|
||||||
|
string DateFormat)
|
||||||
{
|
{
|
||||||
public Guild Guild { get; }
|
private string? _outputBaseFilePath;
|
||||||
|
public string OutputBaseFilePath => _outputBaseFilePath ??= GetOutputBaseFilePath(
|
||||||
|
Guild,
|
||||||
|
Channel,
|
||||||
|
OutputPath,
|
||||||
|
Format,
|
||||||
|
After,
|
||||||
|
Before
|
||||||
|
);
|
||||||
|
|
||||||
public Channel Channel { get; }
|
public string OutputBaseDirPath => Path.GetDirectoryName(OutputBaseFilePath) ?? OutputPath;
|
||||||
|
|
||||||
public string OutputPath { get; }
|
public string OutputMediaDirPath => $"{OutputBaseFilePath}_Files{Path.DirectorySeparatorChar}";
|
||||||
|
|
||||||
public string OutputBaseFilePath { get; }
|
|
||||||
|
|
||||||
public string OutputBaseDirPath { get; }
|
|
||||||
|
|
||||||
public string OutputMediaDirPath { get; }
|
|
||||||
|
|
||||||
public ExportFormat Format { get; }
|
|
||||||
|
|
||||||
public Snowflake? After { get; }
|
|
||||||
|
|
||||||
public Snowflake? Before { get; }
|
|
||||||
|
|
||||||
public PartitionLimit PartitionLimit { get; }
|
|
||||||
|
|
||||||
public MessageFilter MessageFilter { get; }
|
|
||||||
|
|
||||||
public bool ShouldDownloadMedia { get; }
|
|
||||||
|
|
||||||
public bool ShouldReuseMedia { get; }
|
|
||||||
|
|
||||||
public string DateFormat { get; }
|
|
||||||
|
|
||||||
public ExportRequest(
|
|
||||||
Guild guild,
|
|
||||||
Channel channel,
|
|
||||||
string outputPath,
|
|
||||||
ExportFormat format,
|
|
||||||
Snowflake? after,
|
|
||||||
Snowflake? before,
|
|
||||||
PartitionLimit partitionLimit,
|
|
||||||
MessageFilter messageFilter,
|
|
||||||
bool shouldDownloadMedia,
|
|
||||||
bool shouldReuseMedia,
|
|
||||||
string dateFormat)
|
|
||||||
{
|
|
||||||
Guild = guild;
|
|
||||||
Channel = channel;
|
|
||||||
OutputPath = outputPath;
|
|
||||||
Format = format;
|
|
||||||
After = after;
|
|
||||||
Before = before;
|
|
||||||
PartitionLimit = partitionLimit;
|
|
||||||
MessageFilter = messageFilter;
|
|
||||||
ShouldDownloadMedia = shouldDownloadMedia;
|
|
||||||
ShouldReuseMedia = shouldReuseMedia;
|
|
||||||
DateFormat = dateFormat;
|
|
||||||
|
|
||||||
OutputBaseFilePath = GetOutputBaseFilePath(
|
|
||||||
guild,
|
|
||||||
channel,
|
|
||||||
outputPath,
|
|
||||||
format,
|
|
||||||
after,
|
|
||||||
before
|
|
||||||
);
|
|
||||||
|
|
||||||
OutputBaseDirPath = Path.GetDirectoryName(OutputBaseFilePath) ?? OutputPath;
|
|
||||||
OutputMediaDirPath = $"{OutputBaseFilePath}_Files{Path.DirectorySeparatorChar}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ExportRequest
|
public partial record ExportRequest
|
||||||
{
|
{
|
||||||
private static string GetOutputBaseFilePath(
|
private static string GetOutputBaseFilePath(
|
||||||
Guild guild,
|
Guild guild,
|
||||||
|
@ -140,17 +99,17 @@ namespace DiscordChatExporter.Core.Exporting
|
||||||
// Both 'after' and 'before' are set
|
// Both 'after' and 'before' are set
|
||||||
if (after is not null && before is not null)
|
if (after is not null && before is not null)
|
||||||
{
|
{
|
||||||
buffer.Append($"{after?.ToDate():yyyy-MM-dd} to {before?.ToDate():yyyy-MM-dd}");
|
buffer.Append($"{after.Value.ToDate():yyyy-MM-dd} to {before.Value.ToDate():yyyy-MM-dd}");
|
||||||
}
|
}
|
||||||
// Only 'after' is set
|
// Only 'after' is set
|
||||||
else if (after is not null)
|
else if (after is not null)
|
||||||
{
|
{
|
||||||
buffer.Append($"after {after?.ToDate():yyyy-MM-dd}");
|
buffer.Append($"after {after.Value.ToDate():yyyy-MM-dd}");
|
||||||
}
|
}
|
||||||
// Only 'before' is set
|
// Only 'before' is set
|
||||||
else
|
else if (before is not null)
|
||||||
{
|
{
|
||||||
buffer.Append($"before {before?.ToDate():yyyy-MM-dd}");
|
buffer.Append($"before {before.Value.ToDate():yyyy-MM-dd}");
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.Append(")");
|
buffer.Append(")");
|
||||||
|
|
|
@ -1,38 +1,24 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using DiscordChatExporter.Core.Utils;
|
||||||
using DiscordChatExporter.Core.Utils;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
{
|
{
|
||||||
internal class EmojiNode : MarkdownNode
|
internal record EmojiNode(
|
||||||
{
|
|
||||||
// Only present on custom emoji
|
// Only present on custom emoji
|
||||||
public string? Id { get; }
|
string? Id,
|
||||||
|
|
||||||
// Name of custom emoji (e.g. LUL) or actual representation of standard emoji (e.g. 🙂)
|
// Name of custom emoji (e.g. LUL) or actual representation of standard emoji (e.g. 🙂)
|
||||||
public string Name { get; }
|
string Name,
|
||||||
|
bool IsAnimated) : MarkdownNode
|
||||||
|
{
|
||||||
// Name of custom emoji (e.g. LUL) or name of standard emoji (e.g. slight_smile)
|
// Name of custom emoji (e.g. LUL) or name of standard emoji (e.g. slight_smile)
|
||||||
public string Code => !string.IsNullOrWhiteSpace(Id)
|
public string Code => !string.IsNullOrWhiteSpace(Id)
|
||||||
? Name
|
? Name
|
||||||
: EmojiIndex.TryGetCode(Name) ?? Name;
|
: EmojiIndex.TryGetCode(Name) ?? Name;
|
||||||
|
|
||||||
public bool IsAnimated { get; }
|
|
||||||
|
|
||||||
public bool IsCustomEmoji => !string.IsNullOrWhiteSpace(Id);
|
public bool IsCustomEmoji => !string.IsNullOrWhiteSpace(Id);
|
||||||
|
|
||||||
public EmojiNode(string? id, string name, bool isAnimated)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Name = name;
|
|
||||||
IsAnimated = isAnimated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmojiNode(string name)
|
public EmojiNode(string name)
|
||||||
: this(null, name, false)
|
: this(null, name, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"<Emoji> {Name}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,29 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
{
|
{
|
||||||
internal class FormattingNode : MarkdownNode
|
internal record FormattingNode(FormattingKind Kind, IReadOnlyList<MarkdownNode> Children) : MarkdownNode;
|
||||||
{
|
|
||||||
public FormattingKind Kind { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<MarkdownNode> Children { get; }
|
|
||||||
|
|
||||||
public FormattingNode(FormattingKind kind, IReadOnlyList<MarkdownNode> children)
|
|
||||||
{
|
|
||||||
Kind = kind;
|
|
||||||
Children = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var childrenFormatted = Children.Count == 1
|
|
||||||
? Children.Single().ToString()
|
|
||||||
: "+" + Children.Count;
|
|
||||||
|
|
||||||
return $"<{Kind}> ({childrenFormatted})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,17 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
|
||||||
{
|
{
|
||||||
internal class InlineCodeBlockNode : MarkdownNode
|
internal record InlineCodeBlockNode(string Code) : MarkdownNode;
|
||||||
{
|
|
||||||
public string Code { get; }
|
|
||||||
|
|
||||||
public InlineCodeBlockNode(string code)
|
|
||||||
{
|
|
||||||
Code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"<Code> {Code}";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,34 +1,14 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
{
|
{
|
||||||
internal class LinkNode : MarkdownNode
|
internal record LinkNode(
|
||||||
|
string Url,
|
||||||
|
IReadOnlyList<MarkdownNode> Children) : MarkdownNode
|
||||||
{
|
{
|
||||||
public string Url { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<MarkdownNode> Children { get; }
|
|
||||||
|
|
||||||
public LinkNode(string url, IReadOnlyList<MarkdownNode> children)
|
|
||||||
{
|
|
||||||
Url = url;
|
|
||||||
Children = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LinkNode(string url)
|
public LinkNode(string url)
|
||||||
: this(url, new[] {new TextNode(url)})
|
: this(url, new[] { new TextNode(url) })
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var childrenFormatted = Children.Count == 1
|
|
||||||
? Children.Single().ToString()
|
|
||||||
: "+" + Children.Count;
|
|
||||||
|
|
||||||
return $"<Link> ({childrenFormatted})";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
{
|
{
|
||||||
internal abstract class MarkdownNode
|
internal abstract record MarkdownNode;
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,20 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
|
||||||
{
|
{
|
||||||
internal class MentionNode : MarkdownNode
|
internal record MentionNode(string Id, MentionKind Kind) : MarkdownNode;
|
||||||
{
|
|
||||||
public string Id { get; }
|
|
||||||
|
|
||||||
public MentionKind Kind { get; }
|
|
||||||
|
|
||||||
public MentionNode(string id, MentionKind kind)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Kind = kind;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"<{Kind} mention> {Id}";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,20 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
|
||||||
{
|
{
|
||||||
internal class MultiLineCodeBlockNode : MarkdownNode
|
internal record MultiLineCodeBlockNode(string Language, string Code) : MarkdownNode;
|
||||||
{
|
|
||||||
public string Language { get; }
|
|
||||||
|
|
||||||
public string Code { get; }
|
|
||||||
|
|
||||||
public MultiLineCodeBlockNode(string language, string code)
|
|
||||||
{
|
|
||||||
Language = language;
|
|
||||||
Code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => $"<{Language}> {Code}";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -2,23 +2,10 @@
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown.Parsing
|
namespace DiscordChatExporter.Core.Markdown.Parsing
|
||||||
{
|
{
|
||||||
internal readonly struct StringPart
|
internal readonly record struct StringPart(string Target, int StartIndex, int Length)
|
||||||
{
|
{
|
||||||
public string Target { get; }
|
|
||||||
|
|
||||||
public int StartIndex { get; }
|
|
||||||
|
|
||||||
public int Length { get; }
|
|
||||||
|
|
||||||
public int EndIndex => StartIndex + Length;
|
public int EndIndex => StartIndex + Length;
|
||||||
|
|
||||||
public StringPart(string target, int startIndex, int length)
|
|
||||||
{
|
|
||||||
Target = target;
|
|
||||||
StartIndex = startIndex;
|
|
||||||
Length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringPart(string target)
|
public StringPart(string target)
|
||||||
: this(target, 0, target.Length)
|
: this(target, 0, target.Length)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
|
||||||
{
|
{
|
||||||
internal class TextNode : MarkdownNode
|
internal record TextNode(string Text) : MarkdownNode;
|
||||||
{
|
|
||||||
public string Text { get; }
|
|
||||||
|
|
||||||
public TextNode(string text)
|
|
||||||
{
|
|
||||||
Text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Text;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,15 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Markdown
|
namespace DiscordChatExporter.Core.Markdown
|
||||||
{
|
{
|
||||||
internal class UnixTimestampNode : MarkdownNode
|
internal record UnixTimestampNode(DateTimeOffset Value) : MarkdownNode;
|
||||||
{
|
|
||||||
public DateTimeOffset Value { get; }
|
|
||||||
|
|
||||||
public UnixTimestampNode(DateTimeOffset value) => Value = value;
|
|
||||||
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
public override string ToString() => Value.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
// ReSharper disable CheckNamespace
|
|
||||||
// TODO: remove after moving to .NET 5
|
|
||||||
|
|
||||||
namespace System.Runtime.CompilerServices
|
|
||||||
{
|
|
||||||
internal static class IsExternalInit
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
using DiscordChatExporter.Core.Discord;
|
using DiscordChatExporter.Core.Discord;
|
||||||
using DiscordChatExporter.Core.Exporting;
|
using DiscordChatExporter.Core.Exporting;
|
||||||
using DiscordChatExporter.Core.Exporting.Partitioning;
|
|
||||||
using Tyrrrz.Settings;
|
using Tyrrrz.Settings;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Gui.Services
|
namespace DiscordChatExporter.Gui.Services
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue