From 9e5390de96a93f1cd5232516cad8815c566dab39 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Fri, 22 May 2026 07:11:30 -0400 Subject: [PATCH] =?UTF-8?q?feat(platform):=20Phase=20115=20bridges=20?= =?UTF-8?q?=E2=80=94=20CSV=20mesh,=20archive=20health,=20webhook=20URL=20(?= =?UTF-8?q?v0.8.79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - exportHookFailureDigest / getHookFailureDigestCsvExport with signatureValid - GUILD_SYNC slices: hookFailureDigestCsvExport, archiveHealthPrefs, modDigestGroupDmMeta - refreshArchivePeerHealth + archive health prefs on guild focus/join/open - dryRunDigestWebhookRelay + digest relay webhook URL plugin sync - syncModDigestGroupDmFromMesh for mod digest group DM participant sync Co-authored-by: Cursor --- index.js | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 00b0e53..5594193 100644 --- a/index.js +++ b/index.js @@ -292,6 +292,7 @@ class PearcordPlatform extends EventEmitter { this._devicePairSwarm = null this._discoveryWired = false this._archivePeerHealthRefreshedAt = null + this._archiveHealthRefreshTimer = null this.mode = 'home' this.activeChannelId = null this.onboarded = false @@ -1199,6 +1200,8 @@ class PearcordPlatform extends EventEmitter { let auditExportArchives = [] let archivePeerSeen = [] let hookFailureDigest = null + let hookFailureDigestCsvExport = null + let archiveHealthPrefs = null await this._initDeliveryReceipts(guild.id) if (this.deliveryReceipts) { auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice() @@ -1209,6 +1212,9 @@ class PearcordPlatform extends EventEmitter { await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2) auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3) archivePeerSeen = await this.deliveryReceipts.exportArchivePeerSeenSlice(16) + archiveHealthPrefs = await this.deliveryReceipts.exportArchiveHealthPrefsSlice() + hookFailureDigestCsvExport = + await this.deliveryReceipts.exportHookFailureDigestCsvSlice() digestRelayHandoff = await this.deliveryReceipts.exportDigestRelayHandoffSlice() const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot() if (digestSnap) { @@ -1293,6 +1299,8 @@ class PearcordPlatform extends EventEmitter { auditExportArchives, archivePeerSeen, hookFailureDigest, + hookFailureDigestCsvExport, + archiveHealthPrefs, modDigestGroupDmMeta, workerReleaseHint, automodConfig: automodConfig @@ -1427,6 +1435,20 @@ class PearcordPlatform extends EventEmitter { payload.digestRelayHandoff ) } + if (payload.hookFailureDigestCsvExport) { + await this._initDeliveryReceipts(payload.guildId) + await this.deliveryReceipts.ingestHookFailureDigestCsvSlice( + payload.guildId, + payload.hookFailureDigestCsvExport + ) + } + if (payload.archiveHealthPrefs) { + await this._initDeliveryReceipts(payload.guildId) + await this.deliveryReceipts.ingestArchiveHealthPrefsSlice( + payload.guildId, + payload.archiveHealthPrefs + ) + } if (payload.auditExportSchedule) { await this._initDeliveryReceipts(payload.guildId) await this.deliveryReceipts.ingestAuditExportScheduleSlice( @@ -2725,7 +2747,8 @@ class PearcordPlatform extends EventEmitter { guildMute: false, muteNotifications: false, digestInboxChannelId: null, - digestRelayEnabled: false + digestRelayEnabled: false, + digestRelayWebhookUrl: null } } await this._initDeliveryReceipts(this.guild.guild.id) @@ -2741,10 +2764,34 @@ class PearcordPlatform extends EventEmitter { muteNotifications: !!guild.muteNotifications || userMute, digestInboxChannelId: guild.digestInboxChannelId || null, digestRelayEnabled: !!guild.digestRelayEnabled, - activeRelayPluginId: guild.activeRelayPluginId || null + activeRelayPluginId: guild.activeRelayPluginId || null, + digestRelayWebhookUrl: guild.digestRelayWebhookUrl || null } } + async getArchiveHealthPrefs () { + if (!this.guild?.guild) { + return { autoRefreshOnGuildFocus: true, updatedAt: 0 } + } + await this._initDeliveryReceipts(this.guild.guild.id) + return this.deliveryReceipts.getArchiveHealthPrefs() + } + + async setArchiveHealthPrefs (partial = {}) { + if (!this.guild?.guild) throw new Error('no guild') + const roles = await this._memberRoles() + if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) { + throw new Error('no permission to manage archive health prefs') + } + await this._initDeliveryReceipts(this.guild.guild.id) + const prefs = await this.deliveryReceipts.setArchiveHealthPrefs({ + autoRefreshOnGuildFocus: partial.autoRefreshOnGuildFocus + }) + setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) + this._restartArchiveHealthRefreshTimer() + return prefs + } + async setAutomationDigestNotifyPrefs (partial = {}) { if (!this.guild?.guild) throw new Error('no guild') const userId = this.identity.user?.id @@ -2770,7 +2817,13 @@ class PearcordPlatform extends EventEmitter { if (partial.activeRelayPluginId !== undefined) { patch.activeRelayPluginId = partial.activeRelayPluginId || null } + if (partial.digestRelayWebhookUrl !== undefined) { + patch.digestRelayWebhookUrl = partial.digestRelayWebhookUrl || null + } await this.deliveryReceipts.setAutomationDigestNotifyPrefs(patch) + if (patch.digestRelayWebhookUrl !== undefined) { + await this._syncDigestRelayWebhookUrlToPlugin(patch.digestRelayWebhookUrl).catch(() => {}) + } setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) } else { if (partial.userMute != null || partial.muteNotifications != null) { @@ -2867,7 +2920,8 @@ class PearcordPlatform extends EventEmitter { const plugin = await this.deliveryReceipts.getActiveDigestRelayPlugin() if (plugin) { handoff = applyRelayPluginToHandoff(handoff, plugin, { - relaySecret: this.guild.guild.id + relaySecret: this.guild.guild.id, + webhookUrl: prefs.digestRelayWebhookUrl || plugin.config?.webhookUrl || null }) } await this.deliveryReceipts.appendDigestRelayHandoff(handoff) @@ -2978,6 +3032,15 @@ class PearcordPlatform extends EventEmitter { }) signatureValid = verified.ok body = body.csv + await this.deliveryReceipts.recordHookFailureDigestCsvExport({ + csv: body, + signature, + signatureAlg, + exportedAt: Date.now() + }) + if (opts.recordMesh !== false) { + setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) + } } return { format, @@ -2991,6 +3054,18 @@ class PearcordPlatform extends EventEmitter { } } + async getHookFailureDigestCsvExport () { + if (!this.guild?.guild) return null + await this._initDeliveryReceipts(this.guild.guild.id) + const row = await this.deliveryReceipts.getHookFailureDigestCsvExport() + if (!row?.signature) return row + const verified = verifyHookFailureDigestCsv(row.csvBody, row.signature, { + guildId: this.guild.guild.id, + relaySecret: this.guild.guild.id + }) + return { ...row, signatureValid: verified.ok } + } + async setGuildIconImage ({ data, filename, mimeType }) { const user = this.identity.user if (!user || !this.guild?.guild) throw new Error('no guild') @@ -3892,11 +3967,17 @@ class PearcordPlatform extends EventEmitter { guildName: this.guild.guild.name, exported: { exportedAt: Date.now(), body: '', format: 'text' } }) + const digPrefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs() const stamped = plugin - ? applyRelayPluginToHandoff(handoff, plugin, { relaySecret: this.guild.guild.id }) + ? applyRelayPluginToHandoff(handoff, plugin, { + relaySecret: this.guild.guild.id, + webhookUrl: digPrefs.digestRelayWebhookUrl || null + }) : handoff if (plugin?.transport === 'webhook-bridge') { - result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, {}) + result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, { + webhookUrl: digPrefs.digestRelayWebhookUrl || null + }) } } return { sample, result } @@ -4029,6 +4110,8 @@ class PearcordPlatform extends EventEmitter { await this.discovery?.touchGuild(guildRecord.id).catch(() => {}) await this._ensureGuildModules() this._startAuditExportScheduleTimer() + await this._maybeArchiveHealthRefreshOnFocus() + this._restartArchiveHealthRefreshTimer() } async createGuild ({ name }) { @@ -4077,10 +4160,43 @@ class PearcordPlatform extends EventEmitter { }).catch(() => {}) await this._ensureGuildModules() this._startAuditExportScheduleTimer() + await this._maybeArchiveHealthRefreshOnFocus() + this._restartArchiveHealthRefreshTimer() this.emit('guild', result) return result } + _stopArchiveHealthRefreshTimer () { + if (this._archiveHealthRefreshTimer) { + clearInterval(this._archiveHealthRefreshTimer) + this._archiveHealthRefreshTimer = null + } + } + + _restartArchiveHealthRefreshTimer () { + this._stopArchiveHealthRefreshTimer() + if (this.mode !== 'guild' || !this.guild?.guild) return + this._initDeliveryReceipts(this.guild.guild.id) + .then(() => this.deliveryReceipts.getArchiveHealthPrefs()) + .then((prefs) => { + if (!prefs.autoRefreshOnGuildFocus) return + this._archiveHealthRefreshTimer = setInterval(() => { + this.refreshArchivePeerHealth().catch(() => {}) + }, 30000) + }) + .catch(() => {}) + } + + async _maybeArchiveHealthRefreshOnFocus () { + if (!this.guild?.guild) return null + await this._initDeliveryReceipts(this.guild.guild.id) + const prefs = await this.deliveryReceipts.getArchiveHealthPrefs() + if (!prefs.autoRefreshOnGuildFocus) return null + const last = this._archivePeerHealthRefreshedAt || 0 + if (Date.now() - last < 5000) return null + return this.refreshArchivePeerHealth().catch(() => null) + } + async listGuilds () { return this.db.find(COLLECTIONS.GUILDS, {}) } @@ -4114,6 +4230,8 @@ class PearcordPlatform extends EventEmitter { await this.discovery?.recordGuild(guild).catch(() => {}) await this._ensureGuildModules() this._startAuditExportScheduleTimer() + await this._maybeArchiveHealthRefreshOnFocus() + this._restartArchiveHealthRefreshTimer() return guild } @@ -5857,12 +5975,24 @@ class PearcordPlatform extends EventEmitter { }) } } + const prefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs() const preview = dryRunWebhookRelayPost(handoff, { - webhookUrl: opts.webhookUrl || null + webhookUrl: opts.webhookUrl || prefs.digestRelayWebhookUrl || handoff.webhookUrl || null }) return { handoff, preview } } + async _syncDigestRelayWebhookUrlToPlugin (url) { + if (!this.guild?.guild) return null + const plugins = await this.deliveryReceipts.listDigestRelayPlugins() + const webhook = plugins.find((p) => p.pluginId === 'pearcord/digest-webhook-bridge') + if (!webhook) return null + return this.deliveryReceipts.upsertDigestRelayPlugin({ + ...webhook, + config: { ...(webhook.config || {}), webhookUrl: url || null } + }) + } + async syncModDigestGroupDmFromMesh (slice = {}) { if (!this.guild?.guild || !slice?.channelId) return null const roles = await this._memberRoles() @@ -7902,6 +8032,12 @@ class PearcordPlatform extends EventEmitter { digestRelayHandoff, auditArchiveFetchPeers, archivePeerHealthRefresh: this.getArchivePeerHealthRefreshStatus(), + archiveHealthPrefs: this.guild?.guild + ? await this.getArchiveHealthPrefs().catch(() => ({ + autoRefreshOnGuildFocus: true, + updatedAt: 0 + })) + : { autoRefreshOnGuildFocus: true, updatedAt: 0 }, workerReleaseHint, workerInstallPlan, auditLogFilterOptions,