jillo-bot/src/commands/color.ts

133 lines
4.2 KiB
TypeScript

import { RoleCreateOptions, GuildMember, CommandInteraction, 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 * as log from '../lib/log';
import { Command } from '../types/index';
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);
}
export default {
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')
)
.setDefaultPermission(0),
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
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
});
}
}
} satisfies Command;