Add permissions system

This commit is contained in:
Max Isom 2022-01-29 11:20:40 -05:00
parent 8e00726dc2
commit 1621b2c281
No known key found for this signature in database
GPG key ID: 25C9B1A7F6798880
8 changed files with 184 additions and 5 deletions

View file

@ -0,0 +1,19 @@
/*
Warnings:
- You are about to drop the column `channel` on the `Setting` table. All the data in the column will be lost.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Setting" (
"guildId" TEXT NOT NULL PRIMARY KEY,
"playlistLimit" INTEGER NOT NULL DEFAULT 50,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Setting" ("createdAt", "guildId", "playlistLimit", "updatedAt") SELECT "createdAt", "guildId", "playlistLimit", "updatedAt" FROM "Setting";
DROP TABLE "Setting";
ALTER TABLE "new_Setting" RENAME TO "Setting";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Setting" ADD COLUMN "roleId" TEXT;

View file

@ -25,8 +25,8 @@ model KeyValueCache {
model Setting { model Setting {
guildId String @id guildId String @id
channel String?
playlistLimit Int @default(50) playlistLimit Int @default(50)
roleId String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
} }

View file

@ -13,6 +13,7 @@ import Config from './services/config.js';
import {generateDependencyReport} from '@discordjs/voice'; import {generateDependencyReport} from '@discordjs/voice';
import {REST} from '@discordjs/rest'; import {REST} from '@discordjs/rest';
import {Routes} from 'discord-api-types/v9'; import {Routes} from 'discord-api-types/v9';
import updatePermissionsForGuild from './utils/update-permissions-for-guild.js';
@injectable() @injectable()
export default class { export default class {
@ -146,6 +147,10 @@ export default class {
); );
} }
// Update permissions
spinner.text = '📡 updating permissions...';
await Promise.all(this.client.guilds.cache.map(async guild => updatePermissionsForGuild(guild)));
spinner.succeed(`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.client.user?.id ?? ''}&scope=bot%20applications.commands&permissions=2184236096`); spinner.succeed(`Ready! Invite the bot with https://discordapp.com/oauth2/authorize?client_id=${this.client.user?.id ?? ''}&scope=bot%20applications.commands&permissions=2184236096`);
}); });

103
src/commands/config.ts Normal file
View file

@ -0,0 +1,103 @@
import {SlashCommandBuilder} from '@discordjs/builders';
import {CommandInteraction, MessageEmbed} from 'discord.js';
import {injectable} from 'inversify';
import {prisma} from '../utils/db.js';
import updatePermissionsForGuild from '../utils/update-permissions-for-guild.js';
import Command from './index.js';
@injectable()
export default class implements Command {
public readonly slashCommand = new SlashCommandBuilder()
.setName('config')
.setDescription('configure bot settings')
.addSubcommand(subcommand => subcommand
.setName('set-playlist-limit')
.setDescription('set the maximum number of tracks that can be added from a playlist')
.addIntegerOption(option => option
.setName('limit')
.setDescription('maximum number of tracks')
.setRequired(true)))
.addSubcommand(subcommand => subcommand
.setName('set-role')
.setDescription('set the role that is allowed to use the bot')
.addRoleOption(option => option
.setName('role')
.setDescription('allowed role')
.setRequired(true)))
.addSubcommand(subcommand => subcommand
.setName('get')
.setDescription('show all settings'));
async execute(interaction: CommandInteraction) {
switch (interaction.options.getSubcommand()) {
case 'set-playlist-limit': {
const limit = interaction.options.getInteger('limit')!;
if (limit < 1) {
throw new Error('invalid limit');
}
await prisma.setting.update({
where: {
guildId: interaction.guild!.id,
},
data: {
playlistLimit: limit,
},
});
await interaction.reply('👍 limit updated');
break;
}
case 'set-role': {
const role = interaction.options.getRole('role')!;
await prisma.setting.update({
where: {
guildId: interaction.guild!.id,
},
data: {
roleId: role.id,
},
});
await updatePermissionsForGuild(interaction.guild!);
await interaction.reply('👍 role updated');
break;
}
case 'get': {
const embed = new MessageEmbed().setTitle('Config');
const config = await prisma.setting.findUnique({where: {guildId: interaction.guild!.id}});
if (!config) {
throw new Error('no config found');
}
const settingsToShow = {
'Playlist Limit': config.playlistLimit,
Role: config.roleId ? `<@&${config.roleId}>` : 'not set',
};
let description = '';
for (const [key, value] of Object.entries(settingsToShow)) {
description += `**${key}**: ${value}\n`;
}
embed.setDescription(description);
await interaction.reply({embeds: [embed]});
break;
}
default:
throw new Error('unknown subcommand');
}
}
}

View file

@ -75,6 +75,7 @@ export default class implements Command {
} }
async handleAutocompleteInteraction(interaction: AutocompleteInteraction) { async handleAutocompleteInteraction(interaction: AutocompleteInteraction) {
const subcommand = interaction.options.getSubcommand();
const query = interaction.options.getString('name')!.trim(); const query = interaction.options.getString('name')!.trim();
const favorites = await prisma.favoriteQuery.findMany({ const favorites = await prisma.favoriteQuery.findMany({
@ -83,13 +84,16 @@ export default class implements Command {
}, },
}); });
const names = favorites.map(favorite => favorite.name); let results = query === '' ? favorites : favorites.filter(f => f.name.startsWith(query));
const results = query === '' ? names : names.filter(name => name.startsWith(query)); if (subcommand === 'remove') {
// Only show favorites that user is allowed to remove
results = interaction.member?.user.id === interaction.guild?.ownerId ? results : results.filter(r => r.authorId === interaction.member!.user.id);
}
await interaction.respond(results.map(r => ({ await interaction.respond(results.map(r => ({
name: r, name: r.name,
value: r, value: r.name,
}))); })));
} }

View file

@ -15,6 +15,7 @@ import GetSongs from './services/get-songs.js';
// Comands // Comands
import Command from './commands'; import Command from './commands';
import Clear from './commands/clear.js'; import Clear from './commands/clear.js';
import Config from './commands/config.js';
import Disconnect from './commands/disconnect.js'; import Disconnect from './commands/disconnect.js';
import Favorites from './commands/favorites.js'; import Favorites from './commands/favorites.js';
import ForwardSeek from './commands/fseek.js'; import ForwardSeek from './commands/fseek.js';
@ -54,6 +55,7 @@ container.bind<AddQueryToQueue>(TYPES.Services.AddQueryToQueue).to(AddQueryToQue
// Commands // Commands
[ [
Clear, Clear,
Config,
Disconnect, Disconnect,
Favorites, Favorites,
ForwardSeek, ForwardSeek,

View file

@ -0,0 +1,44 @@
import {ApplicationCommandPermissionData, Guild} from 'discord.js';
import {prisma} from './db.js';
const COMMANDS_TO_LIMIT_TO_GUILD_OWNER = ['config'];
const updatePermissionsForGuild = async (guild: Guild) => {
const settings = await prisma.setting.findUnique({
where: {
guildId: guild.id,
},
});
if (!settings) {
throw new Error('could not find settings for guild');
}
const permissions: ApplicationCommandPermissionData[] = [
{
id: guild.ownerId,
type: 'USER',
permission: true,
},
{
id: guild.roles.everyone.id,
type: 'ROLE',
permission: false,
},
];
const commands = await guild.commands.fetch();
await guild.commands.permissions.set({fullPermissions: commands.map(command => ({
id: command.id,
permissions: COMMANDS_TO_LIMIT_TO_GUILD_OWNER.includes(command.name) ? permissions : [
...permissions,
...(settings.roleId ? [{
id: settings.roleId,
type: 'ROLE' as const,
permission: true,
}] : []),
],
}))});
};
export default updatePermissionsForGuild;