LOADING JASON DISCORD HOST V1
LOADING JASON DISCORD HOST V1
Jason Discord Host V1
Announce Bot is a small embed studio. /embed previews in place. /announce sends to a chosen channel (or ANNOUNCE_CHANNEL_ID) and can mention @everyone when the caller is allowed to.
Enable: Guilds only.
require("dotenv").config();
const {
Client,
GatewayIntentBits,
EmbedBuilder,
SlashCommandBuilder,
PermissionFlagsBits,
ChannelType,
Events,
MessageFlags,
} = require("discord.js");
const TOKEN = process.env.DISCORD_TOKEN;
const GUILD_ID = process.env.GUILD_ID?.trim();
const DEFAULT_CHANNEL_ID = process.env.ANNOUNCE_CHANNEL_ID?.trim();
if (!TOKEN) {
console.error("Missing DISCORD_TOKEN. Copy .env.example to .env and add your bot token.");
process.exit(1);
}
const client = new Client({
intents: [GatewayIntentBits.Guilds],
});
const COLOR_CHOICES = [
{ name: "Cyan", value: "22f0ff" },
{ name: "Magenta", value: "ff2bd6" },
{ name: "Violet", value: "9b5cff" },
{ name: "Green", value: "22c55e" },
{ name: "Gold", value: "fbbf24" },
{ name: "Red", value: "ef4444" },
];
const announceOptions = (builder) =>
builder
.addStringOption((o) => o.setName("title").setDescription("Embed title").setRequired(true).setMaxLength(256))
.addStringOption((o) => o.setName("description").setDescription("Embed body").setRequired(true).setMaxLength(4000))
.addStringOption((o) =>
o
.setName("color")
.setDescription("Accent color")
.addChoices(...COLOR_CHOICES),
)
.addStringOption((o) => o.setName("image").setDescription("Optional image URL"))
.addStringOption((o) => o.setName("footer").setDescription("Footer text").setMaxLength(200));
const commands = [
new SlashCommandBuilder().setName("ping").setDescription("Check that Announce Bot is online."),
announceOptions(
new SlashCommandBuilder()
.setName("announce")
.setDescription("Post an announcement embed to a channel.")
.addChannelOption((o) =>
o
.setName("channel")
.setDescription("Destination channel")
.addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement),
)
.addBooleanOption((o) => o.setName("mention_everyone").setDescription("Mention @everyone (staff only)")),
).setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
announceOptions(new SlashCommandBuilder().setName("embed").setDescription("Preview an embed in this channel.")).setDefaultMemberPermissions(
PermissionFlagsBits.ManageMessages,
),
].map((c) => c.toJSON());
function buildEmbed(interaction) {
const title = interaction.options.getString("title", true);
const description = interaction.options.getString("description", true);
const color = interaction.options.getString("color") || "22f0ff";
const image = interaction.options.getString("image");
const footer = interaction.options.getString("footer") || "Jason Discord Host V1 · Announce Bot";
const embed = new EmbedBuilder()
.setColor(parseInt(color, 16))
.setTitle(title)
.setDescription(description)
.setFooter({ text: footer })
.setTimestamp();
if (image) {
try {
const url = new URL(image);
if (url.protocol === "https:") embed.setImage(image);
} catch {
// ignore invalid image URLs
}
}
return embed;
}
async function registerCommands() {
if (GUILD_ID) {
const guild = await client.guilds.fetch(GUILD_ID);
await guild.commands.set(commands);
console.log(`Registered guild commands in ${guild.name}`);
return;
}
await client.application.commands.set(commands);
console.log("Registered global slash commands.");
}
client.once(Events.ClientReady, async () => {
console.log(`Announce Bot online as ${client.user.tag}`);
try {
await registerCommands();
} catch (error) {
console.error("Failed to register commands:", error);
}
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
try {
if (interaction.commandName === "ping") {
await interaction.reply({ content: `Pong — ${client.ws.ping}ms`, flags: MessageFlags.Ephemeral });
return;
}
if (!interaction.inGuild()) {
await interaction.reply({ content: "Use this bot inside a server.", flags: MessageFlags.Ephemeral });
return;
}
const embed = buildEmbed(interaction);
if (interaction.commandName === "embed") {
await interaction.reply({ embeds: [embed] });
return;
}
if (interaction.commandName === "announce") {
const mentionEveryone = interaction.options.getBoolean("mention_everyone") === true;
if (mentionEveryone && !interaction.memberPermissions?.has(PermissionFlagsBits.MentionEveryone)) {
await interaction.reply({
content: "You need the Mention Everyone permission to ping @everyone.",
flags: MessageFlags.Ephemeral,
});
return;
}
const selected = interaction.options.getChannel("channel");
const channelId = selected?.id || DEFAULT_CHANNEL_ID || interaction.channelId;
const channel = await interaction.guild.channels.fetch(channelId);
if (!channel?.isTextBased()) {
await interaction.reply({
content: "Set ANNOUNCE_CHANNEL_ID or pass a text channel.",
flags: MessageFlags.Ephemeral,
});
return;
}
await channel.send({
content: mentionEveryone ? "@everyone" : undefined,
embeds: [embed],
allowedMentions: { parse: mentionEveryone ? ["everyone"] : [] },
});
await interaction.reply({
content: `Announcement posted in ${channel}.`,
flags: MessageFlags.Ephemeral,
});
}
} catch (error) {
console.error(error);
const message = error.message || "Could not send that embed.";
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: message, flags: MessageFlags.Ephemeral }).catch(() => null);
} else {
await interaction.reply({ content: message, flags: MessageFlags.Ephemeral }).catch(() => null);
}
}
});
client.login(TOKEN).catch((error) => {
console.error("Login failed. Check DISCORD_TOKEN.", error);
process.exit(1);
});