mirror of https://github.com/clickscodes/nucleus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
13 KiB
302 lines
13 KiB
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 };
|