feat(platform): Phase 115 bridges — CSV mesh, archive health, webhook URL (v0.8.79)
- 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 <[email protected]>
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user