Skip to content

Commit 4a8dc23

Browse files
committed
feat: Add 7TV emote upload command
1 parent 5f4deed commit 4a8dc23

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

src/commands/admin/misc/add7tv.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const {
2+
Client,
3+
SlashCommandBuilder,
4+
PermissionFlagsBits,
5+
MessageFlags
6+
} = require("discord.js");
7+
const fetch = (...args) =>
8+
import("node-fetch").then(({ default: fetch }) => fetch(...args));
9+
const sharp = require("sharp");
10+
11+
module.exports = {
12+
/**
13+
*
14+
* @param {Client} client
15+
* @param {import('discord.js').ChatInputCommandInteraction} interaction
16+
*/
17+
run: async ({ client, interaction }) => {
18+
try {
19+
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
20+
21+
if (!interaction.guild) return;
22+
23+
const url = interaction.options.getString("link", true).trim();
24+
const match = url.match(/\/emotes\/([a-zA-Z0-9]{24,})/);
25+
26+
if (!match) {
27+
return interaction.editReply(
28+
"That doesn’t look like a valid 7TV emote link."
29+
);
30+
}
31+
32+
const emoteId = match[1];
33+
const res = await fetch(`https://7tv.io/v3/emotes/${emoteId}`);
34+
35+
if (!res.ok) {
36+
return interaction.editReply("Couldn’t reach the 7TV API 😢");
37+
}
38+
39+
const emote = await res.json();
40+
const file =
41+
[...emote.host.files]
42+
.filter((f) => f.format === "WEBP")
43+
.sort((a, b) => b.width - a.width)[0] || emote.host.files[0];
44+
45+
const fileUrl = `https:${emote.host.url}/${file.name}`;
46+
47+
const imgBuf = Buffer.from(await (await fetch(fileUrl)).arrayBuffer());
48+
49+
let finalBuf = imgBuf;
50+
51+
// If the file is already < 256 KB, don't touch it
52+
if (imgBuf.length > 256_000) {
53+
finalBuf = await sharp(imgBuf)
54+
.resize(256, 256, { fit: "inside" })
55+
.png()
56+
.toBuffer();
57+
58+
if (finalBuf.length > 256_000) {
59+
return interaction.editReply(
60+
"Still too chunky after resizing (>256 kB)."
61+
);
62+
}
63+
}
64+
65+
if (finalBuf.length > 256_000) {
66+
return interaction.editReply(
67+
"Still too chunky after resizing (>256 kB)."
68+
);
69+
}
70+
71+
const name = (
72+
interaction.options.getString("name") || emote.name
73+
).replace(/[^a-z0-9_]/gi, "_");
74+
75+
const emoji = await interaction.guild.emojis.create({
76+
attachment: finalBuf,
77+
name
78+
});
79+
80+
interaction.editReply(`Added ${emoji.toString()} \`:${emoji.name}:\``);
81+
} catch (err) {
82+
console.error("Error adding 7TV emote:", err);
83+
interaction.editReply("Something went wrong trying to add that emote.");
84+
}
85+
},
86+
87+
data: new SlashCommandBuilder()
88+
.setName("add7tv")
89+
.setDescription("Add a 7TV emote to this server")
90+
.addStringOption((option) =>
91+
option
92+
.setName("link")
93+
.setDescription("Full 7TV emote URL")
94+
.setRequired(true)
95+
)
96+
.addStringOption((option) =>
97+
option.setName("name").setDescription("Custom emoji name (optional)")
98+
)
99+
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
100+
};

0 commit comments

Comments
 (0)