feat(platform): enforce voice/stage permissions per channel (Phase 85)
joinVoiceChannel requires CONNECT_VOICE; missing SPEAK_IN_VOICE forces listen-only. joinStageChannel uses channel-effective MANAGE_CHANNELS for mods. view().voicePermsByChannel maps connect/speak/listenOnly per channel.
This commit is contained in:
@@ -117,6 +117,8 @@ const {
|
|||||||
PERMISSION,
|
PERMISSION,
|
||||||
roleHasPermission,
|
roleHasPermission,
|
||||||
memberCanParticipate,
|
memberCanParticipate,
|
||||||
|
memberCanConnectVoice,
|
||||||
|
memberCanSpeakInVoice,
|
||||||
memberCanJoinVoiceListenOnly,
|
memberCanJoinVoiceListenOnly,
|
||||||
MEMBER_ROLES,
|
MEMBER_ROLES,
|
||||||
memberHasPermission,
|
memberHasPermission,
|
||||||
@@ -2529,22 +2531,22 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
let listenOnly = !!opts.listenOnly
|
let listenOnly = !!opts.listenOnly
|
||||||
if (this.mode === 'guild') {
|
if (this.mode === 'guild') {
|
||||||
const member = await this._resolveMemberRecord()
|
const member = await this._resolveMemberRecord()
|
||||||
|
if (!member) throw new Error('not a guild member')
|
||||||
const customRoles = this.guildRoles && this.guild?.guild
|
const customRoles = this.guildRoles && this.guild?.guild
|
||||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
? await this.guildRoles.listRoles(this.guild.guild.id)
|
||||||
: []
|
: []
|
||||||
const mask = await this._participationMask(channelId)
|
const mask = await this._participationMask(channelId)
|
||||||
const canParticipate = memberCanParticipate(member, customRoles, mask)
|
if (!memberCanConnectVoice(member, customRoles, mask)) {
|
||||||
if (!listenOnly && !canParticipate) {
|
throw new Error('no permission to connect to voice channel')
|
||||||
if (memberCanJoinVoiceListenOnly(member, customRoles)) listenOnly = true
|
|
||||||
else throw new Error('read-only role cannot join voice')
|
|
||||||
}
|
}
|
||||||
if (listenOnly) {
|
if (opts.listenOnly) {
|
||||||
if (!memberCanJoinVoiceListenOnly(member, customRoles)) {
|
listenOnly = true
|
||||||
|
} else if (!memberCanSpeakInVoice(member, customRoles, mask)) {
|
||||||
|
listenOnly = true
|
||||||
|
}
|
||||||
|
if (listenOnly && !memberCanJoinVoiceListenOnly(member, customRoles, mask)) {
|
||||||
throw new Error('cannot join voice listen-only')
|
throw new Error('cannot join voice listen-only')
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
await this._assertCanParticipate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const guild = this.guild?.guild
|
const guild = this.guild?.guild
|
||||||
if (!guild || !this.voice) throw new Error('voice not available')
|
if (!guild || !this.voice) throw new Error('voice not available')
|
||||||
@@ -2609,8 +2611,15 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
if (!guild || !this.voice || !user) throw new Error('voice not available')
|
if (!guild || !this.voice || !user) throw new Error('voice not available')
|
||||||
const ch = await this.db.get(COLLECTIONS.CHANNELS, { guildId: guild.id, id: channelId })
|
const ch = await this.db.get(COLLECTIONS.CHANNELS, { guildId: guild.id, id: channelId })
|
||||||
if (!ch || !isStageChannel(ch)) throw new Error('not a stage channel')
|
if (!ch || !isStageChannel(ch)) throw new Error('not a stage channel')
|
||||||
const roles = await this._memberRoles()
|
const member = await this._resolveMemberRecord()
|
||||||
const isMod = roleHasPermission(roles, PERMISSION.MANAGE_CHANNELS)
|
const customRoles = this.guildRoles
|
||||||
|
? await this.guildRoles.listRoles(guild.id)
|
||||||
|
: []
|
||||||
|
const mask = await this._participationMask(channelId)
|
||||||
|
if (!memberCanConnectVoice(member, customRoles, mask)) {
|
||||||
|
throw new Error('no permission to connect to stage channel')
|
||||||
|
}
|
||||||
|
const isMod = await this._hasPerm(PERMISSION.MANAGE_CHANNELS, channelId)
|
||||||
const row = await this.voice.join(channelId)
|
const row = await this.voice.join(channelId)
|
||||||
this.guild.gossipVoiceState(row)
|
this.guild.gossipVoiceState(row)
|
||||||
const stageRow = await this.stage.setRole({
|
const stageRow = await this.stage.setRole({
|
||||||
@@ -5077,7 +5086,21 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
customRolesForSelf,
|
customRolesForSelf,
|
||||||
partMask
|
partMask
|
||||||
)
|
)
|
||||||
canJoinVoiceListenOnly = memberCanJoinVoiceListenOnly(selfMember, customRolesForSelf)
|
if (
|
||||||
|
activeChannel &&
|
||||||
|
(activeChannel.type === 'voice' || activeChannel.type === 'stage')
|
||||||
|
) {
|
||||||
|
canJoinVoiceListenOnly = memberCanJoinVoiceListenOnly(
|
||||||
|
selfMember,
|
||||||
|
customRolesForSelf,
|
||||||
|
partMask
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
canJoinVoiceListenOnly = memberCanJoinVoiceListenOnly(
|
||||||
|
selfMember,
|
||||||
|
customRolesForSelf
|
||||||
|
)
|
||||||
|
}
|
||||||
if (activeChannel && isAnnouncementChannel(activeChannel)) {
|
if (activeChannel && isAnnouncementChannel(activeChannel)) {
|
||||||
canPostInActiveChannel = canPostInActiveChannel && !!myPermissions?.manageMessages
|
canPostInActiveChannel = canPostInActiveChannel && !!myPermissions?.manageMessages
|
||||||
}
|
}
|
||||||
@@ -5162,6 +5185,29 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
members: members || [],
|
members: members || [],
|
||||||
contacts: contactsForView
|
contacts: contactsForView
|
||||||
})
|
})
|
||||||
|
const voicePermsByChannel = {}
|
||||||
|
if (this.mode === 'guild' && guild) {
|
||||||
|
const selfMember = await this._resolveMemberRecord()
|
||||||
|
const customRolesForSelf = this.guildRoles
|
||||||
|
? await this.guildRoles.listRoles(guild.id)
|
||||||
|
: []
|
||||||
|
if (selfMember) {
|
||||||
|
for (const ch of channels) {
|
||||||
|
if (ch.type !== 'voice' && ch.type !== 'stage') continue
|
||||||
|
const ovs = await this._channelOverwrites(ch.id)
|
||||||
|
const mask = effectivePermissionMask(selfMember, customRolesForSelf, ovs)
|
||||||
|
voicePermsByChannel[ch.id] = {
|
||||||
|
connect: memberCanConnectVoice(selfMember, customRolesForSelf, mask),
|
||||||
|
speak: memberCanSpeakInVoice(selfMember, customRolesForSelf, mask),
|
||||||
|
listenOnly: memberCanJoinVoiceListenOnly(
|
||||||
|
selfMember,
|
||||||
|
customRolesForSelf,
|
||||||
|
mask
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
onboarded: this.onboarded,
|
onboarded: this.onboarded,
|
||||||
user,
|
user,
|
||||||
@@ -5197,6 +5243,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
myPermissions,
|
myPermissions,
|
||||||
canPostInActiveChannel,
|
canPostInActiveChannel,
|
||||||
canJoinVoiceListenOnly,
|
canJoinVoiceListenOnly,
|
||||||
|
voicePermsByChannel,
|
||||||
pins,
|
pins,
|
||||||
mentionNames: [...mentionNames],
|
mentionNames: [...mentionNames],
|
||||||
channelSettings,
|
channelSettings,
|
||||||
|
|||||||
Reference in New Issue
Block a user