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:
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user