feat(platform): GUILD_SYNC channel meta + SEND_MESSAGES enforcement

Bundle channelSettings (description + slowmode) and all topic-bearing channel
types in GUILD_SYNC; ingest into ChannelDescriptionStore on peer join. Push
mesh sync after topic/description edits. _participationMask applies channel
overwrites for send, voice join, and view.canPostInActiveChannel.
This commit is contained in:
Raven Scott
2026-05-22 03:02:43 -04:00
parent 30f27c515a
commit 5c4b6c1ed4
2 changed files with 102 additions and 48 deletions
+60 -6
View File
@@ -134,7 +134,8 @@ const { PearcordGuildRoles, PERMISSION_META } = require('pearcord-guild-roles')
const {
PearcordChannelPermissions,
memberHasChannelPermission,
effectivePermissionView
effectivePermissionView,
effectivePermissionMask
} = require('pearcord-channel-permissions')
const { viewToMask } = require('pearcord-guild-roles/permissions')
const { maskToPermissionView, memberPermissionMask } = require('pearcord-guild-roles/permissions')
@@ -1051,8 +1052,19 @@ class PearcordPlatform extends EventEmitter {
if (!guild?.id) return null
const channels = await this.guild.listChannels()
const syncChannels = channels.filter((c) =>
c.type === 'text' || c.type === 'announcement' || c.type === 'forum'
['text', 'voice', 'announcement', 'stage', 'forum'].includes(c.type)
)
const channelSettings = []
await this._initChannelPermissions(guild.id)
for (const ch of channels) {
const settings = await this.getChannelSettings(ch.id, guild.id)
channelSettings.push({
guildId: guild.id,
channelId: ch.id,
slowmodeSeconds: settings.slowmodeSeconds || 0,
description: settings.description ?? null
})
}
const messages = []
for (const ch of syncChannels) {
const rows = await this.db.find(COLLECTIONS.MESSAGES, { channelId: ch.id })
@@ -1106,7 +1118,8 @@ class PearcordPlatform extends EventEmitter {
members,
guildRoles: roleBundle.guildRoles,
memberRoleLinks: roleBundle.memberRoleLinks,
channelOverwrites: permBundle.channelOverwrites
channelOverwrites: permBundle.channelOverwrites,
channelSettings
}
}
@@ -1159,6 +1172,25 @@ class PearcordPlatform extends EventEmitter {
}
for (const ch of payload.channels || []) {
await this.guild.ingestChannel(ch).catch(() => {})
if (ch?.topic != null) this.emit('channel-update', ch)
}
if (this.channelDescriptions && payload.channelSettings?.length) {
for (const row of payload.channelSettings) {
if (row?.guildId !== payload.guildId || !row?.channelId) continue
await this.db.insert(COLLECTIONS.CHANNEL_SETTINGS, {
guildId: row.guildId,
channelId: row.channelId,
slowmodeSeconds: row.slowmodeSeconds || 0
})
if (row.description !== undefined) {
await this.channelDescriptions.set(
row.guildId,
row.channelId,
row.description
)
}
}
this.emit('channel-settings-sync', { guildId: payload.guildId })
}
for (const att of payload.attachments || []) {
await this.attachments?.ingestGossip(att).catch(() => {})
@@ -1919,6 +1951,7 @@ class PearcordPlatform extends EventEmitter {
targetId: channelId
})
this.emit('channel-update', updated)
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return updated
}
@@ -1937,6 +1970,7 @@ class PearcordPlatform extends EventEmitter {
description: normalizeDescription(description)
})
await this._audit('channel.description', { guildId, targetId: channelId })
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return settings
}
@@ -2119,6 +2153,19 @@ class PearcordPlatform extends EventEmitter {
return this.channelPermissions.listForChannel(this.guild.guild.id, channelId)
}
async _participationMask (channelId) {
const member = await this._resolveMemberRecord()
if (!member) return 0
const customRoles = this.guildRoles
? await this.guildRoles.listRoles(this.guild.guild.id)
: []
const base = memberPermissionMask(member, customRoles)
const chId = channelId || this.activeChannelId
if (!chId || this.mode !== 'guild') return base
const overwrites = await this._channelOverwrites(chId)
return effectivePermissionMask(member, customRoles, overwrites)
}
async _initBotEvents (guildId) {
this.botEvents = new BotEventFanout({
guildId,
@@ -2485,7 +2532,8 @@ class PearcordPlatform extends EventEmitter {
const customRoles = this.guildRoles && this.guild?.guild
? await this.guildRoles.listRoles(this.guild.guild.id)
: []
const canParticipate = memberCanParticipate(member, customRoles)
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')
@@ -4342,7 +4390,8 @@ class PearcordPlatform extends EventEmitter {
const customRoles = this.guildRoles
? await this.guildRoles.listRoles(this.guild.guild.id)
: []
if (!memberCanParticipate(member, customRoles)) {
const mask = await this._participationMask(this.activeChannelId)
if (!memberCanParticipate(member, customRoles, mask)) {
throw new Error('read-only role cannot send messages or reactions')
}
}
@@ -5022,7 +5071,12 @@ class PearcordPlatform extends EventEmitter {
const customRolesForSelf = this.guildRoles && guild
? await this.guildRoles.listRoles(guild.id)
: []
canPostInActiveChannel = memberCanParticipate(selfMember, customRolesForSelf)
const partMask = await this._participationMask(this.activeChannelId)
canPostInActiveChannel = memberCanParticipate(
selfMember,
customRolesForSelf,
partMask
)
canJoinVoiceListenOnly = memberCanJoinVoiceListenOnly(selfMember, customRolesForSelf)
if (activeChannel && isAnnouncementChannel(activeChannel)) {
canPostInActiveChannel = canPostInActiveChannel && !!myPermissions?.manageMessages
+42 -42
View File
@@ -7,56 +7,56 @@
"bare-events": "^2.8.0",
"bare-path": "^3.0.0",
"bare-process": "^4.4.0",
"pearcord-db": "file:../pearcord-db",
"pearcord-identity": "file:../pearcord-identity",
"pearcord-guild": "file:../pearcord-guild",
"pearcord-message": "file:../pearcord-message",
"pearcord-message-layout": "file:../pearcord-message-layout",
"pearcord-profile-assets": "file:../pearcord-profile-assets",
"pearcord-lottie": "file:../pearcord-lottie",
"pearcord-presence": "file:../pearcord-presence",
"hyperswarm": "^4.11.6",
"pearcord-activity": "file:../pearcord-activity",
"pearcord-channel-meta": "file:../pearcord-channel-meta",
"pearcord-invite": "file:../pearcord-invite",
"pearcord-dm": "file:../pearcord-dm",
"pearcord-announcement-hub": "file:../pearcord-announcement-hub",
"pearcord-announcements": "file:../pearcord-announcements",
"pearcord-attachments": "file:../pearcord-attachments",
"pearcord-drive": "file:../pearcord-drive",
"pearcord-moderation": "file:../pearcord-moderation",
"pearcord-voice": "file:../pearcord-voice",
"pearcord-voice-media": "file:../pearcord-voice-media",
"pearcord-discovery": "file:../pearcord-discovery",
"pearcord-embed": "file:../pearcord-embed",
"pearcord-crypto": "file:../pearcord-crypto",
"pearcord-threads": "file:../pearcord-threads",
"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",
"pearcord-automod": "file:../pearcord-automod",
"pearcord-boosts": "file:../pearcord-boosts",
"pearcord-bot-auth": "file:../pearcord-bot-auth",
"pearcord-bot-events": "file:../pearcord-bot-events",
"pearcord-bot-limits": "file:../pearcord-bot-limits",
"pearcord-bot-sdk": "file:../pearcord-bot-sdk",
"pearcord-channel-meta": "file:../pearcord-channel-meta",
"pearcord-channel-permissions": "file:../pearcord-channel-permissions",
"pearcord-commands": "file:../pearcord-commands",
"pearcord-contacts": "file:../pearcord-contacts",
"pearcord-notifications": "file:../pearcord-notifications",
"pearcord-settings": "file:../pearcord-settings",
"pearcord-server-folders": "file:../pearcord-server-folders",
"pearcord-profile-cosmetics": "file:../pearcord-profile-cosmetics",
"pearcord-crypto": "file:../pearcord-crypto",
"pearcord-db": "file:../pearcord-db",
"pearcord-device-sync": "file:../pearcord-device-sync",
"pearcord-step-up": "file:../pearcord-step-up",
"pearcord-discovery": "file:../pearcord-discovery",
"pearcord-display-capture": "file:../pearcord-display-capture",
"pearcord-dm": "file:../pearcord-dm",
"pearcord-drive": "file:../pearcord-drive",
"pearcord-embed": "file:../pearcord-embed",
"pearcord-emoji": "file:../pearcord-emoji",
"pearcord-forum": "file:../pearcord-forum",
"pearcord-forum-search": "file:../pearcord-forum-search",
"pearcord-stage": "file:../pearcord-stage",
"pearcord-automod": "file:../pearcord-automod",
"pearcord-screen-share": "file:../pearcord-screen-share",
"pearcord-display-capture": "file:../pearcord-display-capture",
"pearcord-stickers": "file:../pearcord-stickers",
"pearcord-soundboard": "file:../pearcord-soundboard",
"pearcord-announcement-hub": "file:../pearcord-announcement-hub",
"pearcord-guild": "file:../pearcord-guild",
"pearcord-guild-roles": "file:../pearcord-guild-roles",
"pearcord-hub-cards": "file:../pearcord-hub-cards",
"pearcord-announcements": "file:../pearcord-announcements",
"pearcord-boosts": "file:../pearcord-boosts",
"pearcord-identity": "file:../pearcord-identity",
"pearcord-invite": "file:../pearcord-invite",
"pearcord-lottie": "file:../pearcord-lottie",
"pearcord-message": "file:../pearcord-message",
"pearcord-message-layout": "file:../pearcord-message-layout",
"pearcord-moderation": "file:../pearcord-moderation",
"pearcord-notifications": "file:../pearcord-notifications",
"pearcord-presence": "file:../pearcord-presence",
"pearcord-profile-assets": "file:../pearcord-profile-assets",
"pearcord-profile-cosmetics": "file:../pearcord-profile-cosmetics",
"pearcord-screen-share": "file:../pearcord-screen-share",
"pearcord-server-folders": "file:../pearcord-server-folders",
"pearcord-settings": "file:../pearcord-settings",
"pearcord-shared": "file:../pearcord-shared",
"pearcord-soundboard": "file:../pearcord-soundboard",
"pearcord-stage": "file:../pearcord-stage",
"pearcord-step-up": "file:../pearcord-step-up",
"pearcord-stickers": "file:../pearcord-stickers",
"pearcord-threads": "file:../pearcord-threads",
"pearcord-voice": "file:../pearcord-voice",
"pearcord-voice-capture": "file:../pearcord-voice-capture",
"hyperswarm": "^4.11.6",
"pearcord-shared": "file:../pearcord-shared"
"pearcord-voice-media": "file:../pearcord-voice-media"
}
}