feat(platform): Phase 105 mesh peer indicator and digest export schedule

- listAuditArchiveFetchPeers returns meshConnected per member
- automationDigestExportSchedule timer and snapshot persistence
- GUILD_SYNC slices for schedule and snapshot metadata
- runOverdueAutomationSchedules includes digest export task

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 05:23:36 -04:00
co-authored by Cursor
parent 2eb9ece6f0
commit 7adf714b8a
+104 -6
View File
@@ -1167,12 +1167,18 @@ class PearcordPlatform extends EventEmitter {
const auditRecent = await this._exportAuditSyncSlice(guild.id) const auditRecent = await this._exportAuditSyncSlice(guild.id)
let auditExportSchedule = null let auditExportSchedule = null
let digestExportSchedule = null let digestExportSchedule = null
let automationDigestExportSchedule = null
let automationDigestExportSnapshots = []
let auditExportArchives = [] let auditExportArchives = []
let hookFailureDigest = null let hookFailureDigest = null
await this._initDeliveryReceipts(guild.id) await this._initDeliveryReceipts(guild.id)
if (this.deliveryReceipts) { if (this.deliveryReceipts) {
auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice() auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice()
digestExportSchedule = await this.deliveryReceipts.exportDigestExportScheduleSlice() digestExportSchedule = await this.deliveryReceipts.exportDigestExportScheduleSlice()
automationDigestExportSchedule =
await this.deliveryReceipts.exportAutomationDigestExportScheduleSlice()
automationDigestExportSnapshots =
await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2)
auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3) auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3)
const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot() const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot()
if (digestSnap) { if (digestSnap) {
@@ -1232,6 +1238,8 @@ class PearcordPlatform extends EventEmitter {
auditRecent, auditRecent,
auditExportSchedule, auditExportSchedule,
digestExportSchedule, digestExportSchedule,
automationDigestExportSchedule,
automationDigestExportSnapshots,
auditExportArchives, auditExportArchives,
hookFailureDigest, hookFailureDigest,
workerReleaseHint, workerReleaseHint,
@@ -1342,6 +1350,13 @@ class PearcordPlatform extends EventEmitter {
payload.digestExportSchedule payload.digestExportSchedule
) )
} }
if (payload.automationDigestExportSchedule) {
await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestAutomationDigestExportScheduleSlice(
payload.guildId,
payload.automationDigestExportSchedule
)
}
for (const archive of payload.auditExportArchives || []) { for (const archive of payload.auditExportArchives || []) {
if (!archive?.id || archive.guildId !== payload.guildId) continue if (!archive?.id || archive.guildId !== payload.guildId) continue
await this._initDeliveryReceipts(payload.guildId) await this._initDeliveryReceipts(payload.guildId)
@@ -2057,8 +2072,9 @@ class PearcordPlatform extends EventEmitter {
} }
async listAuditArchiveFetchPeers () { async listAuditArchiveFetchPeers () {
if (!this.guild?.guild) return { meshPeerCount: 0, options: [] } if (!this.guild?.guild) return { meshPeerCount: 0, meshLive: false, options: [] }
const meshPeerCount = this.guild.getStats?.()?.peers || 0 const meshPeerCount = this.guild.getStats?.()?.peers || 0
const meshLive = meshPeerCount > 0
const selfId = this.identity.user?.id || null const selfId = this.identity.user?.id || null
const presenceSnap = this.presence?.snapshot?.() || { peers: [] } const presenceSnap = this.presence?.snapshot?.() || { peers: [] }
const onlineIds = new Set( const onlineIds = new Set(
@@ -2068,8 +2084,11 @@ class PearcordPlatform extends EventEmitter {
const options = [ const options = [
{ {
memberId: null, memberId: null,
label: meshPeerCount > 0 ? `Any connected peer (${meshPeerCount} on mesh)` : 'Any peer (mesh offline)', label: meshLive
online: meshPeerCount > 0, ? `Any connected peer (${meshPeerCount} on mesh)`
: 'Any peer (mesh offline)',
online: meshLive,
meshConnected: meshLive,
isSelf: false isSelf: false
} }
] ]
@@ -2079,15 +2098,17 @@ class PearcordPlatform extends EventEmitter {
if (!userId) continue if (!userId) continue
const user = await this.db.get(COLLECTIONS.USERS, { id: userId }).catch(() => null) const user = await this.db.get(COLLECTIONS.USERS, { id: userId }).catch(() => null)
const name = user?.displayName || user?.username || userId.slice(0, 8) const name = user?.displayName || user?.username || userId.slice(0, 8)
const online = onlineIds.has(userId) || meshPeerCount > 0 const presenceOnline = onlineIds.has(userId)
const meshConnected = meshLive && presenceOnline
options.push({ options.push({
memberId: userId, memberId: userId,
label: userId === selfId ? `${name} (you)` : name, label: userId === selfId ? `${name} (you)` : name,
online, online: presenceOnline || (userId === selfId && meshLive),
meshConnected,
isSelf: userId === selfId isSelf: userId === selfId
}) })
} }
return { meshPeerCount, options } return { meshPeerCount, meshLive, options }
} }
_resolveAuditArchiveFetchWaiter (archive) { _resolveAuditArchiveFetchWaiter (archive) {
@@ -2348,11 +2369,17 @@ class PearcordPlatform extends EventEmitter {
digestResult = await this.runScheduledDigestExportNow() digestResult = await this.runScheduledDigestExportNow()
if (digestResult) ran.push('digest-sync') if (digestResult) ran.push('digest-sync')
} }
let automationDigestResult = null
if (before.automationDigestExport?.overdue) {
automationDigestResult = await this.runScheduledAutomationDigestExportNow()
if (automationDigestResult) ran.push('automation-digest-export')
}
const dashboard = await this.getAutomationScheduleDashboard() const dashboard = await this.getAutomationScheduleDashboard()
return { return {
ran, ran,
auditResult, auditResult,
digestResult, digestResult,
automationDigestResult,
dashboard, dashboard,
guildId: this.guild.guild.id guildId: this.guild.guild.id
} }
@@ -2364,14 +2391,77 @@ class PearcordPlatform extends EventEmitter {
} }
const auditExportSchedule = await this.getAuditExportSchedule() const auditExportSchedule = await this.getAuditExportSchedule()
const digestExportSchedule = await this.getDigestExportSchedule() const digestExportSchedule = await this.getDigestExportSchedule()
const automationDigestExportSchedule = await this.getAutomationDigestExportSchedule()
const hookFailureDigest = await this.getHookFailureDigest() const hookFailureDigest = await this.getHookFailureDigest()
return buildAutomationScheduleDashboard({ return buildAutomationScheduleDashboard({
auditExportSchedule, auditExportSchedule,
digestExportSchedule, digestExportSchedule,
automationDigestExportSchedule,
hookFailureDigest hookFailureDigest
}) })
} }
async getAutomationDigestExportSchedule () {
if (!this.guild?.guild) {
return { enabled: false, intervalHours: 24, format: 'text', lastExportAt: 0 }
}
await this._initDeliveryReceipts(this.guild.guild.id)
return this.deliveryReceipts.getAutomationDigestExportSchedule()
}
async setAutomationDigestExportSchedule (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 automation digest export schedule')
}
await this._initDeliveryReceipts(this.guild.guild.id)
const prefs = await this.deliveryReceipts.setAutomationDigestExportSchedule(partial)
this.emit('automation-digest-export-schedule', prefs)
this._startAuditExportScheduleTimer()
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return prefs
}
async listAutomationDigestExportSnapshots (limit = 3) {
if (!this.guild?.guild) return []
await this._initDeliveryReceipts(this.guild.guild.id)
return this.deliveryReceipts.listAutomationDigestExportSnapshots(limit)
}
async runScheduledAutomationDigestExportNow () {
return this._runScheduledAutomationDigestExportIfDue(true)
}
async _runScheduledAutomationDigestExportIfDue (force = false) {
if (!this.guild?.guild) return null
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) return null
await this._initDeliveryReceipts(this.guild.guild.id)
const schedule = await this.deliveryReceipts.getAutomationDigestExportSchedule()
if (!schedule.enabled && !force) return null
const intervalMs = (schedule.intervalHours || 24) * 60 * 60 * 1000
const last = schedule.lastExportAt || 0
if (!force && Date.now() - last < intervalMs) return null
const exported = await this.exportAutomationScheduleDigest({
format: schedule.format || 'text'
})
const summaryLines = String(exported.body || '').split('\n').length
const snap = await this.deliveryReceipts.appendAutomationDigestExportSnapshot({
exportedAt: exported.exportedAt,
exportedBy: this.identity.user?.id || null,
format: exported.format,
summaryLines,
body: exported.body
})
await this.deliveryReceipts.setAutomationDigestExportSchedule({
lastExportAt: exported.exportedAt
})
this.emit('automation-digest-export-scheduled', { exported, snap, schedule })
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return { ...exported, snapshotId: snap?.id || null }
}
async exportHookFailureDigest (opts = {}) { async exportHookFailureDigest (opts = {}) {
if (!this.guild?.guild) throw new Error('no guild') if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles() const roles = await this._memberRoles()
@@ -5084,9 +5174,11 @@ class PearcordPlatform extends EventEmitter {
this._auditScheduleTimer = setInterval(() => { this._auditScheduleTimer = setInterval(() => {
this._runScheduledAuditExportIfDue().catch(() => {}) this._runScheduledAuditExportIfDue().catch(() => {})
this._runScheduledDigestExportIfDue().catch(() => {}) this._runScheduledDigestExportIfDue().catch(() => {})
this._runScheduledAutomationDigestExportIfDue().catch(() => {})
}, 60 * 1000) }, 60 * 1000)
this._runScheduledAuditExportIfDue().catch(() => {}) this._runScheduledAuditExportIfDue().catch(() => {})
this._runScheduledDigestExportIfDue().catch(() => {}) this._runScheduledDigestExportIfDue().catch(() => {})
this._runScheduledAutomationDigestExportIfDue().catch(() => {})
} }
async runScheduledAuditExportNow () { async runScheduledAuditExportNow () {
@@ -6652,6 +6744,8 @@ class PearcordPlatform extends EventEmitter {
let hookFailureDigest = null let hookFailureDigest = null
let auditExportSchedule = null let auditExportSchedule = null
let digestExportSchedule = null let digestExportSchedule = null
let automationDigestExportSchedule = null
let automationDigestExportSnapshots = []
let auditExportArchives = [] let auditExportArchives = []
let automationScheduleDashboard = null let automationScheduleDashboard = null
let auditArchiveFetchPeers = null let auditArchiveFetchPeers = null
@@ -6661,6 +6755,8 @@ class PearcordPlatform extends EventEmitter {
hookFailureDigest = await this.getHookFailureDigest() hookFailureDigest = await this.getHookFailureDigest()
auditExportSchedule = await this.getAuditExportSchedule() auditExportSchedule = await this.getAuditExportSchedule()
digestExportSchedule = await this.getDigestExportSchedule() digestExportSchedule = await this.getDigestExportSchedule()
automationDigestExportSchedule = await this.getAutomationDigestExportSchedule()
automationDigestExportSnapshots = await this.listAutomationDigestExportSnapshots(3)
auditExportArchives = await this.listAuditExportArchives(6) auditExportArchives = await this.listAuditExportArchives(6)
automationScheduleDashboard = await this.getAutomationScheduleDashboard() automationScheduleDashboard = await this.getAutomationScheduleDashboard()
auditArchiveFetchPeers = await this.listAuditArchiveFetchPeers() auditArchiveFetchPeers = await this.listAuditArchiveFetchPeers()
@@ -6835,6 +6931,8 @@ class PearcordPlatform extends EventEmitter {
hookFailureDigest, hookFailureDigest,
auditExportSchedule, auditExportSchedule,
digestExportSchedule, digestExportSchedule,
automationDigestExportSchedule,
automationDigestExportSnapshots,
auditExportArchives, auditExportArchives,
automationScheduleDashboard, automationScheduleDashboard,
auditArchiveFetchPeers, auditArchiveFetchPeers,