summary refs log tree commit diff
path: root/src/util/Permissions.ts
blob: 89e1d1d374a7b3e6e6d248b8a9518fe235d4616b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js
// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah

import { ChannelPermissionOverwrite } from "../models/Channel";
import { Role } from "../models/Role";
import { BitField } from "./BitField";

export type PermissionResolvable = string | number | Permissions | PermissionResolvable[];

export class Permissions extends BitField {
	static FLAGS = {
		CREATE_INSTANT_INVITE: 1n << 0n,
		KICK_MEMBERS: 1n << 1n,
		BAN_MEMBERS: 1n << 2n,
		ADMINISTRATOR: 1n << 3n,
		MANAGE_CHANNELS: 1n << 4n,
		MANAGE_GUILD: 1n << 5n,
		ADD_REACTIONS: 1n << 6n,
		VIEW_AUDIT_LOG: 1n << 7n,
		PRIORITY_SPEAKER: 1n << 8n,
		STREAM: 1n << 9n,
		VIEW_CHANNEL: 1n << 10n,
		SEND_MESSAGES: 1n << 11n,
		SEND_TTS_MESSAGES: 1n << 12n,
		MANAGE_MESSAGES: 1n << 13n,
		EMBED_LINKS: 1n << 14n,
		ATTACH_FILES: 1n << 15n,
		READ_MESSAGE_HISTORY: 1n << 16n,
		MENTION_EVERYONE: 1n << 17n,
		USE_EXTERNAL_EMOJIS: 1n << 18n,
		VIEW_GUILD_INSIGHTS: 1n << 19n,
		CONNECT: 1n << 20n,
		SPEAK: 1n << 21n,
		MUTE_MEMBERS: 1n << 22n,
		DEAFEN_MEMBERS: 1n << 23n,
		MOVE_MEMBERS: 1n << 24n,
		USE_VAD: 1n << 25n,
		CHANGE_NICKNAME: 1n << 26n,
		MANAGE_NICKNAMES: 1n << 27n,
		MANAGE_ROLES: 1n << 28n,
		MANAGE_WEBHOOKS: 1n << 29n,
		MANAGE_EMOJIS: 1n << 30n,
	};

	any(permission: PermissionResolvable, checkAdmin = true) {
		return (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.any(permission);
	}

	/**
	 * Checks whether the bitfield has a permission, or multiple permissions.
	 */
	has(permission: PermissionResolvable, checkAdmin = true) {
		return (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.has(permission);
	}

	static channelPermission(overwrites: ChannelPermissionOverwrite[], init?: bigint) {
		// channelOverwrites.filter((x) => x.type === 1 && x.id !== user.id);
		return overwrites.reduce((permission, overwrite) => {
			// apply disallowed permission
			// * permission: current calculated permission (e.g. 010)
			// * deny contains all denied permissions (e.g. 011)
			// * allow contains all explicitly allowed permisions (e.g. 100)
			return (permission & ~overwrite.deny) | overwrite.allow;
			// ~ operator inverts deny (e.g. 011 -> 100)
			// & operator only allows 1 for both ~deny and permission (e.g. 010 & 100 -> 000)
			// | operators adds both together (e.g. 000 + 100 -> 100)
		}, 0n ?? init);
	}

	static rolePermission(roles: Role[]) {
		// adds all permissions of all roles together (Bit OR)
		return roles.reduce((permission, role) => permission | role.permissions, 0n);
	}

	static finalPermission({
		user,
		guild,
		channel,
	}: {
		user: { id: bigint; roles: bigint[] };
		guild: { roles: Role[] };
		channel?: {
			overwrites: ChannelPermissionOverwrite[];
		};
	}) {
		let roles = guild.roles.filter((x) => user.roles.includes(x.id));
		let permission = Permissions.rolePermission(roles);

		if (channel?.overwrites) {
			let overwrites = channel.overwrites.filter((x) => {
				if (x.type === 0 && user.roles.includes(x.id)) return true;
				if (x.type === 1 && x.id == user.id) return true;
				return false;
			});
			permission = Permissions.channelPermission(overwrites, permission);
		}

		return permission;
	}
}