colors & pronouns
This commit is contained in:
parent
c34497c61a
commit
4cdd583eea
|
@ -22,6 +22,7 @@
|
|||
"@types/parse-color": "^1.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.27.1",
|
||||
"@typescript-eslint/parser": "^5.27.1",
|
||||
"discord-api-types": "^0.36.2",
|
||||
"eslint": "^8.17.0",
|
||||
"typescript": "4.8.0-dev.20220606"
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ specifiers:
|
|||
'@types/parse-color': ^1.0.1
|
||||
'@typescript-eslint/eslint-plugin': ^5.27.1
|
||||
'@typescript-eslint/parser': ^5.27.1
|
||||
discord-api-types: ^0.36.2
|
||||
discord.js: ^13.8.0
|
||||
eslint: ^8.17.0
|
||||
parse-color: ^1.0.0
|
||||
|
@ -23,6 +24,7 @@ devDependencies:
|
|||
'@types/parse-color': 1.0.1
|
||||
'@typescript-eslint/eslint-plugin': 5.27.1_555veff7pbs3zkovuk7bd6t3v4
|
||||
'@typescript-eslint/parser': 5.27.1_tk6tc2atvak5hdwmpl7y3anzei
|
||||
discord-api-types: 0.36.2
|
||||
eslint: 8.17.0
|
||||
typescript: 4.8.0-dev.20220606
|
||||
|
||||
|
@ -461,6 +463,10 @@ packages:
|
|||
resolution: {integrity: sha512-Y6RMvXsHKiBgQhm/q5MgRieXc4Tzh5p/JuDyqreI48lmy+AQfO+g9Xhz0tuGBaN1FtsrLT7mD+lbFONPo5vdwA==}
|
||||
dev: false
|
||||
|
||||
/discord-api-types/0.36.2:
|
||||
resolution: {integrity: sha512-TunPAvzwneK/m5fr4hxH3bMsrtI22nr9yjfHyo5NBGMjpsAauGNiGCmwoFf0oO3jSd2mZiKUvZwCKDaB166u2Q==}
|
||||
dev: true
|
||||
|
||||
/discord.js/13.8.0:
|
||||
resolution: {integrity: sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==}
|
||||
engines: {node: '>=16.6.0', npm: '>=7.0.0'}
|
||||
|
|
|
@ -42,7 +42,12 @@ module.exports = {
|
|||
data: new SlashCommandBuilder()
|
||||
.setName('change')
|
||||
.setDescription('Change')
|
||||
.addStringOption((option) => option.setName('what').setDescription('Change what? Examples include: "environment", "perspective", etc...').setRequired(true)),
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('what')
|
||||
.setDescription('Change what? Examples include: "environment", "perspective", etc...')
|
||||
.setRequired(true)
|
||||
),
|
||||
|
||||
execute: async (interaction: CommandInteraction, member: GuildMember) => {
|
||||
const what = interaction.options.getString('what')!;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { SlashCommandBuilder } from '@discordjs/builders';
|
||||
import { CommandInteraction, Guild, GuildMember } from 'discord.js';
|
||||
import { default as parseColor } from 'parse-color';
|
||||
import { CommandInteraction, CreateRoleOptions, GuildMember, Message, MessageActionRow, MessageButton, MessageEmbed, TextChannel } from 'discord.js';
|
||||
import { default as parseColor, Color } from 'parse-color';
|
||||
import { isColorRole, COLOR_ROLE_SEPERATOR } from '../lib/assignableRoles';
|
||||
|
||||
function isColorRole(name: string) {
|
||||
return name.startsWith('#') && name.length === 7;
|
||||
}
|
||||
const PREVIEW_DURATION = 1000 * 60;
|
||||
|
||||
function removeAllColorRoles(member: GuildMember) {
|
||||
return Promise.all(member.roles.cache.map(role => {
|
||||
|
@ -14,50 +13,109 @@ function removeAllColorRoles(member: GuildMember) {
|
|||
}));
|
||||
}
|
||||
|
||||
function garbageCollectRoles(guild: Guild) {
|
||||
return Promise.all(guild.roles.cache.map(role => {
|
||||
if (isColorRole(role.name)) {
|
||||
const members = role.members;
|
||||
if (members.size === 0) {
|
||||
return role.delete();
|
||||
}
|
||||
}
|
||||
}));
|
||||
async function applyColor(member: GuildMember, color: Color) {
|
||||
await removeAllColorRoles(member);
|
||||
|
||||
const colorRoleSeperator = await member.guild.roles.fetch(COLOR_ROLE_SEPERATOR);
|
||||
if (!colorRoleSeperator) console.error('no color role seperator found?!?!');
|
||||
|
||||
const roleSettings: CreateRoleOptions = {
|
||||
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.')),
|
||||
.addStringOption((option) =>
|
||||
option.setName('color')
|
||||
.setDescription('Color to set. Must be a parsable HTML color. Examples include: #ff0000, rgb(50, 0, 0), red, etc.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addBooleanOption((option) =>
|
||||
option.setName('preview').setDescription('Preview the color instead of applying it')
|
||||
),
|
||||
|
||||
execute: async (interaction: CommandInteraction, member: GuildMember) => {
|
||||
const color = interaction.options.getString('color');
|
||||
const preview = interaction.options.getBoolean('preview');
|
||||
|
||||
if (color) {
|
||||
const parsed = parseColor(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,
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
await removeAllColorRoles(member);
|
||||
|
||||
const role = member.guild.roles.cache.find(role => role.name === parsed.hex) || await member.guild.roles.create({
|
||||
name: parsed.hex,
|
||||
color: parsed.rgb,
|
||||
hoist: false,
|
||||
mentionable: false,
|
||||
permissions: 0n,
|
||||
});
|
||||
await member.roles.add(role);
|
||||
//await garbageCollectRoles(member.guild);
|
||||
interaction.reply({
|
||||
content: 'Your color has been set.',
|
||||
ephemeral: true
|
||||
});
|
||||
if (preview) {
|
||||
const embed = new MessageEmbed();
|
||||
embed.setImage(`https://dummyimage.com/200x200/${parsed.hex.slice(1)}/${parsed.hex.slice(1)}.png`);
|
||||
embed.setTitle(`Previewing ${parsed.hex}`);
|
||||
|
||||
const row = new MessageActionRow().addComponents(
|
||||
new MessageButton()
|
||||
.setLabel('Apply')
|
||||
.setStyle('PRIMARY')
|
||||
.setCustomId(`apply-color-${parsed.hex}`)
|
||||
);
|
||||
|
||||
const message = (await interaction.reply({
|
||||
embeds: [embed],
|
||||
components: [row],
|
||||
fetchReply: true
|
||||
})) as Message;
|
||||
|
||||
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({
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { SlashCommandBuilder } from '@discordjs/builders';
|
||||
import { CommandInteraction, Guild, GuildMember, Role } from 'discord.js';
|
||||
import { isColorRole, isPronounRole } from '../lib/assignableRoles';
|
||||
|
||||
async function fetchRoleMembers(role: Role) {
|
||||
const members = await role.guild.members.fetch();
|
||||
return members.filter(m => m.roles.cache.has(role.id));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async function garbageCollectRoles(guild: Guild, dryRun: boolean): Promise<Role[]> {
|
||||
const roles = guild.roles.cache;
|
||||
const garbageRoles = roles.filter((r: Role) => isColorRole(r.name) || isPronounRole(r.name));
|
||||
const deletedRoles = garbageRoles.map(async (role: Role) => {
|
||||
const members = await fetchRoleMembers(role);
|
||||
if (members.size === 0) {
|
||||
if (dryRun) return role;
|
||||
return await role.delete('garbage collection');
|
||||
}
|
||||
});
|
||||
return (await Promise.all(deletedRoles)).filter(r => r) as Role[];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('garbage-collect-roles')
|
||||
.setDescription('Garbage collect unused roles for colors and pronouns.')
|
||||
.addBooleanOption((option) => option.setName('dry-run').setDescription('Only show roles that would be deleted.'))
|
||||
.setDefaultPermission(false),
|
||||
|
||||
execute: async (interaction: CommandInteraction, member: GuildMember) => {
|
||||
await interaction.deferReply({
|
||||
ephemeral: false
|
||||
});
|
||||
|
||||
const dryrun = interaction.options.getBoolean('dry-run');
|
||||
const colorRoles = await garbageCollectRoles(member.guild, dryrun || false);
|
||||
|
||||
if (dryrun) {
|
||||
interaction.followUp({
|
||||
content: `${colorRoles.length} role${colorRoles.length === 1 ? '' : 's'} would be deleted${colorRoles.length === 0 ? '.' : ':\n'}${colorRoles.map(r => `\`${r.name}\``).join('\n')}`,
|
||||
ephemeral: true,
|
||||
});
|
||||
} else {
|
||||
interaction.followUp({
|
||||
content: `${colorRoles.length} role${colorRoles.length === 1 ? ' was' : 's were'} deleted${colorRoles.length === 0 ? '.' : ':\n'}${colorRoles.map(r => `\`${r.name}\``).join('\n')}`,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -26,7 +26,12 @@ module.exports = {
|
|||
data: new SlashCommandBuilder()
|
||||
.setName('monitor')
|
||||
.setDescription('Monitor')
|
||||
.addStringOption((option) => option.setName('what').setDescription('Monitor what? Examples include: "lobby", "bedroom", "park", "playground", etc...').setRequired(true)),
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('what')
|
||||
.setDescription('Monitor what? Examples include: "lobby", "bedroom", "park", "playground", etc...')
|
||||
.setRequired(true)
|
||||
),
|
||||
|
||||
execute: async (interaction: CommandInteraction) => {
|
||||
let img;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import { SlashCommandBuilder } from '@discordjs/builders';
|
||||
import { CommandInteraction, CreateRoleOptions, GuildMember } from 'discord.js';
|
||||
import { pronouns, PRONOUN_ROLE_SEPERATOR } from '../lib/assignableRoles';
|
||||
|
||||
function extendOption(t: string) {
|
||||
return {name: t, value: t};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('pronoun')
|
||||
.setDescription('Give yourself a pronoun or two.')
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('pronoun')
|
||||
.setDescription('Pronoun to add. If you already have a pronoun, you can specify it to remove it.')
|
||||
.setRequired(true)
|
||||
.setChoices(...Array.from(pronouns.values()).map(extendOption))
|
||||
),
|
||||
|
||||
execute: async (interaction: CommandInteraction, member: GuildMember) => {
|
||||
await interaction.deferReply({
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
const pronoun = interaction.options.getString('pronoun') as string;
|
||||
|
||||
const pronounRoleSeperator = await member.guild.roles.fetch(PRONOUN_ROLE_SEPERATOR);
|
||||
if (!pronounRoleSeperator) console.error('no pronoun role seperator found?!?!');
|
||||
|
||||
const roleSettings: CreateRoleOptions = {
|
||||
name: pronoun,
|
||||
hoist: false,
|
||||
mentionable: false,
|
||||
permissions: 0n
|
||||
};
|
||||
if (pronounRoleSeperator) {
|
||||
roleSettings.position = pronounRoleSeperator.position;
|
||||
}
|
||||
|
||||
const pronounRole = member.guild.roles.cache.find(role => role.name === pronoun) || await member.guild.roles.create(roleSettings);
|
||||
|
||||
if (member.roles.cache.has(pronounRole.id)) {
|
||||
await member.roles.remove(pronounRole);
|
||||
interaction.followUp({
|
||||
content: `Removed \`${pronoun}\` from your roles.`
|
||||
});
|
||||
} else {
|
||||
await member.roles.add(pronounRole);
|
||||
interaction.followUp({
|
||||
content: `Added \`${pronoun}\` to your roles.`
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
|
@ -62,6 +62,7 @@ const bot = new Discord.Client({
|
|||
Discord.Intents.FLAGS.GUILD_MESSAGES,
|
||||
Discord.Intents.FLAGS.GUILD_VOICE_STATES,
|
||||
Discord.Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
|
||||
Discord.Intents.FLAGS.GUILD_MEMBERS,
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
export const COLOR_ROLE_SEPERATOR = '997893394850381834'; // put color roles below this role
|
||||
export const PRONOUN_ROLE_SEPERATOR = '997893842961440779'; // put pronoun roles below this role
|
||||
|
||||
export const pronouns = new Set([
|
||||
'he/him',
|
||||
'she/her',
|
||||
'they/them',
|
||||
'it/its',
|
||||
'fae/faer',
|
||||
'no pronouns',
|
||||
'any pronouns',
|
||||
'was/were',
|
||||
'will/would',
|
||||
]);
|
||||
|
||||
export function isColorRole(name: string) {
|
||||
return name.startsWith('#') && name.length === 7;
|
||||
}
|
||||
|
||||
export function isPronounRole(name: string) {
|
||||
return pronouns.has(name);
|
||||
}
|
Loading…
Reference in New Issue