using System; using System.Drawing; using System.Linq; using DiscordChatExporter.Core.Models; using DiscordChatExporter.Core.Services.Internal; using Newtonsoft.Json.Linq; using Tyrrrz.Extensions; namespace DiscordChatExporter.Core.Services { public partial class DataService { private User ParseUser(JToken json) { var id = json["id"]!.Value(); var discriminator = json["discriminator"]!.Value(); var name = json["username"]!.Value(); var avatarHash = json["avatar"]!.Value(); var isBot = json["bot"]?.Value() ?? false; return new User(id, discriminator, name, avatarHash, isBot); } private Member ParseMember(JToken json) { var userId = ParseUser(json["user"]!).Id; var nick = json["nick"]?.Value(); var roles = json["roles"]!.Select(jt => jt.Value()).ToArray(); return new Member(userId, nick, roles); } private Guild ParseGuild(JToken json) { var id = json["id"]!.Value(); var name = json["name"]!.Value(); var iconHash = json["icon"]!.Value(); var roles = json["roles"]!.Select(ParseRole).ToList(); return new Guild(id, name, roles, iconHash); } private Channel ParseChannel(JToken json) { // Get basic data var id = json["id"]!.Value(); var parentId = json["parent_id"]?.Value(); var type = (ChannelType) json["type"]!.Value(); var topic = json["topic"]?.Value(); // Try to extract guild ID var guildId = json["guild_id"]?.Value(); // If the guild ID is blank, it's direct messages if (string.IsNullOrWhiteSpace(guildId)) guildId = Guild.DirectMessages.Id; // Try to extract name var name = json["name"]?.Value(); // If the name is blank, it's direct messages if (string.IsNullOrWhiteSpace(name)) name = json["recipients"]?.Select(ParseUser).Select(u => u.Name).JoinToString(", "); // If the name is still blank for some reason, fallback to ID // (blind fix to https://github.com/Tyrrrz/DiscordChatExporter/issues/227) if (string.IsNullOrWhiteSpace(name)) name = id; return new Channel(id, parentId, guildId, name, topic, type); } private Role ParseRole(JToken json) { var id = json["id"]!.Value(); var name = json["name"]!.Value(); var color = json["color"]!.Value(); var position = json["position"]!.Value(); return new Role(id, name, Color.FromArgb(color), position); } private Attachment ParseAttachment(JToken json) { var id = json["id"]!.Value(); var url = json["url"]!.Value(); var width = json["width"]?.Value(); var height = json["height"]?.Value(); var fileName = json["filename"]!.Value(); var fileSizeBytes = json["size"]!.Value(); var fileSize = new FileSize(fileSizeBytes); return new Attachment(id, width, height, url, fileName, fileSize); } private EmbedAuthor ParseEmbedAuthor(JToken json) { var name = json["name"]?.Value(); var url = json["url"]?.Value(); var iconUrl = json["icon_url"]?.Value(); return new EmbedAuthor(name, url, iconUrl); } private EmbedField ParseEmbedField(JToken json) { var name = json["name"]!.Value(); var value = json["value"]!.Value(); var isInline = json["inline"]?.Value() ?? false; return new EmbedField(name, value, isInline); } private EmbedImage ParseEmbedImage(JToken json) { var url = json["url"]?.Value(); var width = json["width"]?.Value(); var height = json["height"]?.Value(); return new EmbedImage(url, width, height); } private EmbedFooter ParseEmbedFooter(JToken json) { var text = json["text"]!.Value(); var iconUrl = json["icon_url"]?.Value(); return new EmbedFooter(text, iconUrl); } private Embed ParseEmbed(JToken json) { // Get basic data var title = json["title"]?.Value(); var description = json["description"]?.Value(); var url = json["url"]?.Value(); var timestamp = json["timestamp"]?.Value().ToDateTimeOffset(); // Get color var color = json["color"] != null ? Color.FromArgb(json["color"]!.Value()).ResetAlpha() : default(Color?); // Get author var author = json["author"] != null ? ParseEmbedAuthor(json["author"]!) : null; // Get fields var fields = (json["fields"] ?? Enumerable.Empty()).Select(ParseEmbedField).ToArray(); // Get thumbnail var thumbnail = json["thumbnail"] != null ? ParseEmbedImage(json["thumbnail"]!) : null; // Get image var image = json["image"] != null ? ParseEmbedImage(json["image"]!) : null; // Get footer var footer = json["footer"] != null ? ParseEmbedFooter(json["footer"]!) : null; return new Embed(title, url, timestamp, color, author, description, fields, thumbnail, image, footer); } private Emoji ParseEmoji(JToken json) { var id = json["id"]?.Value(); var name = json["name"]!.Value(); var isAnimated = json["animated"]?.Value() ?? false; return new Emoji(id, name, isAnimated); } private Reaction ParseReaction(JToken json) { var count = json["count"]!.Value(); var emoji = ParseEmoji(json["emoji"]!); return new Reaction(count, emoji); } private Message ParseMessage(JToken json) { // Get basic data var id = json["id"]!.Value(); var channelId = json["channel_id"]!.Value(); var timestamp = json["timestamp"]!.Value().ToDateTimeOffset(); var editedTimestamp = json["edited_timestamp"]?.Value()?.ToDateTimeOffset(); var content = json["content"]!.Value(); var type = (MessageType) json["type"]!.Value(); // Workarounds for non-default types if (type == MessageType.RecipientAdd) content = "Added a recipient."; else if (type == MessageType.RecipientRemove) content = "Removed a recipient."; else if (type == MessageType.Call) content = "Started a call."; else if (type == MessageType.ChannelNameChange) content = "Changed the channel name."; else if (type == MessageType.ChannelIconChange) content = "Changed the channel icon."; else if (type == MessageType.ChannelPinnedMessage) content = "Pinned a message."; else if (type == MessageType.GuildMemberJoin) content = "Joined the server."; // Get author var author = ParseUser(json["author"]!); // Get attachments var attachments = (json["attachments"] ?? Enumerable.Empty()).Select(ParseAttachment).ToArray(); // Get embeds var embeds = (json["embeds"] ?? Enumerable.Empty()).Select(ParseEmbed).ToArray(); // Get reactions var reactions = (json["reactions"] ?? Enumerable.Empty()).Select(ParseReaction).ToArray(); // Get mentions var mentionedUsers = (json["mentions"] ?? Enumerable.Empty()).Select(ParseUser).ToArray(); // Get whether this message is pinned var isPinned = json["pinned"]!.Value(); return new Message(id, channelId, type, author, timestamp, editedTimestamp, isPinned, content, attachments, embeds, reactions, mentionedUsers); } } }