unhardcode self-assignable role stuff

this is pretty old stuff and i'm glad i revamped it bc it's a very good system
This commit is contained in:
Jill 2023-11-27 18:14:08 +03:00
parent b6e92b2a52
commit 319f586565
Signed by: oat
GPG Key ID: 33489AA58A955108
7 changed files with 138 additions and 21 deletions

View File

@ -0,0 +1,21 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema
.createTable('roleSeperators', table => {
table.string('guild');
table.string('role');
table.enum('type', ['color', 'pronoun']);
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema
.dropTable('roleSeperators');
};

View File

@ -1,6 +1,6 @@
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 { getRoleSeperator, isColorRole } from '../lib/assignableRoles';
import * as log from '../lib/log';
import { Command } from '../types/index';
@ -17,8 +17,8 @@ function removeAllColorRoles(member: GuildMember) {
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 seperator = await getRoleSeperator('color', member.guild);
if (!seperator) log.info('no color role seperator found');
const roleSettings: RoleCreateOptions = {
name: color.hex,
@ -27,8 +27,8 @@ async function applyColor(member: GuildMember, color: Color) {
mentionable: false,
permissions: 0n,
};
if (colorRoleSeperator) {
roleSettings.position = colorRoleSeperator.position;
if (seperator) {
roleSettings.position = seperator.position;
}
const role = member.guild.roles.cache.find(role => role.name === color.hex) || await member.guild.roles.create(roleSettings);
@ -46,7 +46,8 @@ export default {
.addBooleanOption((option) =>
option.setName('preview').setDescription('Preview the color instead of applying it')
)
.setDefaultMemberPermissions(0),
.setDefaultMemberPermissions(0)
.setDMPermission(false),
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;

View File

@ -1,6 +1,5 @@
import { Guild, GuildMember, CommandInteraction, Role, SlashCommandBuilder } from 'discord.js';
import { Guild, CommandInteraction, Role, SlashCommandBuilder } from 'discord.js';
import { isColorRole, isPronounRole } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import { Command } from '../types/index';
async function fetchRoleMembers(role: Role) {
@ -25,11 +24,10 @@ async function garbageCollectRoles(guild: Guild, dryRun: boolean): Promise<Role[
export default {
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.'))
.setDefaultMemberPermissions('0'),
serverWhitelist: [...knownServers.firepit],
.setDescription('[ADMIN] Garbage collect unused self-assignable roles')
.addBooleanOption((option) => option.setName('dry-run').setDescription('Only show roles that would be deleted'))
.setDefaultMemberPermissions(0)
.setDMPermission(false),
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;

View File

@ -1,5 +1,5 @@
import { RoleCreateOptions, GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { pronouns, PRONOUN_ROLE_SEPERATOR } from '../lib/assignableRoles';
import { getRoleSeperator, pronouns } from '../lib/assignableRoles';
import * as log from '../lib/log';
import { Command } from '../types/index';
@ -18,7 +18,8 @@ export default {
.setRequired(true)
.setChoices(...Array.from(pronouns.values()).map(extendOption))
)
.setDefaultMemberPermissions(0),
.setDefaultMemberPermissions(0)
.setDMPermission(false),
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
@ -31,8 +32,8 @@ export default {
const pronoun = interaction.options.getString('pronoun', true);
const pronounRoleSeperator = await member.guild.roles.fetch(PRONOUN_ROLE_SEPERATOR);
if (!pronounRoleSeperator) log.error('no pronoun role seperator found?!?!');
const seperator = await getRoleSeperator('color', member.guild);
if (!seperator) log.info('no color role seperator found');
const roleSettings: RoleCreateOptions = {
name: pronoun,
@ -40,8 +41,8 @@ export default {
mentionable: false,
permissions: 0n
};
if (pronounRoleSeperator) {
roleSettings.position = pronounRoleSeperator.position;
if (seperator) {
roleSettings.position = seperator.position;
}
const pronounRole = member.guild.roles.cache.find(role => role.name === pronoun) || await member.guild.roles.create(roleSettings);

79
src/commands/roles.ts Normal file
View File

@ -0,0 +1,79 @@
import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js';
import { Command } from '../types/index';
import { RoleSeperator, RoleSeperatorType, db } from '../lib/db';
function extendOption(t: string) {
return {name: t, value: t};
}
export default {
data: new SlashCommandBuilder()
.setName('roles')
.setDescription('[ADMIN] Manage self-assignable roles')
.addSubcommand(sub =>
sub
.setName('seperator')
.setDescription('[ADMIN] Determine where new roles will be created')
.addStringOption(opt =>
opt
.setName('type')
.setDescription('The type of self-assignable role')
.setChoices(...['color', 'pronoun'].map(extendOption))
.setRequired(true)
)
.addRoleOption(opt =>
opt
.setName('seperator')
.setDescription('Roles will be put *below* this role; omit to put them at the bottom of the wole list')
)
)
.setDefaultMemberPermissions(0)
.setDMPermission(false),
execute: async (interaction: CommandInteraction) => {
if (!interaction.isChatInputCommand()) return;
const member = interaction.member! as GuildMember;
await interaction.deferReply({
ephemeral: true
});
const subcommand = interaction.options.getSubcommand();
if (subcommand === 'seperator') {
const type = interaction.options.getString('type', true) as RoleSeperatorType;
const seperator = interaction.options.getRole('seperator');
if (seperator) {
const highestRole = member.guild.members.me!.roles.highest;
if (seperator.position > highestRole.position)
return interaction.followUp(`This role is above my highest role (${highestRole.toString()}), so I can\`t put roles up there!`);
await db<RoleSeperator>('roleSeperators')
.insert({
guild: member.guild.id,
type,
role: seperator.id,
});
await interaction.followUp(`New ${type} roles will be placed below ${seperator.toString()}.`);
} else {
const role = await db<RoleSeperator>('roleSeperators')
.where('guild', member.guild.id)
.where('type', type)
.returning('*')
.delete();
if (role === 0) {
await interaction.followUp(`\`${type}\` never had a seperator role in this server!`);
} else {
await interaction.followUp(`Unset seperator role for \`${type}\`. Roles will now be placed at the bottom of the role list.`);
}
}
} else {
// ...
}
}
} satisfies Command;

View File

@ -1,5 +1,5 @@
export const COLOR_ROLE_SEPERATOR = '997893394850381834'; // put color roles below this role
export const PRONOUN_ROLE_SEPERATOR = '997893842961440779'; // put pronoun roles below this role
import { Guild } from 'discord.js';
import { RoleSeperator, RoleSeperatorType, db } from './db';
export const pronouns = new Set([
'he/him',
@ -19,4 +19,15 @@ export function isColorRole(name: string) {
export function isPronounRole(name: string) {
return pronouns.has(name);
}
export async function getRoleSeperator(type: RoleSeperatorType, guild: Guild) {
const seperator = await db<RoleSeperator>('roleSeperators')
.select('role')
.where('guild', guild.id)
.where('type', type)
.first();
if (seperator) return await guild.roles.fetch(seperator.role);
return null;
}

View File

@ -96,4 +96,10 @@ export interface ItemBehavior {
item: number,
behavior: string,
value?: number
}
export type RoleSeperatorType = 'color' | 'pronoun';
export interface RoleSeperator {
guild: string,
role: string,
type: RoleSeperatorType,
}