Phase 719: read state and webhook execute JSON mesh parity (v0.8.695)
Add dedicated signed-export modals for read receipts and webhook execute exports, populate view rows and meta, partition-heal mixins with dedicated spans, agentctl journeys, and CI smokes following the Phase 718 pattern. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -1851,6 +1851,12 @@ class PearcordPlatform extends EventEmitter {
|
||||
const guildWidgetJsonHeal = await this._healGuildWidgetJsonExportCursorOnPartition(gid).catch(() => ({
|
||||
relisted: 0
|
||||
}))
|
||||
const readStateJsonHeal = await this._healReadStateJsonExportCursorOnPartition(gid).catch(() => ({
|
||||
relisted: 0
|
||||
}))
|
||||
const webhookJsonHeal = await this._healWebhookJsonExportCursorOnPartition(gid).catch(() => ({
|
||||
relisted: 0
|
||||
}))
|
||||
return {
|
||||
voiceApplied,
|
||||
emojiSlots,
|
||||
@@ -1921,7 +1927,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
multiGuildSyncJsonHeal,
|
||||
partitionHealDiagnosticsJsonHeal,
|
||||
channelFollowJsonHeal,
|
||||
guildWidgetJsonHeal
|
||||
guildWidgetJsonHeal,
|
||||
readStateJsonHeal,
|
||||
webhookJsonHeal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14255,10 +14263,40 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async getWebhookExecuteExportJsonExport () {
|
||||
const gid = this.guild?.guild?.id || null
|
||||
const span = this.log.time('webhook.json', { spanKind: 'webhook.json', guildId: gid, context: 'read' })
|
||||
try {
|
||||
if (!this.guild?.guild) { span.end({ guildWebhookExecuteJsonCount: 0, skipped: true }); return null }
|
||||
await this._initDeliveryReceipts(this.guild.guild.id)
|
||||
const row = await this.deliveryReceipts.getWebhookExecuteExportJsonExport()
|
||||
if (!row?.signature) {
|
||||
span.end({ guildWebhookExecuteJsonCount: row ? 1 : 0, hasSignature: false, bridgeKind: 'webhook.json' })
|
||||
return row
|
||||
}
|
||||
const verified = verifyWebhookExecuteExportJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: this.guild.guild.id, relaySecret: this.guild.guild.id })
|
||||
const out = { ...row, signatureValid: verified.ok, webhookCount: row.webhookCount || 0 }
|
||||
span.end({ guildWebhookExecuteJsonCount: 1, hasSignature: true, signatureValid: verified.ok, bridgeKind: 'webhook.json' })
|
||||
return out
|
||||
} catch (err) {
|
||||
this.log.error('webhook.json error', { guildId: gid, context: 'read', error: err?.message || String(err) })
|
||||
span.fail(err); throw err
|
||||
}
|
||||
}
|
||||
|
||||
async clearWebhookExecuteExportJsonExports () {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
if (!(await this._hasPerm(PERMISSION.MANAGE_GUILD))) throw new Error('no permission to clear webhook execute JSON exports')
|
||||
await this._initDeliveryReceipts(this.guild.guild.id)
|
||||
const removed = await this.deliveryReceipts.clearWebhookExecuteExportJsonExports()
|
||||
this.emit('webhook-execute-export-json-cleared', { removed })
|
||||
return { removed }
|
||||
}
|
||||
|
||||
async pushWebhookExecuteExportToMesh () {
|
||||
const gid = this.guild?.guild?.id || null
|
||||
const span = this.log.time('bot.json', {
|
||||
spanKind: 'bot.json',
|
||||
const span = this.log.time('webhook.json', {
|
||||
spanKind: 'webhook.json',
|
||||
guildId: gid,
|
||||
context: 'mesh.execute'
|
||||
})
|
||||
@@ -14290,13 +14328,13 @@ class PearcordPlatform extends EventEmitter {
|
||||
meshPushed: true
|
||||
}
|
||||
span.end({
|
||||
guildBotRegistryCount: exported.webhookCount || 0,
|
||||
guildWebhookExecuteJsonCount: exported.webhookCount || 0,
|
||||
meshPushed: true,
|
||||
bridgeKind: 'bot.json'
|
||||
bridgeKind: 'webhook.json'
|
||||
})
|
||||
return exported
|
||||
} catch (err) {
|
||||
this.log.error('bot.json error', {
|
||||
this.log.error('webhook.json error', {
|
||||
guildId: gid,
|
||||
context: 'mesh.execute',
|
||||
error: err?.message || String(err)
|
||||
@@ -26052,10 +26090,40 @@ class PearcordPlatform extends EventEmitter {
|
||||
return { format: 'json', body: signed.json, signature: signed.signature, signatureAlg: signed.signatureAlg, exportedAt, readStateCount: entries.length, meta }
|
||||
}
|
||||
|
||||
async getReadStateExportJsonExport () {
|
||||
const gid = this.guild?.guild?.id || null
|
||||
const span = this.log.time('read-state.json', { spanKind: 'read-state.json', guildId: gid, context: 'read' })
|
||||
try {
|
||||
if (!this.guild?.guild) { span.end({ guildReadStateJsonCount: 0, skipped: true }); return null }
|
||||
await this._initDeliveryReceipts(this.guild.guild.id)
|
||||
const row = await this.deliveryReceipts.getReadStateExportJsonExport()
|
||||
if (!row?.signature) {
|
||||
span.end({ guildReadStateJsonCount: row ? 1 : 0, hasSignature: false, bridgeKind: 'read-state.json' })
|
||||
return row
|
||||
}
|
||||
const verified = verifyReadStateExportJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: this.guild.guild.id, relaySecret: this.guild.guild.id })
|
||||
const out = { ...row, signatureValid: verified.ok, readStateCount: row.readStateCount || 0 }
|
||||
span.end({ guildReadStateJsonCount: 1, hasSignature: true, signatureValid: verified.ok, bridgeKind: 'read-state.json' })
|
||||
return out
|
||||
} catch (err) {
|
||||
this.log.error('read-state.json error', { guildId: gid, context: 'read', error: err?.message || String(err) })
|
||||
span.fail(err); throw err
|
||||
}
|
||||
}
|
||||
|
||||
async clearReadStateExportJsonExports () {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
if (!(await this._hasPerm(PERMISSION.MANAGE_GUILD))) throw new Error('no permission to clear read state JSON exports')
|
||||
await this._initDeliveryReceipts(this.guild.guild.id)
|
||||
const removed = await this.deliveryReceipts.clearReadStateExportJsonExports()
|
||||
this.emit('read-state-export-json-cleared', { removed })
|
||||
return { removed }
|
||||
}
|
||||
|
||||
async pushReadStateExportToMesh () {
|
||||
const gid = this.guild?.guild?.id || null
|
||||
const span = this.log.time('presence.json', {
|
||||
spanKind: 'presence.json',
|
||||
const span = this.log.time('read-state.json', {
|
||||
spanKind: 'read-state.json',
|
||||
guildId: gid,
|
||||
context: 'mesh.read-state'
|
||||
})
|
||||
@@ -26087,13 +26155,13 @@ class PearcordPlatform extends EventEmitter {
|
||||
meshPushed: true
|
||||
}
|
||||
span.end({
|
||||
guildPresenceJsonCount: exported.readStateCount || 0,
|
||||
guildReadStateJsonCount: exported.readStateCount || 0,
|
||||
meshPushed: true,
|
||||
bridgeKind: 'presence.json'
|
||||
bridgeKind: 'read-state.json'
|
||||
})
|
||||
return exported
|
||||
} catch (err) {
|
||||
this.log.error('presence.json error', {
|
||||
this.log.error('read-state.json error', {
|
||||
guildId: gid,
|
||||
context: 'mesh.read-state',
|
||||
error: err?.message || String(err)
|
||||
@@ -27382,6 +27450,34 @@ class PearcordPlatform extends EventEmitter {
|
||||
return row
|
||||
}
|
||||
|
||||
async _readStateJsonRegistryEntries () {
|
||||
if (!this.guild?.guild) return []
|
||||
const rows = await this._readReceiptSyncRowsForView(this.guild.guild.id, 64).catch(() => [])
|
||||
if (!rows.length) {
|
||||
return [{ channelId: '', channelName: 'none', readStateLabel: 'unread', exportedAt: Date.now() }]
|
||||
}
|
||||
return rows.slice(0, 32).map((row) => ({
|
||||
channelId: row.channelId || '',
|
||||
channelName: row.channelName || '',
|
||||
readStateLabel: row.lastReadAt ? 'read' : 'unread',
|
||||
exportedAt: Date.now()
|
||||
}))
|
||||
}
|
||||
|
||||
async _webhookJsonRegistryEntries () {
|
||||
if (!this.guild?.guild) return []
|
||||
const hooks = await this.listChannelWebhooks().catch(() => [])
|
||||
if (!hooks.length) {
|
||||
return [{ id: 'stub', webhookName: 'none', executeState: 'idle', exportedAt: Date.now() }]
|
||||
}
|
||||
return hooks.slice(0, 32).map((h) => ({
|
||||
id: h.id,
|
||||
webhookName: h.name || 'webhook',
|
||||
executeState: 'registered',
|
||||
exportedAt: h.createdAt || Date.now()
|
||||
}))
|
||||
}
|
||||
|
||||
async _channelFollowJsonRegistryEntries () {
|
||||
if (!this.guild?.guild) return []
|
||||
const follows = this.announcements
|
||||
@@ -32490,6 +32586,8 @@ class PearcordPlatform extends EventEmitter {
|
||||
let botRegistryJsonMeta = null
|
||||
let botRegistryJsonRows = []
|
||||
let webhookExecuteExportJsonRows = []
|
||||
let webhookExecuteExportJsonMeta = null
|
||||
let guildWebhookExecuteEntries = []
|
||||
let lastWebhookExecuteExport = null
|
||||
let slashRegistryJsonMeta = null
|
||||
let slashRegistryJsonRows = []
|
||||
@@ -32554,7 +32652,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
let presenceActivityJsonMeta = null
|
||||
let presenceActivityJsonRows = []
|
||||
let readStateExportJsonRows = []
|
||||
let readStateExportJsonMeta = null
|
||||
let lastReadStateExport = null
|
||||
let guildReadStateEntries = []
|
||||
let guildPresenceActivities = []
|
||||
let notificationOverrideJsonMeta = null
|
||||
let notificationOverrideJsonRows = []
|
||||
@@ -32702,6 +32802,14 @@ class PearcordPlatform extends EventEmitter {
|
||||
return { ...row, signatureValid: verified.ok }
|
||||
})
|
||||
lastWebhookExecuteExport = await this.getLastWebhookExecuteExport()
|
||||
webhookExecuteExportJsonMeta = await this.getWebhookExecuteExportJsonExport().catch(() => null)
|
||||
const webhookEntries = await this._webhookJsonRegistryEntries().catch(() => [])
|
||||
guildWebhookExecuteEntries = webhookEntries.slice(0, 16).map((r) => ({
|
||||
id: r.id || 'webhook',
|
||||
name: r.webhookName || 'Webhook',
|
||||
role: r.executeState || 'registered',
|
||||
updatedAt: r.exportedAt || Date.now()
|
||||
}))
|
||||
slashRegistryJsonMeta = await this.getSlashRegistryJsonExport().catch(() => null)
|
||||
const slashJsonHistory = await this.deliveryReceipts.listSlashRegistryJsonExports(32)
|
||||
slashRegistryJsonRows = slashJsonHistory.map((row) => {
|
||||
@@ -32940,6 +33048,20 @@ class PearcordPlatform extends EventEmitter {
|
||||
role: r.widgetState || 'private',
|
||||
updatedAt: r.exportedAt || Date.now()
|
||||
}))
|
||||
readStateExportJsonMeta = await this.getReadStateExportJsonExport().catch(() => null)
|
||||
readStateExportJsonRows = (await this.deliveryReceipts.listReadStateExportJsonExports(32)).map((row) => {
|
||||
if (!row?.signature || !guild?.id) return row
|
||||
const verified = verifyReadStateExportJson(exportBodyFromSlice(row) || row.jsonBody, row.signature, { guildId: guild.id, relaySecret: guild.id })
|
||||
return { ...row, signatureValid: verified.ok }
|
||||
})
|
||||
lastReadStateExport = await this.getLastReadStateExport().catch(() => null)
|
||||
const readEntries = await this._readStateJsonRegistryEntries().catch(() => [])
|
||||
guildReadStateEntries = readEntries.slice(0, 16).map((r) => ({
|
||||
id: r.channelId || 'read',
|
||||
name: r.channelName || 'Channel',
|
||||
role: r.readStateLabel || 'read',
|
||||
updatedAt: r.exportedAt || Date.now()
|
||||
}))
|
||||
const multiEntries = await this._multiGuildSyncJsonRegistryEntries().catch(() => [])
|
||||
guildMultiGuildSyncEntries = multiEntries.slice(0, 16).map((r) => ({
|
||||
id: r.guildId || guild.id,
|
||||
@@ -33527,6 +33649,10 @@ class PearcordPlatform extends EventEmitter {
|
||||
botRegistryJsonMeta,
|
||||
botRegistryJsonRows,
|
||||
webhookExecuteExportJsonRows,
|
||||
webhookExecuteExportJsonMeta,
|
||||
guildWebhookExecuteEntries,
|
||||
guildWebhookExecuteJsonCount:
|
||||
(webhookExecuteExportJsonRows || []).length + (guildWebhookExecuteEntries || []).length,
|
||||
lastWebhookExecuteExport,
|
||||
slashRegistryJsonMeta,
|
||||
slashRegistryJsonRows,
|
||||
@@ -33613,6 +33739,10 @@ class PearcordPlatform extends EventEmitter {
|
||||
presenceActivityJsonMeta,
|
||||
presenceActivityJsonRows,
|
||||
readStateExportJsonRows,
|
||||
readStateExportJsonMeta,
|
||||
guildReadStateEntries,
|
||||
guildReadStateJsonCount:
|
||||
(readStateExportJsonRows || []).length + (guildReadStateEntries || []).length,
|
||||
lastReadStateExport,
|
||||
guildPresenceActivities,
|
||||
guildPresenceJsonCount:
|
||||
@@ -34318,6 +34448,8 @@ const { multiGuildJsonMixin } = require('./multi-guild-json-mixin')
|
||||
const { partitionHealJsonMixin } = require('./partition-heal-json-mixin')
|
||||
const { channelFollowJsonMixin } = require('./channel-follow-json-mixin')
|
||||
const { guildWidgetJsonMixin } = require('./guild-widget-json-mixin')
|
||||
const { readStateJsonMixin } = require('./read-state-json-mixin')
|
||||
const { webhookJsonMixin } = require('./webhook-json-mixin')
|
||||
Object.assign(PearcordPlatform.prototype, pollSchedulingMixin)
|
||||
Object.assign(PearcordPlatform.prototype, notificationsActivityMixin)
|
||||
Object.assign(PearcordPlatform.prototype, userSettingsMixin)
|
||||
@@ -34365,3 +34497,5 @@ Object.assign(PearcordPlatform.prototype, multiGuildJsonMixin)
|
||||
Object.assign(PearcordPlatform.prototype, partitionHealJsonMixin)
|
||||
Object.assign(PearcordPlatform.prototype, channelFollowJsonMixin)
|
||||
Object.assign(PearcordPlatform.prototype, guildWidgetJsonMixin)
|
||||
Object.assign(PearcordPlatform.prototype, readStateJsonMixin)
|
||||
Object.assign(PearcordPlatform.prototype, webhookJsonMixin)
|
||||
|
||||
Reference in New Issue
Block a user