feat(guild): enforce lurker read-only across send, react, and voice
- setMemberRole accepts lurker; _assertCanParticipate guards writes - canPostInActiveChannel false for lurkers without custom role perms - Blocks joinVoiceChannel, toggleReaction, and message edits for lurkers Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -97,6 +97,8 @@ const {
|
||||
USER_STATUS,
|
||||
PERMISSION,
|
||||
roleHasPermission,
|
||||
memberCanParticipate,
|
||||
MEMBER_ROLES,
|
||||
memberHasPermission,
|
||||
canModifyMessage,
|
||||
resolveMentionedUserIds,
|
||||
@@ -1639,8 +1641,12 @@ class PearcordPlatform extends EventEmitter {
|
||||
throw new Error('no permission to change roles')
|
||||
}
|
||||
const next = String(role || '').toLowerCase()
|
||||
if (next !== 'moderator' && next !== 'member') {
|
||||
throw new Error('role must be moderator or member')
|
||||
if (
|
||||
next !== MEMBER_ROLES.MODERATOR &&
|
||||
next !== MEMBER_ROLES.MEMBER &&
|
||||
next !== MEMBER_ROLES.LURKER
|
||||
) {
|
||||
throw new Error('role must be moderator, member, or lurker')
|
||||
}
|
||||
if (userId === this.guild.guild.ownerId) throw new Error('cannot change owner role')
|
||||
const member = await this.db.get(COLLECTIONS.MEMBERS, {
|
||||
@@ -2139,6 +2145,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
|
||||
async joinVoiceChannel (channelId) {
|
||||
if (this.mode === 'guild') await this._assertCanParticipate()
|
||||
const guild = this.guild?.guild
|
||||
if (!guild || !this.voice) throw new Error('voice not available')
|
||||
const ch = await this.db.get(COLLECTIONS.CHANNELS, { guildId: guild.id, id: channelId })
|
||||
@@ -2398,6 +2405,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (msg.authorId !== user.id) throw new Error('cannot modify another user\'s message')
|
||||
return msg
|
||||
}
|
||||
await this._assertCanParticipate()
|
||||
const member = await this._resolveMemberRecord()
|
||||
const customRoles = this.guildRoles
|
||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
||||
@@ -3435,6 +3443,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
|
||||
async _sendPlainMessage (content, opts = {}) {
|
||||
if (!this.messages) throw new Error('no channel')
|
||||
if (this.mode === 'guild') await this._assertCanParticipate()
|
||||
if (this.mode === 'guild' && this.guild) {
|
||||
const channels = await this.guild.listChannels()
|
||||
const ch = channels.find((c) => c.id === this.activeChannelId)
|
||||
@@ -3951,11 +3960,24 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async _assertCanParticipate () {
|
||||
if (this.mode !== 'guild' || !this.guild?.guild) return
|
||||
const member = await this._resolveMemberRecord()
|
||||
if (!member) throw new Error('not a guild member')
|
||||
const customRoles = this.guildRoles
|
||||
? await this.guildRoles.listRoles(this.guild.guild.id)
|
||||
: []
|
||||
if (!memberCanParticipate(member, customRoles)) {
|
||||
throw new Error('read-only role cannot send messages or reactions')
|
||||
}
|
||||
}
|
||||
|
||||
async _assertChannelWritable (ch) {
|
||||
if (!ch) return
|
||||
if (isThreadChannel(ch) && ch.archived) {
|
||||
throw new Error('thread is archived')
|
||||
}
|
||||
await this._assertCanParticipate()
|
||||
await this._assertAnnouncementPost(ch)
|
||||
}
|
||||
|
||||
@@ -4217,6 +4239,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
async toggleReaction (messageId, emoji) {
|
||||
const user = this.identity.user
|
||||
if (!user) throw new Error('register first')
|
||||
if (this.mode === 'guild') await this._assertCanParticipate()
|
||||
const result = await this.messages.toggleReaction(messageId, emoji, user.id)
|
||||
if (this.mode !== 'dm' && this.guild) this.guild.gossipReaction(result)
|
||||
this.emit('reaction', result)
|
||||
@@ -4559,8 +4582,15 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (peerId) dmPeerReadAt = dmReadByUser[peerId] || 0
|
||||
}
|
||||
let canPostInActiveChannel = true
|
||||
if (activeChannel && isAnnouncementChannel(activeChannel) && this.mode === 'guild') {
|
||||
canPostInActiveChannel = !!myPermissions?.manageMessages
|
||||
if (this.mode === 'guild') {
|
||||
const selfMember = await this._resolveMemberRecord()
|
||||
const customRolesForSelf = this.guildRoles && guild
|
||||
? await this.guildRoles.listRoles(guild.id)
|
||||
: []
|
||||
canPostInActiveChannel = memberCanParticipate(selfMember, customRolesForSelf)
|
||||
if (activeChannel && isAnnouncementChannel(activeChannel)) {
|
||||
canPostInActiveChannel = canPostInActiveChannel && !!myPermissions?.manageMessages
|
||||
}
|
||||
}
|
||||
let automodConfig = null
|
||||
if (guild && this.automod) {
|
||||
|
||||
Reference in New Issue
Block a user