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,
|
||||
roleHasPermission,
|
||||
memberCanParticipate,
|
||||
memberCanConnectVoice,
|
||||
memberCanSpeakInVoice,
|
||||
memberCanJoinVoiceListenOnly,
|
||||
MEMBER_ROLES,
|
||||
memberHasPermission,
|
||||
@@ -2529,21 +2531,21 @@ class PearcordPlatform extends EventEmitter {
|
||||
let listenOnly = !!opts.listenOnly
|
||||
if (this.mode === 'guild') {
|
||||
const member = await this._resolveMemberRecord()
|
||||
if (!member) throw new Error('not a guild member')
|
||||
const customRoles = this.guildRoles && this.guild?.guild
|
||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
||||
: []
|
||||
const mask = await this._participationMask(channelId)
|
||||
const canParticipate = memberCanParticipate(member, customRoles, mask)
|
||||
if (!listenOnly && !canParticipate) {
|
||||
if (memberCanJoinVoiceListenOnly(member, customRoles)) listenOnly = true
|
||||
else throw new Error('read-only role cannot join voice')
|
||||
if (!memberCanConnectVoice(member, customRoles, mask)) {
|
||||
throw new Error('no permission to connect to voice channel')
|
||||
}
|
||||
if (listenOnly) {
|
||||
if (!memberCanJoinVoiceListenOnly(member, customRoles)) {
|
||||
throw new Error('cannot join voice listen-only')
|
||||
}
|
||||
} else {
|
||||
await this._assertCanParticipate()
|
||||
if (opts.listenOnly) {
|
||||
listenOnly = true
|
||||
} else if (!memberCanSpeakInVoice(member, customRoles, mask)) {
|
||||
listenOnly = true
|
||||
}
|
||||
if (listenOnly && !memberCanJoinVoiceListenOnly(member, customRoles, mask)) {
|
||||
throw new Error('cannot join voice listen-only')
|
||||
}
|
||||
}
|
||||
const guild = this.guild?.guild
|
||||
@@ -2609,8 +2611,15 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (!guild || !this.voice || !user) throw new Error('voice not available')
|
||||
const ch = await this.db.get(COLLECTIONS.CHANNELS, { guildId: guild.id, id: channelId })
|
||||
if (!ch || !isStageChannel(ch)) throw new Error('not a stage channel')
|
||||
const roles = await this._memberRoles()
|
||||
const isMod = roleHasPermission(roles, PERMISSION.MANAGE_CHANNELS)
|
||||
const member = await this._resolveMemberRecord()
|
||||
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)
|
||||
this.guild.gossipVoiceState(row)
|
||||
const stageRow = await this.stage.setRole({
|
||||
@@ -5077,7 +5086,21 @@ class PearcordPlatform extends EventEmitter {
|
||||
customRolesForSelf,
|
||||
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)) {
|
||||
canPostInActiveChannel = canPostInActiveChannel && !!myPermissions?.manageMessages
|
||||
}
|
||||
@@ -5162,6 +5185,29 @@ class PearcordPlatform extends EventEmitter {
|
||||
members: members || [],
|
||||
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 {
|
||||
onboarded: this.onboarded,
|
||||
user,
|
||||
@@ -5197,6 +5243,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
myPermissions,
|
||||
canPostInActiveChannel,
|
||||
canJoinVoiceListenOnly,
|
||||
voicePermsByChannel,
|
||||
pins,
|
||||
mentionNames: [...mentionNames],
|
||||
channelSettings,
|
||||
|
||||
Reference in New Issue
Block a user