fedimbed: attempt to components v2 fedi posts
This commit is contained in:
		
							parent
							
								
									15f709ed13
								
							
						
					
					
						commit
						fd98ba3667
					
				
					 1 changed files with 184 additions and 444 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,3 @@
 | 
			
		|||
const {Message} = require("@projectdysnomia/dysnomia");
 | 
			
		||||
 | 
			
		||||
const fs = require("node:fs");
 | 
			
		||||
const httpSignature = require("@peertube/http-signature");
 | 
			
		||||
const {XMLParser} = require("fast-xml-parser");
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +9,6 @@ const InteractionCommand = require("#lib/interactionCommand.js");
 | 
			
		|||
 | 
			
		||||
const {Icons} = require("#util/constants.js");
 | 
			
		||||
const {MessageFlags, ApplicationCommandOptionTypes, Permissions} = require("#util/dconstants.js");
 | 
			
		||||
const {getUploadLimit} = require("#util/misc.js");
 | 
			
		||||
const {htmlToMarkdown} = require("#util/html.js");
 | 
			
		||||
 | 
			
		||||
const FRIENDLY_USERAGENT =
 | 
			
		||||
| 
						 | 
				
			
			@ -715,7 +712,7 @@ async function bluesky(msg, url, spoiler = false) {
 | 
			
		|||
  return {
 | 
			
		||||
    response: {
 | 
			
		||||
      flags: 1 << 15,
 | 
			
		||||
      components: [warnings.length > 0 ? warningText : false, container].filter((x) => !!x),
 | 
			
		||||
      components: [warnings.length > 0 && warningText, container].filter((x) => !!x),
 | 
			
		||||
      allowedMentions: {
 | 
			
		||||
        repliedUser: false,
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -910,18 +907,20 @@ async function getStatsAS(post) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
  const stats = [];
 | 
			
		||||
  if (replyCount > 0) stats.push(`\u21a9 ${statsFormatter.format(replyCount)}`);
 | 
			
		||||
  if (post.shares?.totalItems ?? 0 > 0) stats.push(`\ud83d\udd01 ${statsFormatter.format(post.shares.totalItems)}`);
 | 
			
		||||
  if (post.likes?.totalItems ?? 0 > 0) stats.push(`\u2665 ${statsFormatter.format(post.likes.totalItems)}`);
 | 
			
		||||
  if (replyCount > 0) stats.push(`${Icons.fedimbed.reply} ${statsFormatter.format(replyCount)}`);
 | 
			
		||||
  if (post.shares?.totalItems ?? 0 > 0)
 | 
			
		||||
    stats.push(`${Icons.fedimbed.repost} ${statsFormatter.format(post.shares.totalItems)}`);
 | 
			
		||||
  if (post.likes?.totalItems ?? 0 > 0)
 | 
			
		||||
    stats.push(`${Icons.fedimbed.like} ${statsFormatter.format(post.likes.totalItems)}`);
 | 
			
		||||
 | 
			
		||||
  return stats.join("\u3000");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getStatsMasto(post) {
 | 
			
		||||
  const stats = [];
 | 
			
		||||
  if (post.replies_count > 0) stats.push(`\u21a9 ${statsFormatter.format(post.replies_count)}`);
 | 
			
		||||
  if (post.reblogs_count > 0) stats.push(`\ud83d\udd01 ${statsFormatter.format(post.reblogs_count)}`);
 | 
			
		||||
  if (post.favourites_count > 0) stats.push(`\u2665 ${statsFormatter.format(post.favourites_count)}`);
 | 
			
		||||
  if (post.replies_count > 0) stats.push(`${Icons.fedimbed.reply} ${statsFormatter.format(post.replies_count)}`);
 | 
			
		||||
  if (post.reblogs_count > 0) stats.push(`${Icons.fedimbed.repost} ${statsFormatter.format(post.reblogs_count)}`);
 | 
			
		||||
  if (post.favourites_count > 0) stats.push(`${Icons.fedimbed.like} ${statsFormatter.format(post.favourites_count)}`);
 | 
			
		||||
 | 
			
		||||
  return stats.join("\u3000");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1061,61 +1060,26 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
    const attachments = postData.media_attachments ?? postData.files;
 | 
			
		||||
    if (attachments) {
 | 
			
		||||
      for (const attachment of attachments) {
 | 
			
		||||
        const contentType = await fetch(attachment.url, {
 | 
			
		||||
          method: "HEAD",
 | 
			
		||||
        }).then((res) => res.headers.get("Content-Type"));
 | 
			
		||||
 | 
			
		||||
        if (contentType) {
 | 
			
		||||
          if (contentType.startsWith("image/")) {
 | 
			
		||||
            images.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: contentType,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (contentType.startsWith("video/")) {
 | 
			
		||||
            videos.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: contentType,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (contentType.startsWith("audio/")) {
 | 
			
		||||
            audios.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: contentType,
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
        const type = attachment.type?.toLowerCase();
 | 
			
		||||
 | 
			
		||||
          const fileType =
 | 
			
		||||
            attachment.pleroma?.mime_type ?? type.indexOf("/") > -1
 | 
			
		||||
              ? type
 | 
			
		||||
              : type +
 | 
			
		||||
                "/" +
 | 
			
		||||
                (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg");
 | 
			
		||||
        if (type.startsWith("image")) {
 | 
			
		||||
          images.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: fileType,
 | 
			
		||||
            media: {url: attachment.url},
 | 
			
		||||
            description: attachment.description ?? attachment.comment,
 | 
			
		||||
          });
 | 
			
		||||
        } else if (type.startsWith("video")) {
 | 
			
		||||
          videos.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: fileType,
 | 
			
		||||
            media: {url: attachment.url},
 | 
			
		||||
            description: attachment.description ?? attachment.comment,
 | 
			
		||||
          });
 | 
			
		||||
        } else if (type.startsWith("audio")) {
 | 
			
		||||
          audios.push({
 | 
			
		||||
            url: attachment.url,
 | 
			
		||||
              desc: attachment.description ?? attachment.comment,
 | 
			
		||||
              type: fileType,
 | 
			
		||||
            description: attachment.description ?? attachment.comment,
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
    if (!spoiler && postData.sensitive && attachments.length > 0) {
 | 
			
		||||
      spoiler = true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1156,19 +1120,19 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
 | 
			
		||||
    if (postData.inReplyTo) {
 | 
			
		||||
      contextUrl = postData.inReplyTo;
 | 
			
		||||
      context = "Replying to: ";
 | 
			
		||||
      context = `-# ${Icons.fedimbed.reply} Replying to: `;
 | 
			
		||||
 | 
			
		||||
      const replyData = await fetchPost(postData.inReplyTo, platform);
 | 
			
		||||
      if (replyData) {
 | 
			
		||||
        if (replyData._fedimbed_mastoapi) {
 | 
			
		||||
          context += `${
 | 
			
		||||
          context += `[${
 | 
			
		||||
            replyData.account?.display_name ??
 | 
			
		||||
            replyData.account?.username ??
 | 
			
		||||
            replyData.user?.name ??
 | 
			
		||||
            replyData.user?.username
 | 
			
		||||
          } (@${
 | 
			
		||||
            replyData.account?.fqn ?? `${replyData.account?.username ?? replyData.user?.username}@${urlObj.hostname}`
 | 
			
		||||
          })`;
 | 
			
		||||
          })](${contextUrl})`;
 | 
			
		||||
        } else {
 | 
			
		||||
          const authorData = await signedFetch(replyData.actor ?? replyData.attributedTo, {
 | 
			
		||||
            headers: {
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,13 +1147,13 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
 | 
			
		||||
          if (authorData) {
 | 
			
		||||
            const authorUrlObj = new URL(authorData.url ?? authorData.id);
 | 
			
		||||
            context += `${authorData.name} (${authorData.preferredUsername}@${authorUrlObj.hostname})`;
 | 
			
		||||
            context += `[${authorData.name} (${authorData.preferredUsername}@${authorUrlObj.hostname})](${contextUrl})`;
 | 
			
		||||
          } else {
 | 
			
		||||
            // bootleg author
 | 
			
		||||
            const authorUrl = replyData.actor ?? replyData.attributedTo;
 | 
			
		||||
            const authorUrlObj = new URL(authorUrl);
 | 
			
		||||
            const name = authorUrlObj.pathname.substring(authorUrlObj.pathname.lastIndexOf("/") + 1);
 | 
			
		||||
            context += `${name}@${authorUrlObj.hostname}`;
 | 
			
		||||
            context += `[${name}@${authorUrlObj.hostname}](${authorUrl})`;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1201,36 +1165,18 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
      // NB: everyone else except gts doesnt follow the spec (they should be using attachments)
 | 
			
		||||
      const attachments = Array.isArray(postData.attachment) ? postData.attachment : [postData.attachment];
 | 
			
		||||
      for (const attachment of attachments) {
 | 
			
		||||
        if (attachment.mediaType) {
 | 
			
		||||
          if (attachment.mediaType.startsWith("video/")) {
 | 
			
		||||
            videos.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
              type: attachment.mediaType,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (attachment.mediaType.startsWith("image/")) {
 | 
			
		||||
            images.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
              type: attachment.mediaType,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (attachment.mediaType.startsWith("audio/")) {
 | 
			
		||||
            audios.push({
 | 
			
		||||
              url: attachment.url,
 | 
			
		||||
              desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
              type: attachment.mediaType,
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        } else if (attachment.url) {
 | 
			
		||||
        if (attachment.url) {
 | 
			
		||||
          const type = attachment.type?.toLowerCase();
 | 
			
		||||
 | 
			
		||||
          let attUrl = attachment.url;
 | 
			
		||||
          if (Array.isArray(attachment.url)) {
 | 
			
		||||
            switch (attachment.type) {
 | 
			
		||||
              case "Image": {
 | 
			
		||||
            switch (type) {
 | 
			
		||||
              case "image": {
 | 
			
		||||
                const newUrl = attachment.url.find((a) => a.mediaType?.startsWith("image/"))?.href;
 | 
			
		||||
                if (newUrl) attUrl = newUrl;
 | 
			
		||||
                break;
 | 
			
		||||
              }
 | 
			
		||||
              case "Video": {
 | 
			
		||||
              case "video": {
 | 
			
		||||
                const newUrl = attachment.url.find((a) => a.mediaType?.startsWith("video/"))?.href;
 | 
			
		||||
                if (newUrl) attUrl = newUrl;
 | 
			
		||||
                break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1240,61 +1186,22 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          let contentType;
 | 
			
		||||
          if (attUrl != null)
 | 
			
		||||
            contentType = await fetch(attUrl, {
 | 
			
		||||
              method: "HEAD",
 | 
			
		||||
            }).then((res) => res.headers.get("Content-Type"));
 | 
			
		||||
 | 
			
		||||
          if (contentType) {
 | 
			
		||||
            if (contentType.startsWith("image/")) {
 | 
			
		||||
              images.push({
 | 
			
		||||
                url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: contentType,
 | 
			
		||||
              });
 | 
			
		||||
            } else if (contentType.startsWith("video/")) {
 | 
			
		||||
              videos.push({
 | 
			
		||||
                url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: contentType,
 | 
			
		||||
              });
 | 
			
		||||
            } else if (contentType.startsWith("audio/")) {
 | 
			
		||||
              audios.push({
 | 
			
		||||
                url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: contentType,
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            const type = attachment.type?.toLowerCase();
 | 
			
		||||
 | 
			
		||||
            const fileType =
 | 
			
		||||
              type.indexOf("/") > -1
 | 
			
		||||
                ? type
 | 
			
		||||
                : type +
 | 
			
		||||
                  "/" +
 | 
			
		||||
                  (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg");
 | 
			
		||||
          if (type.startsWith("image")) {
 | 
			
		||||
            images.push({
 | 
			
		||||
                url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: fileType,
 | 
			
		||||
              media: {url: attUrl},
 | 
			
		||||
              description: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (type.startsWith("video")) {
 | 
			
		||||
            videos.push({
 | 
			
		||||
                url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: fileType,
 | 
			
		||||
              media: {url: attUrl},
 | 
			
		||||
              description: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
            });
 | 
			
		||||
          } else if (type.startsWith("audio")) {
 | 
			
		||||
            audios.push({
 | 
			
		||||
              url: attUrl,
 | 
			
		||||
                desc: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
                type: fileType,
 | 
			
		||||
              description: attachment.name ?? attachment.description ?? attachment.comment,
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          logger.warn("fedimbed", `Unhandled attachment structure! ${JSON.stringify(attachment)}`);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1306,14 +1213,8 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (postData.image?.url) {
 | 
			
		||||
      const imageUrl = new URL(postData.image.url);
 | 
			
		||||
      const contentType = await fetch(postData.image.url, {
 | 
			
		||||
        method: "HEAD",
 | 
			
		||||
      }).then((res) => res.headers.get("Content-Type"));
 | 
			
		||||
      images.push({
 | 
			
		||||
        url: postData.image.url,
 | 
			
		||||
        desc: "",
 | 
			
		||||
        type: contentType ?? "image/" + imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1),
 | 
			
		||||
        media: {url: postData.image.url},
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1328,7 +1229,7 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
    })
 | 
			
		||||
      .then((res) => res.json())
 | 
			
		||||
      .catch((err) => {
 | 
			
		||||
        /*if (platform !== "cohost")*/ logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
 | 
			
		||||
        logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    if (authorData) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1394,31 +1295,15 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
 | 
			
		||||
  let desc = "";
 | 
			
		||||
  let MAX_LENGTH = 3999;
 | 
			
		||||
  if ((cw != "" || sensitive) && images.length == 0 && videos.length == 0 && audios.length == 0) {
 | 
			
		||||
  if (cw != "" || sensitive) {
 | 
			
		||||
    const ors = content.split("||");
 | 
			
		||||
    desc += `||${content.replaceAll("||", "|\u200b|")}||`;
 | 
			
		||||
    MAX_LENGTH -= ors.length - 1;
 | 
			
		||||
    MAX_LENGTH -= 4;
 | 
			
		||||
 | 
			
		||||
    if (cw != "") {
 | 
			
		||||
      desc = "\u26a0 " + cw + "\n\n" + desc;
 | 
			
		||||
      MAX_LENGTH -= 4 - cw.length;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    desc = content;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (desc.length > MAX_LENGTH) {
 | 
			
		||||
    if (desc.endsWith("||")) {
 | 
			
		||||
      desc = desc.substring(0, MAX_LENGTH - 2);
 | 
			
		||||
      desc += "\u2026||";
 | 
			
		||||
    } else {
 | 
			
		||||
      desc = desc.substring(0, MAX_LENGTH) + "\u2026";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let user = author.name ? `${author.name} (${author.handle})` : author.handle;
 | 
			
		||||
 | 
			
		||||
  const crawled = await getCrawledData(url, color, platformName);
 | 
			
		||||
  if (!color && crawled?.color) {
 | 
			
		||||
    color = crawled.color;
 | 
			
		||||
| 
						 | 
				
			
			@ -1429,328 +1314,184 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
 | 
			
		||||
  if (platformName.includes("Nitter") && platformName.includes(" \u2022 ")) {
 | 
			
		||||
    const [nn, ns] = platformName.split(" \u2022 ");
 | 
			
		||||
    stats = ns;
 | 
			
		||||
    stats = ns
 | 
			
		||||
      .replace("\u21a9", Icons.fedimbed.reply)
 | 
			
		||||
      .replace("\ud83d\udd01", Icons.fedimbed.repost)
 | 
			
		||||
      .replace("\u2198", Icons.fedimbed.quote)
 | 
			
		||||
      .replace("\u2665", Icons.fedimbed.like)
 | 
			
		||||
      .replace("\ud83d\udd16", Icons.fedimbed.bookmark)
 | 
			
		||||
      .replace("\ud83d\udc41", Icons.fedimbed.views)
 | 
			
		||||
      .replaceAll(/ <:i:/g, "\u3000<:i:");
 | 
			
		||||
    platformName = nn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (platformName == "Nitter") {
 | 
			
		||||
    const newHandle = author.handle.split("@")[0];
 | 
			
		||||
    user = `${author.name} (@${newHandle})`;
 | 
			
		||||
    author.handle = "@" + newHandle;
 | 
			
		||||
 | 
			
		||||
    if (context) {
 | 
			
		||||
      const contextHandle = context.match(/\(([^@]+?@.+?)\)/)?.[1];
 | 
			
		||||
      if (contextHandle) {
 | 
			
		||||
        const newContextHandle = "@" + contextHandle.split("@")[0];
 | 
			
		||||
        context = context.replace(contextHandle, newContextHandle);
 | 
			
		||||
        const newContextHandle = contextHandle.split("@")[0];
 | 
			
		||||
        context = context.replace(contextHandle, "@" + newContextHandle);
 | 
			
		||||
        context = context.replace(/: (.+?)$/, `: [$1](${contextUrl})`);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const baseEmbed = {
 | 
			
		||||
    color,
 | 
			
		||||
    url,
 | 
			
		||||
    timestamp,
 | 
			
		||||
    description: desc,
 | 
			
		||||
    title: title ?? user,
 | 
			
		||||
    author: title
 | 
			
		||||
      ? {
 | 
			
		||||
          name: user,
 | 
			
		||||
          url: author.url,
 | 
			
		||||
  const warningText = {
 | 
			
		||||
    type: 10,
 | 
			
		||||
    content: `## ${cw}`,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const container = {
 | 
			
		||||
    type: 17,
 | 
			
		||||
    accent_color: color,
 | 
			
		||||
    components: [],
 | 
			
		||||
    spoiler,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let headerContent = `${author.name ? `## ${author.name}\n` : ""}-# [${author.handle}](${author.url})`;
 | 
			
		||||
 | 
			
		||||
  if (title) headerContent += "\n### " + title;
 | 
			
		||||
 | 
			
		||||
  MAX_LENGTH -= headerContent.length + 1;
 | 
			
		||||
 | 
			
		||||
  if (desc.length > MAX_LENGTH) {
 | 
			
		||||
    if (desc.endsWith("||")) {
 | 
			
		||||
      desc = desc.substring(0, MAX_LENGTH - 2);
 | 
			
		||||
      desc += "\u2026||";
 | 
			
		||||
    } else {
 | 
			
		||||
      desc = desc.substring(0, MAX_LENGTH) + "\u2026";
 | 
			
		||||
    }
 | 
			
		||||
      : context
 | 
			
		||||
      ? {
 | 
			
		||||
          name: context,
 | 
			
		||||
          url: contextUrl,
 | 
			
		||||
          icon_url: "https://cdn.discordapp.com/emojis/1308640078825787412.png",
 | 
			
		||||
  }
 | 
			
		||||
      : null,
 | 
			
		||||
    footer: {
 | 
			
		||||
      icon_url: crawled?.icon,
 | 
			
		||||
      text: (stats != null && stats != "" ? stats + "\n" : "") + platformName,
 | 
			
		||||
  headerContent += "\n" + desc;
 | 
			
		||||
 | 
			
		||||
  const header = [
 | 
			
		||||
    context && {
 | 
			
		||||
      type: 10,
 | 
			
		||||
      content: context,
 | 
			
		||||
    },
 | 
			
		||||
    thumbnail: {
 | 
			
		||||
    {
 | 
			
		||||
      type: 9,
 | 
			
		||||
      components: [
 | 
			
		||||
        {
 | 
			
		||||
          type: 10,
 | 
			
		||||
          content: headerContent,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      accessory: {
 | 
			
		||||
        type: 11,
 | 
			
		||||
        media: {
 | 
			
		||||
          url: author.avatar,
 | 
			
		||||
        },
 | 
			
		||||
    fields: [],
 | 
			
		||||
  };
 | 
			
		||||
  if (images.length > 0) {
 | 
			
		||||
    if (images.length > 1) {
 | 
			
		||||
      const links = images.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`).join("\u3000\u3000");
 | 
			
		||||
 | 
			
		||||
      if (links.length <= 1024)
 | 
			
		||||
        baseEmbed.fields.push({
 | 
			
		||||
          name: "Images",
 | 
			
		||||
          value: links,
 | 
			
		||||
          inline: true,
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
      baseEmbed.fields.push({
 | 
			
		||||
        name: "Image",
 | 
			
		||||
        value: `[Click for image](${images[0].url})`,
 | 
			
		||||
        inline: true,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (videos.length > 0) {
 | 
			
		||||
    if (videos.length > 1) {
 | 
			
		||||
      baseEmbed.fields.push({
 | 
			
		||||
        name: "Videos",
 | 
			
		||||
        value: videos.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`).join("\u3000\u3000"),
 | 
			
		||||
        inline: true,
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      baseEmbed.fields.push({
 | 
			
		||||
        name: "Video",
 | 
			
		||||
        value: `[Click for video](${videos[0].url})`,
 | 
			
		||||
        inline: true,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (audios.length > 0) {
 | 
			
		||||
    if (audios.length > 1) {
 | 
			
		||||
      baseEmbed.fields.push({
 | 
			
		||||
        name: "Audios",
 | 
			
		||||
        value: audios.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`).join("\u3000\u3000"),
 | 
			
		||||
        inline: true,
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      baseEmbed.fields.push({
 | 
			
		||||
        name: "Audio",
 | 
			
		||||
        value: `[Click for audio](${audios[0].url})`,
 | 
			
		||||
        inline: true,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  ].filter((x) => !!x);
 | 
			
		||||
  container.components.push(...header);
 | 
			
		||||
 | 
			
		||||
  if (poll) {
 | 
			
		||||
    const pollTime = poll.end.getTime();
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    baseEmbed.fields.push({
 | 
			
		||||
      name: "Poll",
 | 
			
		||||
      value:
 | 
			
		||||
    container.components.push({
 | 
			
		||||
      type: 10,
 | 
			
		||||
      content:
 | 
			
		||||
        poll.options
 | 
			
		||||
          .map((o) => {
 | 
			
		||||
            const percent = o.count / poll.total;
 | 
			
		||||
            const bar = Math.round(percent * 32);
 | 
			
		||||
 | 
			
		||||
            return `**${o.name}** (${numberFormatter.format(o.count)}, ${Math.round(
 | 
			
		||||
            return `> **${o.name}** (${numberFormatter.format(o.count)}, ${Math.round(
 | 
			
		||||
              percent * 100
 | 
			
		||||
            )}%)\n\`${"\u2588".repeat(bar)}${" ".repeat(32 - bar)}\``;
 | 
			
		||||
            )}%)> \n\`${"\u2588".repeat(bar)}${" ".repeat(32 - bar)}\``;
 | 
			
		||||
          })
 | 
			
		||||
          .join("\n\n") +
 | 
			
		||||
        `\n\n${poll.total} votes \u2022 End${pollTime > now ? "s" : "ed"} <t:${Math.floor(pollTime / 1000)}:R>`,
 | 
			
		||||
          .join("> \n> \n") +
 | 
			
		||||
        `> \n> \n${poll.total} votes \u2022 End${pollTime > now ? "s" : "ed"} <t:${Math.floor(pollTime / 1000)}:R>`,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let sendWait = false;
 | 
			
		||||
  if (videos.length > 0 || audios.length > 0 || images.length > 4) {
 | 
			
		||||
    sendWait = true;
 | 
			
		||||
    if (msg instanceof Message) await msg.addReaction("\uD83D\uDCE4");
 | 
			
		||||
  const footer = {
 | 
			
		||||
    type: 9,
 | 
			
		||||
    components: [
 | 
			
		||||
      {
 | 
			
		||||
        type: 10,
 | 
			
		||||
        content: `-# ${stats}\n${platformName} \u2022 <t:${Math.floor(new Date(timestamp).getTime() / 1000)}:F>`,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    accessory: {
 | 
			
		||||
      type: 2,
 | 
			
		||||
      style: 5,
 | 
			
		||||
      label: "View Post",
 | 
			
		||||
      url,
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  for (const image in images) {
 | 
			
		||||
    if (image.description?.length === 0) image.description = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const embeds = [];
 | 
			
		||||
  const files = [];
 | 
			
		||||
 | 
			
		||||
  const guild = msg.channel?.guild ?? (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
 | 
			
		||||
 | 
			
		||||
  let limit = getUploadLimit(guild);
 | 
			
		||||
  if (msg.attachmentSizeLimit != null) limit = msg.attachmentSizeLimit;
 | 
			
		||||
 | 
			
		||||
  if (images.length > 0) {
 | 
			
		||||
    if (images.length <= 4) {
 | 
			
		||||
      for (const attachment of images) {
 | 
			
		||||
        const embed = Object.assign({}, baseEmbed);
 | 
			
		||||
        embed.image = {
 | 
			
		||||
          url: attachment.url,
 | 
			
		||||
          description: attachment.desc,
 | 
			
		||||
        };
 | 
			
		||||
        embeds.push(embed);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (images.length > 4 && images.length <= 10) {
 | 
			
		||||
      for (const attachment of images) {
 | 
			
		||||
        const size = await fetch(attachment.url, {
 | 
			
		||||
          method: "HEAD",
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
          },
 | 
			
		||||
        }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
        if (size <= limit) {
 | 
			
		||||
          const file = await fetch(attachment.url, {
 | 
			
		||||
            headers: {
 | 
			
		||||
              "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
            },
 | 
			
		||||
          })
 | 
			
		||||
            .then((res) => res.arrayBuffer())
 | 
			
		||||
            .then((buf) => Buffer.from(buf));
 | 
			
		||||
 | 
			
		||||
          files.push({
 | 
			
		||||
            filename:
 | 
			
		||||
              (cw != "" || spoiler ? "SPOILER_" : "") +
 | 
			
		||||
              (attachment.type.indexOf("/") > -1
 | 
			
		||||
                ? attachment.type.replace("/", ".")
 | 
			
		||||
                : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
 | 
			
		||||
            file,
 | 
			
		||||
            description: attachment.desc,
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      embeds.push(baseEmbed);
 | 
			
		||||
    } else {
 | 
			
		||||
      const ten = images.slice(0, 10);
 | 
			
		||||
 | 
			
		||||
      for (const attachment of ten) {
 | 
			
		||||
        const size = await fetch(attachment.url, {
 | 
			
		||||
          method: "HEAD",
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
          },
 | 
			
		||||
        }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
        if (size <= limit) {
 | 
			
		||||
          const file = await fetch(attachment.url, {
 | 
			
		||||
            headers: {
 | 
			
		||||
              "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
            },
 | 
			
		||||
          })
 | 
			
		||||
            .then((res) => res.arrayBuffer())
 | 
			
		||||
            .then((buf) => Buffer.from(buf));
 | 
			
		||||
 | 
			
		||||
          files.push({
 | 
			
		||||
            filename:
 | 
			
		||||
              (cw != "" || spoiler ? "SPOILER_" : "") +
 | 
			
		||||
              (attachment.type.indexOf("/") > -1
 | 
			
		||||
                ? attachment.type.replace("/", ".")
 | 
			
		||||
                : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")),
 | 
			
		||||
            file,
 | 
			
		||||
            description: attachment.desc,
 | 
			
		||||
    if (images.length > 10) {
 | 
			
		||||
      while (images.length > 10) {
 | 
			
		||||
        container.components.push({
 | 
			
		||||
          type: 12,
 | 
			
		||||
          items: images.splice(0, 10),
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      if (images.length <= 14) {
 | 
			
		||||
        const fourteen = images.slice(10, 14);
 | 
			
		||||
 | 
			
		||||
        for (const attachment of fourteen) {
 | 
			
		||||
          const embed = Object.assign({}, baseEmbed);
 | 
			
		||||
          embed.image = {
 | 
			
		||||
            url: attachment.url,
 | 
			
		||||
            description: attachment.desc,
 | 
			
		||||
          };
 | 
			
		||||
          embeds.push(embed);
 | 
			
		||||
        }
 | 
			
		||||
      } else if (images.length <= 18) {
 | 
			
		||||
        const fourteen = images.slice(10, 14);
 | 
			
		||||
 | 
			
		||||
        for (const attachment of fourteen) {
 | 
			
		||||
          const embed = Object.assign({}, baseEmbed);
 | 
			
		||||
          embed.image = {
 | 
			
		||||
            url: attachment.url,
 | 
			
		||||
            description: attachment.desc,
 | 
			
		||||
          };
 | 
			
		||||
          embeds.push(embed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const eighteen = images.slice(14, 18);
 | 
			
		||||
        const _embed = {
 | 
			
		||||
          color: baseEmbed.color,
 | 
			
		||||
          url: baseEmbed.url + "?_",
 | 
			
		||||
          title: "Additional Images",
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for (const attachment of eighteen) {
 | 
			
		||||
          const embed = Object.assign({}, _embed);
 | 
			
		||||
          embed.image = {
 | 
			
		||||
            url: attachment.url,
 | 
			
		||||
            description: attachment.desc,
 | 
			
		||||
          };
 | 
			
		||||
          embeds.push(embed);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    embeds.push(baseEmbed);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (videos.length > 0) {
 | 
			
		||||
    for (const attachment of videos) {
 | 
			
		||||
      const size = await fetch(attachment.url, {
 | 
			
		||||
        method: "HEAD",
 | 
			
		||||
        headers: {
 | 
			
		||||
          "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
        },
 | 
			
		||||
      }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
      if (size <= limit) {
 | 
			
		||||
        const file = await fetch(attachment.url, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
          .then((res) => res.arrayBuffer())
 | 
			
		||||
          .then((buf) => Buffer.from(buf));
 | 
			
		||||
 | 
			
		||||
        files.push({
 | 
			
		||||
          filename:
 | 
			
		||||
            (cw != "" || spoiler ? "SPOILER_" : "") +
 | 
			
		||||
            (attachment.type.indexOf("/") > -1
 | 
			
		||||
              ? attachment.type.replace("/", ".").replace("quicktime", "mov")
 | 
			
		||||
              : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")),
 | 
			
		||||
          file,
 | 
			
		||||
          description: attachment.desc,
 | 
			
		||||
    container.components.push({
 | 
			
		||||
      type: 12,
 | 
			
		||||
      items: images,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  for (const video of videos) {
 | 
			
		||||
    if (video.description?.length === 0) video.description = null;
 | 
			
		||||
    container.components.push({
 | 
			
		||||
      type: 12,
 | 
			
		||||
      items: [video],
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (audios.length > 0) {
 | 
			
		||||
    for (const attachment of audios) {
 | 
			
		||||
      const size = await fetch(attachment.url, {
 | 
			
		||||
        method: "HEAD",
 | 
			
		||||
        headers: {
 | 
			
		||||
          "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
    container.components.push(
 | 
			
		||||
      {
 | 
			
		||||
        type: 10,
 | 
			
		||||
        content: "### Audio Attachments",
 | 
			
		||||
      },
 | 
			
		||||
      }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
      {
 | 
			
		||||
        type: 1,
 | 
			
		||||
        components: audios.map((a) => ({
 | 
			
		||||
          type: 2,
 | 
			
		||||
          style: 5,
 | 
			
		||||
          emoji: {name: "silk_sound", id: "1273119755376529418", animated: false},
 | 
			
		||||
          label:
 | 
			
		||||
            a.description?.length > 0
 | 
			
		||||
              ? `"${a.description.length > 78 ? a.description.substring(0, 77) + "\u2026" : a.description}"`
 | 
			
		||||
              : "Audio",
 | 
			
		||||
          url: a.url,
 | 
			
		||||
        })),
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
      if (size <= limit) {
 | 
			
		||||
        const file = await fetch(attachment.url, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
          .then((res) => res.arrayBuffer())
 | 
			
		||||
          .then((buf) => Buffer.from(buf));
 | 
			
		||||
 | 
			
		||||
        files.push({
 | 
			
		||||
          filename:
 | 
			
		||||
            (cw != "" || spoiler ? "SPOILER_" : "") +
 | 
			
		||||
            (attachment.type.indexOf("/") > -1
 | 
			
		||||
              ? attachment.type.replace("/", ".").replace("mpeg", "mp3").replace("vnd.wave", "wav").replace("x-", "")
 | 
			
		||||
              : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")),
 | 
			
		||||
          file,
 | 
			
		||||
          description: attachment.desc,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  container.components.push(footer);
 | 
			
		||||
 | 
			
		||||
  if (quoteRes) {
 | 
			
		||||
    if (!sendWait && quoteRes.sendWait) sendWait = true;
 | 
			
		||||
    files.push(...quoteRes.response.attachments);
 | 
			
		||||
    const quoteEmbed = quoteRes.response.embeds[0];
 | 
			
		||||
    quoteEmbed.author = {name: "Quoted Post", icon_url: "https://cdn.discordapp.com/emojis/1308640087759654922.png"};
 | 
			
		||||
    embeds.push(quoteEmbed);
 | 
			
		||||
    const quoteComponents = quoteRes.response.components[0].components;
 | 
			
		||||
    const quoteContext = `-# ${Icons.fedimbed.quote} Quoted Post`;
 | 
			
		||||
    if (quoteComponents[0].type == 10) {
 | 
			
		||||
      quoteComponents[0].content = quoteContext + "\n" + quoteComponents[0].content;
 | 
			
		||||
    } else {
 | 
			
		||||
      quoteComponents.splice(0, 0, {type: 10, content: quoteContext});
 | 
			
		||||
    }
 | 
			
		||||
    container.components.push({type: 14}, ...quoteComponents);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    response: {
 | 
			
		||||
      content:
 | 
			
		||||
        cw != "" && (images.length > 0 || videos.length > 0 || audios.length > 0)
 | 
			
		||||
          ? `:warning: ${cw} || ${url} ||`
 | 
			
		||||
          : spoiler
 | 
			
		||||
          ? `|| ${url} ||`
 | 
			
		||||
          : "",
 | 
			
		||||
      embeds,
 | 
			
		||||
      attachments: files,
 | 
			
		||||
      flags: 1 << 15,
 | 
			
		||||
      components: [cw.length > 0 ? warningText : false, container].filter((x) => !!x),
 | 
			
		||||
      allowedMentions: {
 | 
			
		||||
        repliedUser: false,
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -1758,7 +1499,6 @@ async function processUrl(msg, url, spoiler = false, command = false) {
 | 
			
		|||
        messageID: msg.id,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    sendWait,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue