diff --git a/index.js b/index.js index 1fa1040..560d9d1 100644 --- a/index.js +++ b/index.js @@ -131,6 +131,12 @@ const { now } = require('pearcord-shared') const { PearcordGuildRoles, PERMISSION_META } = require('pearcord-guild-roles') +const { + PearcordChannelPermissions, + memberHasChannelPermission, + effectivePermissionView +} = require('pearcord-channel-permissions') +const { viewToMask } = require('pearcord-guild-roles/permissions') const { maskToPermissionView, memberPermissionMask } = require('pearcord-guild-roles/permissions') function messageLayoutKind (m) { @@ -197,6 +203,7 @@ class PearcordPlatform extends EventEmitter { this.stickerRegistry = null this.soundboardRegistry = null this.guildRoles = null + this.channelPermissions = null this._recentSoundPlays = [] this.botEvents = null this.discovery = null @@ -1074,6 +1081,11 @@ class PearcordPlatform extends EventEmitter { await this._initGuildRoles(guild.id) if (this.guildRoles) roleBundle = await this.guildRoles.exportSyncBundle(guild.id) } + let permBundle = { channelOverwrites: [] } + await this._initChannelPermissions(guild.id) + if (this.channelPermissions) { + permBundle = await this.channelPermissions.exportSyncBundle(guild.id) + } if (guild.iconHash && this.attachments) { const iconRow = await this.attachments.get(guild.iconHash).catch(() => null) if (iconRow && !attachments.some((a) => a.id === iconRow.id)) { @@ -1093,7 +1105,8 @@ class PearcordPlatform extends EventEmitter { attachments, members, guildRoles: roleBundle.guildRoles, - memberRoleLinks: roleBundle.memberRoleLinks + memberRoleLinks: roleBundle.memberRoleLinks, + channelOverwrites: permBundle.channelOverwrites } } @@ -1128,6 +1141,10 @@ class PearcordPlatform extends EventEmitter { guildRoles: payload.guildRoles, memberRoleLinks: payload.memberRoleLinks }) + await this._initChannelPermissions(payload.guildId) + await this.channelPermissions.ingestSyncBundle(payload.guildId, { + channelOverwrites: payload.channelOverwrites + }) } for (const mem of payload.members || []) { if (!mem?.guildId || !mem?.userId) continue @@ -1303,6 +1320,14 @@ class PearcordPlatform extends EventEmitter { this.guildRoles?.ingestMemberRolesGossip(payload).catch(() => {}) this.emit('guild-member-roles', payload) }) + guildInstance.on('channel-overwrite', (row) => { + this.channelPermissions?.ingestOverwriteGossip(row).catch(() => {}) + this.emit('channel-overwrite', row) + }) + guildInstance.on('channel-overwrite-delete', (payload) => { + this.channelPermissions?.ingestOverwriteDelete(payload).catch(() => {}) + this.emit('channel-overwrite-delete', payload) + }) guildInstance.on('bot-event', (payload) => { this.botEvents?.ingest(payload) this.emit('bot-event', payload) @@ -1915,6 +1940,76 @@ class PearcordPlatform extends EventEmitter { return settings } + async listChannelPermissionOverwrites (channelId) { + if (!this.guild?.guild) throw new Error('no guild') + const chId = channelId || this.activeChannelId + if (!chId) throw new Error('channelId required') + await this._initChannelPermissions(this.guild.guild.id) + return this.channelPermissions.listForChannel(this.guild.guild.id, chId) + } + + async upsertChannelPermissionOverwrite (channelId, payload = {}) { + if (!this.guild?.guild) throw new Error('no guild') + if (!(await this._hasPerm(PERMISSION.MANAGE_CHANNELS))) { + throw new Error('no permission to edit channel permissions') + } + const chId = channelId || this.activeChannelId + if (!chId) throw new Error('channelId required') + const ch = await this.db.get(COLLECTIONS.CHANNELS, { + guildId: this.guild.guild.id, + id: chId + }) + if (!ch) throw new Error('channel not found') + await this._initChannelPermissions(this.guild.guild.id) + const allow = + payload.allow !== undefined + ? Number(payload.allow) || 0 + : viewToMask(payload.allowView || {}) + const deny = + payload.deny !== undefined + ? Number(payload.deny) || 0 + : viewToMask(payload.denyView || {}) + const row = await this.channelPermissions.upsertOverwrite( + this.guild.guild.id, + chId, + { + targetType: payload.targetType, + targetId: payload.targetId, + allow, + deny + } + ) + this.guild.gossipChannelOverwriteUpsert(row) + await this._audit('channel.overwrite', { guildId: this.guild.guild.id, targetId: chId }) + this.emit('channel-overwrite', row) + return row + } + + async deleteChannelPermissionOverwrite (channelId, targetType, targetId) { + if (!this.guild?.guild) throw new Error('no guild') + if (!(await this._hasPerm(PERMISSION.MANAGE_CHANNELS))) { + throw new Error('no permission to edit channel permissions') + } + const chId = channelId || this.activeChannelId + await this._initChannelPermissions(this.guild.guild.id) + const prev = await this.channelPermissions.deleteOverwrite( + this.guild.guild.id, + chId, + targetType, + targetId + ) + if (prev && this.guild) { + this.guild.gossipChannelOverwriteDelete({ + guildId: this.guild.guild.id, + channelId: chId, + targetType: prev.targetType, + targetId: prev.targetId + }) + } + this.emit('channel-overwrite-delete', { channelId: chId, targetType, targetId }) + return prev + } + async _assertSlowmodeFor (channelId) { const chId = channelId || this.activeChannelId const user = this.identity.user @@ -2002,6 +2097,28 @@ class PearcordPlatform extends EventEmitter { return this.guildRoles } + async _initChannelPermissions (guildId) { + const gid = guildId || this.guild?.guild?.id + if (!gid) return null + if (!this.channelPermissions || this.channelPermissions.guildId !== gid) { + this.channelPermissions = new PearcordChannelPermissions({ + storagePath: this.storagePath, + guildId: gid + }) + await this.channelPermissions.ready() + } else { + this.channelPermissions.setGuild(gid) + } + return this.channelPermissions + } + + async _channelOverwrites (channelId) { + if (!this.guild?.guild || !channelId) return [] + await this._initChannelPermissions(this.guild.guild.id) + if (!this.channelPermissions) return [] + return this.channelPermissions.listForChannel(this.guild.guild.id, channelId) + } + async _initBotEvents (guildId) { this.botEvents = new BotEventFanout({ guildId, @@ -2614,16 +2731,21 @@ class PearcordPlatform extends EventEmitter { return { ...member, customRoleIds } } - async _hasPerm (perm) { + async _hasPerm (perm, channelId) { const member = await this._resolveMemberRecord() if (!member) return false const customRoles = this.guildRoles ? await this.guildRoles.listRoles(this.guild.guild.id) : [] + const chId = channelId || this.activeChannelId + if (chId && this.mode === 'guild') { + const overwrites = await this._channelOverwrites(chId) + return memberHasChannelPermission(member, customRoles, overwrites, perm) + } return memberHasPermission(member, customRoles, perm) } - async _myPermissions () { + async _myPermissions (channelId) { const member = await this._resolveMemberRecord() if (!member) { return { @@ -2637,6 +2759,11 @@ class PearcordPlatform extends EventEmitter { const customRoles = this.guildRoles ? await this.guildRoles.listRoles(this.guild.guild.id) : [] + const chId = channelId || this.activeChannelId + if (chId && this.mode === 'guild') { + const overwrites = await this._channelOverwrites(chId) + return effectivePermissionView(member, customRoles, overwrites) + } return maskToPermissionView(memberPermissionMask(member, customRoles)) } @@ -4610,7 +4737,12 @@ class PearcordPlatform extends EventEmitter { const stats = this.mode === 'dm' ? this.dm?.getStats() || null : this.guild?.getStats() || null - const myPermissions = this.mode === 'guild' ? await this._myPermissions() : null + const myPermissions = + this.mode === 'guild' ? await this._myPermissions(this.activeChannelId) : null + let activeChannelOverwrites = [] + if (this.mode === 'guild' && guild && this.activeChannelId) { + activeChannelOverwrites = await this._channelOverwrites(this.activeChannelId) + } let pins = [] const mentionNames = new Set() if (user) { @@ -4996,6 +5128,7 @@ class PearcordPlatform extends EventEmitter { dmReadByUser, activeChannelId: this.activeChannelId, activeChannelSummary, + activeChannelOverwrites, messages, messageById, reactions, diff --git a/package.json b/package.json index ffbf018..ae3c828 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "pearcord-commands": "file:../pearcord-commands", "pearcord-emoji": "file:../pearcord-emoji", "pearcord-guild-roles": "file:../pearcord-guild-roles", + "pearcord-channel-permissions": "file:../pearcord-channel-permissions", "pearcord-bot-events": "file:../pearcord-bot-events", "pearcord-bot-sdk": "file:../pearcord-bot-sdk", "pearcord-bot-limits": "file:../pearcord-bot-limits",