src/interactions/menus/highlight.ts (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
import { getGuild } from "@/utils/queries";
import {
ApplicationCommandType,
type Attachment,
type Client,
ContextMenuCommandBuilder,
type GuildTextBasedChannel,
type MessageContextMenuCommandInteraction,
} from "discord.js";
const commandData = new ContextMenuCommandBuilder()
.setName("Highlight Clip")
.setType(ApplicationCommandType.Message);
function extractVideoLinks(message: { content: string }): string[] {
const urlRegex =
/https?:\/\/(?:www\.)?(?:youtube\.com\/watch\?v=[\w-]+|youtu\.be\/[\w-]+|medal\.tv\/(?:games\/[\w-]+\/clips\/[\w-]+|g\/[\w-]+|clips\/[\w-]+))/gi;
return Array.from(message.content.matchAll(urlRegex)).map((m) => m[0]);
}
function isVideoAttachment(attachment: Attachment): boolean {
return (
typeof attachment.contentType === "string" &&
attachment.contentType.startsWith("video/")
);
}
async function execute(
client: Client,
interaction: MessageContextMenuCommandInteraction,
) {
if (!interaction.guild) {
await interaction.reply({
content: "❌ This command can only be used in a server.",
ephemeral: true,
});
return;
}
await interaction.deferReply({ flags: ["Ephemeral"] });
const { data: guildConfig, exists } = await getGuild(interaction.guild.id);
if (!exists) {
await interaction.editReply({
content: "❌ No guild configuration found.",
});
return;
}
if (!guildConfig.highlights_channel_id) {
await interaction.editReply({
content:
"❌ No highlights channel configured. Use `/config highlight-channel`.",
});
return;
}
const targetMessage = interaction.targetMessage;
if (!targetMessage) {
await interaction.editReply({
content: "❌ Couldn't find the target message.",
});
return;
}
const videoAttachments = targetMessage.attachments.filter(isVideoAttachment);
const videoLinks = extractVideoLinks(targetMessage);
if (videoAttachments.size === 0 && videoLinks.length === 0) {
await interaction.editReply({
content:
"❌ No video attachments or supported links found in this message.",
});
return;
}
let highlightsChannel: GuildTextBasedChannel | null = null;
try {
highlightsChannel = (await client.channels.fetch(
guildConfig.highlights_channel_id,
)) as GuildTextBasedChannel | null;
} catch (e) {
await interaction.editReply({
content: "❌ Could not access the highlights channel.",
});
return;
}
if (!highlightsChannel) {
await interaction.editReply({
content: "❌ Highlights channel not found.",
});
return;
}
const files = Array.from(videoAttachments.values()).map((a) => a.url);
const author = `<@${targetMessage.author.id}>`;
const jumpUrl = targetMessage.url;
const videoLinksText = videoLinks
.map((link) => `• [Video Link](${link})`)
.join(" ");
const description = `:star: New highlight from ${author}!\n-# [Jump to message](${jumpUrl}) ${videoLinksText}`;
try {
const msg = await highlightsChannel.send({
content: description,
files: files.length > 0 ? files : undefined,
allowedMentions: { users: [targetMessage.author.id] },
});
await msg.react("⭐");
await interaction.editReply({
content: "✅ Highlight sent!",
});
} catch (error) {
await interaction.editReply({
content: "❌ Failed to send highlight.",
});
}
}
export default {
data: commandData,
execute,
};
|