interaction command support
This commit is contained in:
		
							parent
							
								
									73590257c8
								
							
						
					
					
						commit
						6e85237cab
					
				
					 6 changed files with 2376 additions and 1743 deletions
				
			
		
							
								
								
									
										3770
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										3770
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										65
									
								
								src/index.js
									
										
									
									
									
								
							
							
						
						
									
										65
									
								
								src/index.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -7,9 +7,14 @@ const {instead, before} = require("spitroast");
 | 
			
		|||
 | 
			
		||||
const config = require("../config.json");
 | 
			
		||||
const apikeys = require("../apikeys.json");
 | 
			
		||||
const Command = require("./lib/command.js");
 | 
			
		||||
 | 
			
		||||
const events = require("./lib/events.js");
 | 
			
		||||
const {formatUsername} = require("./lib/utils.js");
 | 
			
		||||
const timer = require("./lib/timer.js");
 | 
			
		||||
const Command = require("./lib/command.js");
 | 
			
		||||
const CommandDispatcher = require("./lib/commandDispatcher.js");
 | 
			
		||||
const InteractionCommand = require("./lib/interactionCommand.js");
 | 
			
		||||
const {InteractionDispatcher} = require("./lib/interactionDispatcher.js");
 | 
			
		||||
 | 
			
		||||
const bot = new Dysnomia.Client(config.token, {
 | 
			
		||||
  defaultImageFormat: "png",
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +26,7 @@ const bot = new Dysnomia.Client(config.token, {
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
const commands = new Dysnomia.Collection();
 | 
			
		||||
const interactionCommands = new Dysnomia.Collection();
 | 
			
		||||
 | 
			
		||||
const database = new sqlite3.Database(resolve(__dirname, "..", "database.db"));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +40,9 @@ function registerCommand(cmdObj) {
 | 
			
		|||
        aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""
 | 
			
		||||
      }`
 | 
			
		||||
    );
 | 
			
		||||
  } else if (cmdObj instanceof InteractionCommand) {
 | 
			
		||||
    interactionCommands.set(cmdObj.name, cmdObj);
 | 
			
		||||
    logger.info("hf:cmd", `Registered interaction command '${cmdObj.name}'`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,15 +51,13 @@ global.hf = {
 | 
			
		|||
  config,
 | 
			
		||||
  apikeys,
 | 
			
		||||
  commands,
 | 
			
		||||
  interactionCommands,
 | 
			
		||||
  registerCommand,
 | 
			
		||||
  events,
 | 
			
		||||
  timer,
 | 
			
		||||
  database,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CommandDispatcher = require("./lib/commandDispatcher.js");
 | 
			
		||||
const {formatUsername} = require("./lib/utils.js");
 | 
			
		||||
 | 
			
		||||
for (const file of fs.readdirSync(resolve(__dirname, "modules"))) {
 | 
			
		||||
  require(resolve(__dirname, "modules", file));
 | 
			
		||||
  logger.info("hf:modules", `Loaded module: '${file}'`);
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +87,7 @@ bot.on("messageCreate", async (msg) => {
 | 
			
		|||
    );
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
bot.on("messageUpdate", (msg, oldMsg) => {
 | 
			
		||||
bot.on("messageUpdate", async (msg, oldMsg) => {
 | 
			
		||||
  try {
 | 
			
		||||
    const oneDay = Date.now() - 86400000;
 | 
			
		||||
    if (
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +96,7 @@ bot.on("messageUpdate", (msg, oldMsg) => {
 | 
			
		|||
      oldMsg &&
 | 
			
		||||
      oldMsg.content !== msg.content
 | 
			
		||||
    ) {
 | 
			
		||||
      CommandDispatcher(msg);
 | 
			
		||||
      await CommandDispatcher(msg);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    const stack = (err?.stack ?? err.message).split("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +142,31 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => {
 | 
			
		|||
    );
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
bot.on("interactionCreate", async (interaction) => {
 | 
			
		||||
  try {
 | 
			
		||||
    if (!(interaction.channel instanceof Dysnomia.Channel)) {
 | 
			
		||||
      const newChannel = hf.bot.getChannel(interaction.channel.id);
 | 
			
		||||
      if (newChannel) {
 | 
			
		||||
        interaction.channel = newChannel;
 | 
			
		||||
      } else {
 | 
			
		||||
        interaction.channel = await hf.bot.getRESTChannel(
 | 
			
		||||
          interaction.channel.id
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await InteractionDispatcher(interaction);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    const stack = (err?.stack ?? err.message).split("\n");
 | 
			
		||||
    const error = stack.shift();
 | 
			
		||||
    logger.error(
 | 
			
		||||
      "hf:main",
 | 
			
		||||
      `Failed to dispatch interaction command: ${error}\n\t${stack.join(
 | 
			
		||||
        "\n\t"
 | 
			
		||||
      )}`
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
bot.once("ready", async () => {
 | 
			
		||||
  logger.info("hf:main", "Connected to Discord.");
 | 
			
		||||
| 
						 | 
				
			
			@ -152,6 +184,27 @@ bot.once("ready", async () => {
 | 
			
		|||
  bot.on("ready", () => {
 | 
			
		||||
    logger.info("hf:main", "Reconnected to Discord.");
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const commands = await bot.getCommands();
 | 
			
		||||
  for (const command of interactionCommands.values()) {
 | 
			
		||||
    const hasCommand = commands.find((c) => c.name == command.name);
 | 
			
		||||
    if (hasCommand) continue;
 | 
			
		||||
 | 
			
		||||
    await bot.createCommand({
 | 
			
		||||
      name: command.name,
 | 
			
		||||
      type: command.type,
 | 
			
		||||
      description: command.helpText,
 | 
			
		||||
      options: Object.values(command.options),
 | 
			
		||||
      defaultMemberPermissions: command.permissions,
 | 
			
		||||
      dmPermission: !command.guildOnly,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const command of commands) {
 | 
			
		||||
    if (interactionCommands.has(command.name)) continue;
 | 
			
		||||
 | 
			
		||||
    await bot.deleteCommand(command.id);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
bot.on("error", (err) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								src/lib/interactionCommand.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/lib/interactionCommand.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
const {ApplicationCommandTypes, ApplicationCommandOptionTypes} =
 | 
			
		||||
  require("@projectdysnomia/dysnomia").Constants;
 | 
			
		||||
 | 
			
		||||
class InteractionCommand {
 | 
			
		||||
  constructor(name) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.type = ApplicationCommandTypes.CHAT_INPUT;
 | 
			
		||||
    this.helpText = "No description provided.";
 | 
			
		||||
    this.guildOnly = false;
 | 
			
		||||
    this.options = {
 | 
			
		||||
      send: {
 | 
			
		||||
        type: ApplicationCommandOptionTypes.BOOLEAN,
 | 
			
		||||
        name: "send",
 | 
			
		||||
        description: "Should the output be ephemeral or not",
 | 
			
		||||
        required: false,
 | 
			
		||||
        default: true,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  callback() {
 | 
			
		||||
    return "Callback not overwritten.";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = InteractionCommand;
 | 
			
		||||
							
								
								
									
										127
									
								
								src/lib/interactionDispatcher.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/lib/interactionDispatcher.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,127 @@
 | 
			
		|||
const logger = require("./logger.js");
 | 
			
		||||
const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants;
 | 
			
		||||
const {pastelize, getTopColor} = require("./utils.js");
 | 
			
		||||
 | 
			
		||||
function getOption(interaction, command, key) {
 | 
			
		||||
  return (
 | 
			
		||||
    interaction.data.options?.find((o) => o.name === key)?.value ??
 | 
			
		||||
    command.options[key].default
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function runCommand(interaction, command) {
 | 
			
		||||
  if (command.ownerOnly && interaction.user.id != hf.config.owner_id) {
 | 
			
		||||
    return {
 | 
			
		||||
      content: "No\n\nSent from my iPhone.",
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    command.elevatedOnly &&
 | 
			
		||||
    !hf.config.elevated.includes(interaction.user.id)
 | 
			
		||||
  ) {
 | 
			
		||||
    return {
 | 
			
		||||
      content: "No\n\nSent from my iPhone.",
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const ret = command.callback(interaction);
 | 
			
		||||
    if (ret instanceof Promise) {
 | 
			
		||||
      return await ret;
 | 
			
		||||
    } else {
 | 
			
		||||
      return ret;
 | 
			
		||||
    }
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    logger.error("hf:cmd:" + command.name, err + "\n" + err.stack);
 | 
			
		||||
    return {
 | 
			
		||||
      content: ":warning: An internal error occurred.",
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function InteractionDispatcher(interaction) {
 | 
			
		||||
  const command = hf.interactionCommands.get(interaction.data.name);
 | 
			
		||||
  const shouldSend = getOption(interaction, command, "send");
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const response = await runCommand(interaction, command);
 | 
			
		||||
    if (response != null) {
 | 
			
		||||
      if (response.file) {
 | 
			
		||||
        const newFile = response.file;
 | 
			
		||||
        delete response.file;
 | 
			
		||||
        if (newFile.contents) {
 | 
			
		||||
          newFile.file = newFile.contents;
 | 
			
		||||
          delete newFile.contents;
 | 
			
		||||
        }
 | 
			
		||||
        if (newFile.name) {
 | 
			
		||||
          newFile.filename = newFile.name;
 | 
			
		||||
          delete newFile.name;
 | 
			
		||||
        }
 | 
			
		||||
        const files = response.attachments ?? [];
 | 
			
		||||
        files.push(newFile);
 | 
			
		||||
        response.attachments = files;
 | 
			
		||||
      }
 | 
			
		||||
      if (response.files) {
 | 
			
		||||
        response.attachments = response.files;
 | 
			
		||||
        delete response.files;
 | 
			
		||||
        for (const attachment of response.attachments) {
 | 
			
		||||
          if (attachment.contents) {
 | 
			
		||||
            attachment.file = attachment.contents;
 | 
			
		||||
            delete attachment.contents;
 | 
			
		||||
          }
 | 
			
		||||
          if (attachment.name) {
 | 
			
		||||
            attachment.filename = attachment.name;
 | 
			
		||||
            delete attachment.name;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (response.embed) {
 | 
			
		||||
        response.embeds = [...(response.embeds ?? []), response.embed];
 | 
			
		||||
        delete response.embed;
 | 
			
		||||
      }
 | 
			
		||||
      if (response.embeds) {
 | 
			
		||||
        for (const embed of response.embeds) {
 | 
			
		||||
          embed.color =
 | 
			
		||||
            embed.color ??
 | 
			
		||||
            getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await interaction.createMessage(
 | 
			
		||||
          Object.assign(
 | 
			
		||||
            typeof response === "string" ? {content: response} : response,
 | 
			
		||||
            {
 | 
			
		||||
              flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL,
 | 
			
		||||
              allowedMentions: {
 | 
			
		||||
                repliedUser: false,
 | 
			
		||||
              },
 | 
			
		||||
            }
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        await interaction.createMessage({
 | 
			
		||||
          content: `:warning: An error has occurred:\n\`\`\`${err}\`\`\``,
 | 
			
		||||
          flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
          allowedMentions: {
 | 
			
		||||
            repliedUser: false,
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    await interaction.createMessage({
 | 
			
		||||
      content: `:warning: An error has occurred:\n\`\`\`${err}\`\`\``,
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
      allowedMentions: {
 | 
			
		||||
        repliedUser: false,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {InteractionDispatcher, getOption};
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ async function processFile(link, spoiler = false) {
 | 
			
		|||
  const file = await res.text();
 | 
			
		||||
  const lines = file.replace(/\r/g, "").split("\n");
 | 
			
		||||
 | 
			
		||||
  const fileName = link.substring(
 | 
			
		||||
  const fileName = decodeURI(link).substring(
 | 
			
		||||
    link.lastIndexOf("/") + 1,
 | 
			
		||||
    link.indexOf("#") == -1 ? link.length : link.indexOf("#")
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants;
 | 
			
		||||
const {MessageFlags, ApplicationCommandOptionTypes} =
 | 
			
		||||
  require("@projectdysnomia/dysnomia").Constants;
 | 
			
		||||
const fs = require("node:fs");
 | 
			
		||||
const path = require("node:path");
 | 
			
		||||
const httpSignature = require("@peertube/http-signature");
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +8,8 @@ const events = require("../lib/events.js");
 | 
			
		|||
const logger = require("../lib/logger.js");
 | 
			
		||||
const {hasFlag} = require("../lib/guildSettings.js");
 | 
			
		||||
const {parseHtmlEntities, getUploadLimit} = require("../lib/utils.js");
 | 
			
		||||
const InteractionCommand = require("../lib/interactionCommand.js");
 | 
			
		||||
const {getOption} = require("../lib/interactionDispatcher.js");
 | 
			
		||||
 | 
			
		||||
const FRIENDLY_USERAGENT =
 | 
			
		||||
  "HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)";
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +160,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
    invalidUrl = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (invalidUrl) return;
 | 
			
		||||
  if (invalidUrl) return {};
 | 
			
		||||
 | 
			
		||||
  // some lemmy instances have old reddit frontend subdomains
 | 
			
		||||
  // but these frontends are just frontends and dont actually expose the API
 | 
			
		||||
| 
						 | 
				
			
			@ -661,7 +664,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
      "fedimbed",
 | 
			
		||||
      `Bailing trying to re-embed "${url}": Failed to get author.`
 | 
			
		||||
    );
 | 
			
		||||
    return;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Start constructing embed
 | 
			
		||||
| 
						 | 
				
			
			@ -806,12 +809,16 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
  let sendWait = false;
 | 
			
		||||
  if (videos.length > 0 || audios.length > 0 || images.length > 4) {
 | 
			
		||||
    sendWait = true;
 | 
			
		||||
    await msg.addReaction("\uD83D\uDCE4");
 | 
			
		||||
    if (msg) await msg.addReaction("\uD83D\uDCE4");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const embeds = [];
 | 
			
		||||
  const files = [];
 | 
			
		||||
 | 
			
		||||
  const guild =
 | 
			
		||||
    msg.channel?.guild ??
 | 
			
		||||
    (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false);
 | 
			
		||||
 | 
			
		||||
  if (images.length > 0) {
 | 
			
		||||
    if (images.length <= 4) {
 | 
			
		||||
      for (const attachment of images) {
 | 
			
		||||
| 
						 | 
				
			
			@ -830,7 +837,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
          },
 | 
			
		||||
        }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
        if (size <= getUploadLimit(msg.channel.guild)) {
 | 
			
		||||
        if (size <= getUploadLimit(guild)) {
 | 
			
		||||
          const file = await fetch(attachment.url, {
 | 
			
		||||
            headers: {
 | 
			
		||||
              "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
| 
						 | 
				
			
			@ -864,7 +871,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
          },
 | 
			
		||||
        }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
        if (size <= getUploadLimit(msg.channel.guild)) {
 | 
			
		||||
        if (size <= getUploadLimit(guild)) {
 | 
			
		||||
          const file = await fetch(attachment.url, {
 | 
			
		||||
            headers: {
 | 
			
		||||
              "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
| 
						 | 
				
			
			@ -937,7 +944,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
        },
 | 
			
		||||
      }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
      if (size <= getUploadLimit(msg.channel.guild)) {
 | 
			
		||||
      if (size <= getUploadLimit(guild)) {
 | 
			
		||||
        const file = await fetch(attachment.url, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
| 
						 | 
				
			
			@ -968,7 +975,7 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
        },
 | 
			
		||||
      }).then((res) => Number(res.headers.get("Content-Length")));
 | 
			
		||||
 | 
			
		||||
      if (size <= getUploadLimit(msg.channel.guild)) {
 | 
			
		||||
      if (size <= getUploadLimit(guild)) {
 | 
			
		||||
        const file = await fetch(attachment.url, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            "User-Agent": FRIENDLY_USERAGENT,
 | 
			
		||||
| 
						 | 
				
			
			@ -995,8 +1002,8 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await msg.channel
 | 
			
		||||
    .createMessage({
 | 
			
		||||
  return {
 | 
			
		||||
    response: {
 | 
			
		||||
      content:
 | 
			
		||||
        cw != "" &&
 | 
			
		||||
        (images.length > 0 || videos.length > 0 || audios.length > 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -1012,16 +1019,9 @@ async function processUrl(msg, url, spoiler = false) {
 | 
			
		|||
      messageReference: {
 | 
			
		||||
        messageID: msg.id,
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    .then(() => {
 | 
			
		||||
      if (sendWait) {
 | 
			
		||||
        msg.removeReaction("\uD83D\uDCE4");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ((msg.flags & MessageFlags.SUPPRESS_EMBEDS) === 0) {
 | 
			
		||||
        msg.edit({flags: MessageFlags.SUPPRESS_EMBEDS}).catch(() => {});
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    },
 | 
			
		||||
    sendWait,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
events.add("messageCreate", "fedimbed", async function (msg) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1054,15 +1054,100 @@ events.add("messageCreate", "fedimbed", async function (msg) {
 | 
			
		|||
            "fedimbed",
 | 
			
		||||
            `Hit "${service}" for "${url}", processing now.`
 | 
			
		||||
          );
 | 
			
		||||
          await processUrl(msg, url, hasSpoiler).catch((err) => {
 | 
			
		||||
          try {
 | 
			
		||||
            const {response, sendWait} = await processUrl(msg, url, hasSpoiler);
 | 
			
		||||
            await msg.channel.createMessage(response).then(() => {
 | 
			
		||||
              if (sendWait) {
 | 
			
		||||
                msg.removeReaction("\uD83D\uDCE4");
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              if ((msg.flags & MessageFlags.SUPPRESS_EMBEDS) === 0) {
 | 
			
		||||
                msg.edit({flags: MessageFlags.SUPPRESS_EMBEDS}).catch(() => {});
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            logger.error(
 | 
			
		||||
              "fedimbed",
 | 
			
		||||
              `Error processing "${url}":\n` + err.stack
 | 
			
		||||
            );
 | 
			
		||||
          });
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const fedimbedCommand = new InteractionCommand("fedimbed");
 | 
			
		||||
fedimbedCommand.helpText =
 | 
			
		||||
  "Better embeds for fediverse (Mastodon, Pleroma, etc) posts";
 | 
			
		||||
fedimbedCommand.options.url = {
 | 
			
		||||
  name: "url",
 | 
			
		||||
  type: ApplicationCommandOptionTypes.STRING,
 | 
			
		||||
  description: "URL to attempt to parse for re-embedding",
 | 
			
		||||
  required: true,
 | 
			
		||||
  default: "",
 | 
			
		||||
};
 | 
			
		||||
fedimbedCommand.options.spoiler = {
 | 
			
		||||
  name: "spoiler",
 | 
			
		||||
  type: ApplicationCommandOptionTypes.BOOLEAN,
 | 
			
		||||
  description: "Send embed spoilered",
 | 
			
		||||
  required: false,
 | 
			
		||||
  default: false,
 | 
			
		||||
};
 | 
			
		||||
fedimbedCommand.callback = async function (interaction) {
 | 
			
		||||
  let url = getOption(interaction, fedimbedCommand, "url");
 | 
			
		||||
  const spoiler = getOption(interaction, fedimbedCommand, "spoiler");
 | 
			
		||||
 | 
			
		||||
  url = url
 | 
			
		||||
    .replace(/\|/g, "")
 | 
			
		||||
    .replace(/^\]\(/, "")
 | 
			
		||||
    .replace(/\s*[\S]*?\)$/, "")
 | 
			
		||||
    .trim()
 | 
			
		||||
    .replace("@\u200b", "@")
 | 
			
		||||
    .replace("@%E2%80%8B", "@");
 | 
			
		||||
  let urlObj;
 | 
			
		||||
  try {
 | 
			
		||||
    urlObj = new URL(url);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    return {
 | 
			
		||||
      content: `Failed to parse URL:\`\`\`\n${err}\`\`\``,
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let hasService = false;
 | 
			
		||||
  for (const service of Object.keys(PATH_REGEX)) {
 | 
			
		||||
    const regex = PATH_REGEX[service];
 | 
			
		||||
    if (urlObj && regex.test(urlObj.pathname)) {
 | 
			
		||||
      hasService = true;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (hasService) {
 | 
			
		||||
    try {
 | 
			
		||||
      const {response} = await processUrl(interaction, url, spoiler);
 | 
			
		||||
 | 
			
		||||
      if (!response)
 | 
			
		||||
        return {
 | 
			
		||||
          content: "Failed to process URL.",
 | 
			
		||||
          flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
      delete response.messageReference;
 | 
			
		||||
      return response;
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      logger.error("fedimbed", `Error processing "${url}":\n` + err.stack);
 | 
			
		||||
      return {
 | 
			
		||||
        content: "Failed to process URL.",
 | 
			
		||||
        flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return {
 | 
			
		||||
      content: "Did not get a valid service for this URL.",
 | 
			
		||||
      flags: MessageFlags.EPHEMERAL,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue