blob: bf52dd1a1cb9f56af82bce36970fee9fdc9d0508 [file] [log] [blame]
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
CommandInteraction,
AutocompleteInteraction,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
APIMessageComponentEmoji,
ChannelSelectMenuBuilder,
RoleSelectMenuBuilder,
RoleSelectMenuInteraction,
ChannelSelectMenuInteraction,
ButtonInteraction,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
ModalSubmitInteraction
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import convertCurlyBracketString from "../../utils/convertCurlyBracketString.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder.setName("welcome").setDescription("Messages and roles sent or given when someone joins the server");
const callback = async (interaction: CommandInteraction): Promise<void> => {
const { renderChannel } = client.logger;
const m = await interaction.reply({
embeds: LoadingEmbed,
fetchReply: true,
ephemeral: true
});
let closed = false;
let config = await client.database.guilds.read(interaction.guild!.id);
let data = Object.assign({}, config.welcome);
do {
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("switch")
.setLabel(data.enabled ? "Enabled" : "Disabled")
.setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(
getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji
),
new ButtonBuilder()
.setCustomId("message")
.setLabel((data.message ? "Change" : "Set") + "Message")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("channelDM")
.setLabel("Send in DMs")
.setStyle(ButtonStyle.Primary)
.setDisabled(data.channel === "dm"),
new ButtonBuilder()
.setCustomId("role")
.setLabel("Clear Role")
.setStyle(ButtonStyle.Danger)
.setEmoji(getEmojiByName("CONTROL.CROSS", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setDisabled(
data.enabled === config.welcome.enabled &&
data.message === config.welcome.message &&
data.role === config.welcome.role &&
data.ping === config.welcome.ping &&
data.channel === config.welcome.channel
)
);
const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("channel")
.setPlaceholder("Select a channel to send welcome messages to")
);
const roleMenu = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
new RoleSelectMenuBuilder()
.setCustomId("roleToGive")
.setPlaceholder("Select a role to give to the member when they join the server")
);
const pingMenu = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
new RoleSelectMenuBuilder()
.setCustomId("roleToPing")
.setPlaceholder("Select a role to ping when a member joins the server")
);
const embed = new EmojiEmbed()
.setTitle("Welcome Settings")
.setStatus("Success")
.setDescription(
`${getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS")} Welcome messages and roles are ${
data.enabled ? "enabled" : "disabled"
}\n` +
`**Welcome message:** ${
data.message
? `\n> ` +
(await convertCurlyBracketString(
data.message,
interaction.user.id,
interaction.user.username,
interaction.guild!.name,
interaction.guild!.members
))
: "*None*"
}\n` +
`**Send message in:** ` +
(data.channel ? (data.channel == "dm" ? "DMs" : renderChannel(data.channel)) : `*None set*`) +
`\n` +
`**Role to ping:** ` +
(data.ping ? `<@&${data.ping}>` : `*None set*`) +
`\n` +
`**Role given on join:** ` +
(data.role ? `<@&${data.role}>` : `*None set*`)
);
await interaction.editReply({
embeds: [embed],
components: [buttons, channelMenu, roleMenu, pingMenu]
});
let i: RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
try {
i = (await m.awaitMessageComponent({
filter: (interaction) => interaction.user.id === interaction.user.id,
time: 300000
})) as RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
} catch (e) {
closed = true;
continue;
}
if (i.isButton()) {
switch (i.customId) {
case "switch": {
await i.deferUpdate();
data.enabled = !data.enabled;
break;
}
case "message": {
const modal = new ModalBuilder()
.setCustomId("modal")
.setTitle("Welcome Message")
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex1")
.setLabel("Server Info (1/3)")
.setPlaceholder(
`{serverName} - This server's name\n\n` +
`These placeholders will be replaced with the server's name, etc..`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex2")
.setLabel("Member Counts (2/3) - {MemberCount:...}")
.setPlaceholder(
`{:all} - Total member count\n` +
`{:humans} - Total non-bot users\n` +
`{:bots} - Number of bots\n`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex3")
.setLabel("Member who joined (3/3) - {member:...}")
.setPlaceholder(`{:name} - The members name\n`)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("message")
.setPlaceholder("Enter a message to send when someone joins the server")
.setValue(data.message ?? "")
.setLabel("Message")
.setStyle(TextInputStyle.Paragraph)
)
);
const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
);
await i.showModal(modal);
await i.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Welcome Settings")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
],
components: [button]
});
let out: ModalSubmitInteraction | null;
try {
out = (await modalInteractionCollector(m, interaction.user)) as ModalSubmitInteraction | null;
} catch (e) {
console.error(e);
out = null;
}
if (!out) break;
data.message = out.fields.getTextInputValue("message");
break;
}
case "save": {
await i.deferUpdate();
await client.database.guilds.write(interaction.guild!.id, { welcome: data });
config = await client.database.guilds.read(interaction.guild!.id);
data = Object.assign({}, config.welcome);
await client.memory.forceUpdate(interaction.guild!.id);
break;
}
case "channelDM": {
await i.deferUpdate();
data.channel = "dm";
break;
}
case "role": {
await i.deferUpdate();
data.role = null;
break;
}
}
} else if (i.isRoleSelectMenu()) {
await i.deferUpdate();
switch (i.customId) {
case "roleToGive": {
data.role = i.values[0]!;
break;
}
case "roleToPing": {
data.ping = i.values[0]!;
break;
}
}
} else {
await i.deferUpdate();
data.channel = i.values[0]!;
}
} while (!closed);
await interaction.deleteReply();
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageGuild"))
return "You must have the *Manage Server* permission to use this command";
return true;
};
const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
const validReplacements = ["serverName", "memberCount:all", "memberCount:bots", "memberCount:humans"];
if (!interaction.guild) return [];
const prompt = interaction.options.getString("message");
const autocompletions = [];
if (prompt === null) {
for (const replacement of validReplacements) {
autocompletions.push(`{${replacement}}`);
}
return autocompletions;
}
const beforeLastOpenBracket = prompt.match(/(.*){[^{}]{0,15}$/);
const afterLastOpenBracket = prompt.match(/{[^{}]{0,15}$/);
if (beforeLastOpenBracket !== null) {
if (afterLastOpenBracket !== null) {
for (const replacement of validReplacements) {
if (replacement.startsWith(afterLastOpenBracket[0]!.slice(1))) {
autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
}
}
} else {
for (const replacement of validReplacements) {
autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
}
}
} else {
for (const replacement of validReplacements) {
autocompletions.push(`${prompt} {${replacement}}`);
}
}
return autocompletions;
};
export { command, callback, check, autocomplete };