132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { RoleCreateOptions, GuildMember, Interaction, EmbedBuilder, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } from 'discord.js';
|
|
import { default as parseColor, Color } from 'parse-color';
|
|
import { isColorRole, COLOR_ROLE_SEPERATOR } from '../lib/assignableRoles';
|
|
import { knownServers } from '../lib/knownServers';
|
|
import * as log from '../lib/log';
|
|
|
|
const PREVIEW_DURATION = 1000 * 60;
|
|
|
|
function removeAllColorRoles(member: GuildMember) {
|
|
return Promise.all(member.roles.cache.map(role => {
|
|
if (isColorRole(role.name)) {
|
|
return member.roles.remove(role);
|
|
}
|
|
}));
|
|
}
|
|
|
|
async function applyColor(member: GuildMember, color: Color) {
|
|
await removeAllColorRoles(member);
|
|
|
|
const colorRoleSeperator = await member.guild.roles.fetch(COLOR_ROLE_SEPERATOR);
|
|
if (!colorRoleSeperator) log.error('no color role seperator found?!?!');
|
|
|
|
const roleSettings: RoleCreateOptions = {
|
|
name: color.hex,
|
|
color: color.rgb,
|
|
hoist: false,
|
|
mentionable: false,
|
|
permissions: 0n,
|
|
};
|
|
if (colorRoleSeperator) {
|
|
roleSettings.position = colorRoleSeperator.position;
|
|
}
|
|
|
|
const role = member.guild.roles.cache.find(role => role.name === color.hex) || await member.guild.roles.create(roleSettings);
|
|
await member.roles.add(role);
|
|
}
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('color')
|
|
.setDescription('Change your role color.')
|
|
.addStringOption((option) =>
|
|
option.setName('color')
|
|
.setDescription('Color to set. Must be a parsable HTML color. Examples include: #ff0000, rgb(50, 0, 0), red, etc.')
|
|
)
|
|
.addBooleanOption((option) =>
|
|
option.setName('preview').setDescription('Preview the color instead of applying it')
|
|
),
|
|
|
|
serverWhitelist: [...knownServers.firepit],
|
|
|
|
execute: async (interaction: Interaction, member: GuildMember) => {
|
|
if (!interaction.isChatInputCommand()) return;
|
|
|
|
const color = interaction.options.getString('color');
|
|
const preview = interaction.options.getBoolean('preview');
|
|
|
|
if (color) {
|
|
const parsed = parseColor(color.trim());
|
|
if (!parsed.hex) {
|
|
interaction.reply({
|
|
content: 'Invalid color. Try an HTML color like `#ff0000`, `rgb(50, 0, 0)`, `red`, etc.',
|
|
ephemeral: true
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (preview) {
|
|
const embed = new EmbedBuilder();
|
|
embed.setImage(`https://dummyimage.com/200x200/${parsed.hex.slice(1)}/${parsed.hex.slice(1)}.png`);
|
|
embed.setTitle(`Previewing ${parsed.hex}`);
|
|
|
|
const apply = new ButtonBuilder()
|
|
.setLabel('Apply')
|
|
.setStyle(ButtonStyle.Primary)
|
|
.setCustomId(`apply-color-${parsed.hex}`);
|
|
|
|
const row = new ActionRowBuilder<ButtonBuilder>()
|
|
.addComponents(apply);
|
|
|
|
const message = await interaction.reply({
|
|
embeds: [embed],
|
|
components: [row],
|
|
fetchReply: true
|
|
});
|
|
|
|
const collector = (interaction.channel as TextChannel).createMessageComponentCollector({
|
|
filter: (i) => i.message.id === message.id,
|
|
time: PREVIEW_DURATION,
|
|
});
|
|
|
|
collector.on('collect', async (i) => {
|
|
if (i.customId !== `apply-color-${parsed.hex}`) return;
|
|
if (i.member === member) {
|
|
collector.stop();
|
|
await i.deferReply({ephemeral: false});
|
|
row.components[0].setDisabled(true);
|
|
await message.edit({
|
|
embeds: [embed],
|
|
components: [row]
|
|
});
|
|
await applyColor(member, parsed);
|
|
await i.followUp({
|
|
content: 'Color applied.'
|
|
});
|
|
} else {
|
|
i.reply({
|
|
content: 'You didn\'t run the command!',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
await interaction.deferReply({
|
|
ephemeral: true
|
|
});
|
|
|
|
await applyColor(member, parsed);
|
|
|
|
interaction.followUp({
|
|
content: 'Your color has been set.'
|
|
});
|
|
}
|
|
} else {
|
|
await removeAllColorRoles(member);
|
|
interaction.reply({
|
|
content: 'Removed all colors.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
}
|
|
}; |