feat(platform): per-channel permission overwrites (Phase 82)
Integrate pearcord-channel-permissions: list/upsert/delete overwrites, channel-effective _hasPerm/_myPermissions, GUILD_SYNC bundle, and mesh ingest handlers. Requires MANAGE_CHANNELS on the active channel to edit.
This commit is contained in:
@@ -131,6 +131,12 @@ const {
|
|||||||
now
|
now
|
||||||
} = require('pearcord-shared')
|
} = require('pearcord-shared')
|
||||||
const { PearcordGuildRoles, PERMISSION_META } = require('pearcord-guild-roles')
|
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')
|
const { maskToPermissionView, memberPermissionMask } = require('pearcord-guild-roles/permissions')
|
||||||
|
|
||||||
function messageLayoutKind (m) {
|
function messageLayoutKind (m) {
|
||||||
@@ -197,6 +203,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
this.stickerRegistry = null
|
this.stickerRegistry = null
|
||||||
this.soundboardRegistry = null
|
this.soundboardRegistry = null
|
||||||
this.guildRoles = null
|
this.guildRoles = null
|
||||||
|
this.channelPermissions = null
|
||||||
this._recentSoundPlays = []
|
this._recentSoundPlays = []
|
||||||
this.botEvents = null
|
this.botEvents = null
|
||||||
this.discovery = null
|
this.discovery = null
|
||||||
@@ -1074,6 +1081,11 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
await this._initGuildRoles(guild.id)
|
await this._initGuildRoles(guild.id)
|
||||||
if (this.guildRoles) roleBundle = await this.guildRoles.exportSyncBundle(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) {
|
if (guild.iconHash && this.attachments) {
|
||||||
const iconRow = await this.attachments.get(guild.iconHash).catch(() => null)
|
const iconRow = await this.attachments.get(guild.iconHash).catch(() => null)
|
||||||
if (iconRow && !attachments.some((a) => a.id === iconRow.id)) {
|
if (iconRow && !attachments.some((a) => a.id === iconRow.id)) {
|
||||||
@@ -1093,7 +1105,8 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
attachments,
|
attachments,
|
||||||
members,
|
members,
|
||||||
guildRoles: roleBundle.guildRoles,
|
guildRoles: roleBundle.guildRoles,
|
||||||
memberRoleLinks: roleBundle.memberRoleLinks
|
memberRoleLinks: roleBundle.memberRoleLinks,
|
||||||
|
channelOverwrites: permBundle.channelOverwrites
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1128,6 +1141,10 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
guildRoles: payload.guildRoles,
|
guildRoles: payload.guildRoles,
|
||||||
memberRoleLinks: payload.memberRoleLinks
|
memberRoleLinks: payload.memberRoleLinks
|
||||||
})
|
})
|
||||||
|
await this._initChannelPermissions(payload.guildId)
|
||||||
|
await this.channelPermissions.ingestSyncBundle(payload.guildId, {
|
||||||
|
channelOverwrites: payload.channelOverwrites
|
||||||
|
})
|
||||||
}
|
}
|
||||||
for (const mem of payload.members || []) {
|
for (const mem of payload.members || []) {
|
||||||
if (!mem?.guildId || !mem?.userId) continue
|
if (!mem?.guildId || !mem?.userId) continue
|
||||||
@@ -1303,6 +1320,14 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
this.guildRoles?.ingestMemberRolesGossip(payload).catch(() => {})
|
this.guildRoles?.ingestMemberRolesGossip(payload).catch(() => {})
|
||||||
this.emit('guild-member-roles', payload)
|
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) => {
|
guildInstance.on('bot-event', (payload) => {
|
||||||
this.botEvents?.ingest(payload)
|
this.botEvents?.ingest(payload)
|
||||||
this.emit('bot-event', payload)
|
this.emit('bot-event', payload)
|
||||||
@@ -1915,6 +1940,76 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
return settings
|
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) {
|
async _assertSlowmodeFor (channelId) {
|
||||||
const chId = channelId || this.activeChannelId
|
const chId = channelId || this.activeChannelId
|
||||||
const user = this.identity.user
|
const user = this.identity.user
|
||||||
@@ -2002,6 +2097,28 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
return this.guildRoles
|
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) {
|
async _initBotEvents (guildId) {
|
||||||
this.botEvents = new BotEventFanout({
|
this.botEvents = new BotEventFanout({
|
||||||
guildId,
|
guildId,
|
||||||
@@ -2614,16 +2731,21 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
return { ...member, customRoleIds }
|
return { ...member, customRoleIds }
|
||||||
}
|
}
|
||||||
|
|
||||||
async _hasPerm (perm) {
|
async _hasPerm (perm, channelId) {
|
||||||
const member = await this._resolveMemberRecord()
|
const member = await this._resolveMemberRecord()
|
||||||
if (!member) return false
|
if (!member) return false
|
||||||
const customRoles = this.guildRoles
|
const customRoles = this.guildRoles
|
||||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
? 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)
|
return memberHasPermission(member, customRoles, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
async _myPermissions () {
|
async _myPermissions (channelId) {
|
||||||
const member = await this._resolveMemberRecord()
|
const member = await this._resolveMemberRecord()
|
||||||
if (!member) {
|
if (!member) {
|
||||||
return {
|
return {
|
||||||
@@ -2637,6 +2759,11 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
const customRoles = this.guildRoles
|
const customRoles = this.guildRoles
|
||||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
? 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))
|
return maskToPermissionView(memberPermissionMask(member, customRoles))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4610,7 +4737,12 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
const stats = this.mode === 'dm'
|
const stats = this.mode === 'dm'
|
||||||
? this.dm?.getStats() || null
|
? this.dm?.getStats() || null
|
||||||
: this.guild?.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 = []
|
let pins = []
|
||||||
const mentionNames = new Set()
|
const mentionNames = new Set()
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -4996,6 +5128,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
dmReadByUser,
|
dmReadByUser,
|
||||||
activeChannelId: this.activeChannelId,
|
activeChannelId: this.activeChannelId,
|
||||||
activeChannelSummary,
|
activeChannelSummary,
|
||||||
|
activeChannelOverwrites,
|
||||||
messages,
|
messages,
|
||||||
messageById,
|
messageById,
|
||||||
reactions,
|
reactions,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"pearcord-commands": "file:../pearcord-commands",
|
"pearcord-commands": "file:../pearcord-commands",
|
||||||
"pearcord-emoji": "file:../pearcord-emoji",
|
"pearcord-emoji": "file:../pearcord-emoji",
|
||||||
"pearcord-guild-roles": "file:../pearcord-guild-roles",
|
"pearcord-guild-roles": "file:../pearcord-guild-roles",
|
||||||
|
"pearcord-channel-permissions": "file:../pearcord-channel-permissions",
|
||||||
"pearcord-bot-events": "file:../pearcord-bot-events",
|
"pearcord-bot-events": "file:../pearcord-bot-events",
|
||||||
"pearcord-bot-sdk": "file:../pearcord-bot-sdk",
|
"pearcord-bot-sdk": "file:../pearcord-bot-sdk",
|
||||||
"pearcord-bot-limits": "file:../pearcord-bot-limits",
|
"pearcord-bot-limits": "file:../pearcord-bot-limits",
|
||||||
|
|||||||
Reference in New Issue
Block a user