Skip to content

Commit

Permalink
Added primative permissions system
Browse files Browse the repository at this point in the history
  • Loading branch information
Struck713 committed Feb 23, 2024
1 parent 9f32dbd commit 152d278
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 58 deletions.
1 change: 1 addition & 0 deletions permissions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions roles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
30 changes: 27 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ActivityType, Client, Events, GatewayIntentBits } from "discord.js";
import { ActivityType, Client, Events, GatewayIntentBits, PermissionsBitField } from "discord.js";
import { youtube, token } from '../config.json';
import commands from "./commands";
import YTDlpWrap from "yt-dlp-wrap";
import { Embeds } from "./utils/embeds";
import { permissionManager } from "./lib/permissions";
import * as Style from "./utils/style";


export const ytdl = new YTDlpWrap(youtube.binary_path);
export const client = new Client({
Expand All @@ -29,13 +32,34 @@ client.once(Events.ClientReady, async client => {

client.on(Events.InteractionCreate, async interaction => {
if (interaction.isChatInputCommand()) {
const command = commands.find(command => command.data.name === interaction.commandName);
const command = commands.find(command => command.metadata.name === interaction.commandName);
if (!command) {
await Embeds.error(interaction, "Something went terribly wrong! Contact the developer.");
return;
}
await interaction.deferReply();
command.execute(client, interaction);

const guild = interaction.guild;
if (!guild) {
await Embeds.error(interaction, `${Style.NAME} does not current support DMs!`);
return;
}

const user = guild.members.cache.get(interaction.user.id);
if (!user) {
await Embeds.error(interaction, "You aren't a member of this server?? Something went wrong.");
return;
}

if (command.permission && !user.permissions.has(PermissionsBitField.All, true)) {
const level = permissionManager.getPermissionLevel(guild.id, user.id);
if (command.permission > level) {
await Embeds.error(interaction, "You do not have permission to execute this command!");
return;
}
}

command.execute(client, user, interaction);
}
});

Expand Down
45 changes: 23 additions & 22 deletions src/commands/admin/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,39 @@ import { Embeds } from "../../utils/embeds";
import commands from "..";

import { token, development } from '../../../config.json';
import { PermissionLevel } from "../../lib/permissions";

const MY_SNOWFLAKE = "140520164629151744";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName('deploy')
.setDescription('Deploys slash commands for the bot.'),
execute: async (_, interaction) => {
if (interaction.user.id === MY_SNOWFLAKE) {
permission: PermissionLevel.ADMIN,
execute: async (_, user, interaction) => {
if (user.id === MY_SNOWFLAKE) {
const rest = new REST().setToken(token);
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationCommands(development.application_id),
{ body: commands.map(command => command.metadata) },
) as any[];

// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
// Routes.applicationGuildCommands(DEVELOPMENT.APPLICATION_ID, DEVELOPMENT.GUILD_ID)
const data = await rest.put(
Routes.applicationCommands(development.application_id),
{ body: commands.map(command => command.data) },
) as any[];
let response = JSON.stringify(data, null, 2);
if (response.length > 2000) response = response.substring(0, 2000) + "...";

console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
await Embeds.create()
.setTitle("Deployed commands")
.setDescription(`${commands.length} commands have been deployed.`)
.send(interaction);
.setTitle("Deployed commands")
.setAuthor({ name: `${commands.length} commands have been deployed.` })
.setDescription(`
\`\`\`JSON
${response}
\`\`\`
`)
.send(interaction);

console.log(`Successfully reloaded ${data.length} application (/) commands.`);

} else await Embeds.error(interaction, "You do not have permission to execute this command!");
},
}
49 changes: 49 additions & 0 deletions src/commands/admin/perms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { SlashCommandBuilder } from "discord.js";
import { Command } from "../../lib/command";
import { Embeds } from "../../utils/embeds";
import { PermissionLevel, getNameFromPermissionLevel, permissionManager } from "../../lib/permissions";

export default <Command>{
metadata: new SlashCommandBuilder()
.setName('perms')
.setDescription('Changes internal bot permissions for a user')
.addSubcommand(subcommand => subcommand
.setName("set")
.setDescription("Sets a user's permission level")
.addUserOption(option => option.setName("user").setDescription("The user to set the permission level for").setRequired(true))
.addStringOption(option => option.setName("level")
.setDescription("The permission level to set the user to")
.setChoices(...Object.keys(PermissionLevel).map(level => ({ name: level, value: level })))
.setRequired(true))
)
.addSubcommand(subcommand => subcommand
.setName("view")
.setDescription("View a users permission level")
.addUserOption(option => option.setName("user").setDescription("The user to set the permission level for").setRequired(true))
)
.setDMPermission(false),
permission: PermissionLevel.ADMIN,
execute: async (_, user, interaction) => {
const subcommand = interaction.options.getSubcommand();
if (subcommand == "set") {
const level = interaction.options.getString("level", true);
const target = interaction.options.getUser("user", true);
const permissionLevel = PermissionLevel[level as keyof typeof PermissionLevel];

permissionManager.setPermissionLevel(interaction.guild!.id, target.id, permissionLevel);

await Embeds.create()
.setTitle("Permission Level")
.setDescription(`${target.toString()}'s permission level has been changed to \`${getNameFromPermissionLevel(permissionLevel)}\``)
.send(interaction);
} else if (subcommand == "view") {
const target = interaction.options.getUser("user", true);
const permissionLevel = permissionManager.getPermissionLevel(interaction.guild!.id, target.id);

await Embeds.create()
.setTitle("Permission Level")
.setDescription(`${target.toString()}'s permission level is \`${getNameFromPermissionLevel(permissionLevel)}\``)
.send(interaction);
}
},
}
3 changes: 2 additions & 1 deletion src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Status from "./misc/status";
import Meme from "./silly/meme";
import Help from "./misc/help";
import Oobinate from "./silly/oob";
import Perms from "./admin/perms";

const commands = [ Help, Play, Skip, Queue, Stop, Meme, Oobinate, Status, Deploy ];
const commands = [ Help, Play, Skip, Queue, Stop, Meme, Oobinate, Status, Deploy, Perms ];
export default commands;
6 changes: 3 additions & 3 deletions src/commands/misc/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { Embeds } from "../../utils/embeds";
import commands from "..";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName('help')
.setDescription('Lists all the available commands.'),
execute: async (_, interaction) => {
execute: async (_, user, interaction) => {
await Embeds.create()
.setTitle("Help")
.setDescription(`There's a lot I can do for you. Here is a list of the avaliable commands:`)
.addFields(commands.map(command => ({ name: `/${command.data.name}`, value: command.data.description, inline: true })))
.addFields(commands.map(command => ({ name: `/${command.metadata.name}`, value: command.metadata.description, inline: true })))
.send(interaction);
},
}
4 changes: 2 additions & 2 deletions src/commands/misc/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import Time from "../../utils/time";
const startup = Date.now();

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName('status')
.setDescription('Shows the current status for the bot.'),
execute: async (_, interaction) => {
execute: async (_, user, interaction) => {
let sent = Date.now();
await Embeds.create()
.setTitle("Status")
Expand Down
4 changes: 2 additions & 2 deletions src/commands/silly/meme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import axios from "axios";
import { Embeds } from "../../utils/embeds";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName('meme')
.setDescription('Create a meme with top and bottom text.')
.addStringOption(option => option.setName("top").setRequired(true).setDescription("The text on the top of the meme."))
.addAttachmentOption(option => option.setName("image").setRequired(true).setDescription("The image to create a meme out of."))
.addStringOption(option => option.setName("bottom").setRequired(false).setDescription("The text on the bottom of the meme.")),
execute: async (client, interaction) => {
execute: async (client, user, interaction) => {
let top = interaction.options.get("top", true);
let bottom = interaction.options.get("bottom", false);
let { attachment } = interaction.options.get("image", true);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/silly/oob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Command } from "../../lib/command";
import { Embeds } from "../../utils/embeds";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName('oobinate')
.setDescription('Converts normal text into oob text.')
.addStringOption(option => option.setName("text").setRequired(true).setDescription("The text to oobinate.")),
execute: async (client, interaction) => {
execute: async (client, user, interaction) => {
let text = interaction.options.get("text", true);
await Embeds.create()
.setTitle("Oobinator")
Expand Down
9 changes: 4 additions & 5 deletions src/commands/voice/play.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ import YouTubeAPI from "../../utils/youtube";
import * as VoiceManager from "../../lib/voice";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName("play")
.setDescription("Play a song by search.")
.addStringOption(option => option.setName("search").setRequired(true).setDescription("A valid YouTube search, picking the top result.")),
execute: async (_, interaction) => {
execute: async (_, user, interaction) => {

if (!interaction.guild || !interaction.member) {
if (!interaction.guild) {
await Embeds.error(interaction, "You are not in a guild!");
return;
}

let user = await interaction.guild.members.cache.get(interaction.member.user.id);
if (!user?.voice.channel) {
if (!user.voice.channel) {
await Embeds.error(interaction, "You are not in a voice channel!");
return;
}
Expand Down
9 changes: 4 additions & 5 deletions src/commands/voice/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import { Text } from "../../utils/misc";
import Time from "../../utils/time";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName("queue")
.setDescription("View the current song queue."),
execute: async (_, interaction) => {
execute: async (_, user, interaction) => {

if (!interaction.guild || !interaction.member) {
if (!interaction.guild) {
await Embeds.error(interaction, "You are not in a guild!");
return;
}

let user = await interaction.guild.members.cache.get(interaction.member.user.id);
if (!user?.voice.channel) {
if (!user.voice.channel) {
await Embeds.error(interaction, "You are not in a voice channel!");
return;
}
Expand Down
9 changes: 4 additions & 5 deletions src/commands/voice/skip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import { Embeds } from "../../utils/embeds";
import * as VoiceManager from "../../lib/voice";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName("skip")
.setDescription("Skip to the next song in the queue."),
execute: async (_, interaction) => {
if (!interaction.guild || !interaction.member) {
execute: async (_, user, interaction) => {
if (!interaction.guild) {
await Embeds.error(interaction, "You are not in a guild!");
return;
}

let user = await interaction.guild.members.cache.get(interaction.member.user.id);
if (!user?.voice.channel) {
if (!user.voice.channel) {
await Embeds.error(interaction, "You are not in a voice channel!");
return;
}
Expand Down
9 changes: 4 additions & 5 deletions src/commands/voice/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import { Embeds } from "../../utils/embeds";
import * as VoiceManager from "../../lib/voice";

export default <Command>{
data: new SlashCommandBuilder()
metadata: new SlashCommandBuilder()
.setName("stop")
.setDescription("Disconnect from voice and clear the queue."),
execute: async (_, interaction) => {
execute: async (_, user, interaction) => {

if (!interaction.guild || !interaction.member) {
if (!interaction.guild) {
await Embeds.error(interaction, "You are not in a guild!");
return;
}

let user = await interaction.guild.members.cache.get(interaction.member.user.id);
if (!user?.voice.channel) {
if (!user.voice.channel) {
await Embeds.error(interaction, "You are not in a voice channel!");
return;
}
Expand Down
8 changes: 5 additions & 3 deletions src/lib/command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Client, SlashCommandBuilder, ChatInputCommandInteraction } from "discord.js";
import { ChatInputCommandInteraction, Client, GuildMember, SlashCommandBuilder } from "discord.js";
import { PermissionLevel } from "./permissions";

export interface Command {
data: SlashCommandBuilder | Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
execute: (client: Client, interaction: ChatInputCommandInteraction) => void;
metadata: SlashCommandBuilder | Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
permission?: PermissionLevel;
execute: (client: Client, user: GuildMember, interaction: ChatInputCommandInteraction) => void;
}
Loading

0 comments on commit 152d278

Please sign in to comment.