feat(v0.8.60): audit filters, hook failure digest, worker release hints

- listAuditLog(filter), getHookFailureDigest, announceWorkerRelease
- WORKER_RELEASE_ANNOUNCE gossip ingest + GUILD_SYNC.workerReleaseHint
- appVersion constructor option for update-available comparison
- view: hookFailureDigest, workerReleaseHint, auditLogFilterOptions

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 04:17:52 -04:00
co-authored by Cursor
parent c407bc5d55
commit 5a77582dbb
+119 -3
View File
@@ -38,13 +38,17 @@ const {
dryRunHook,
dryRunHooks,
buildDeliveryMetrics,
buildHookFailureDigest,
buildHookFailureAlert,
AUDIT_FILTER,
filterAuditEntries,
HookFailureAlertStore
} = require('pearcord-delivery-receipts')
const {
listWorkerTemplates,
buildWorkerLaunchScript,
getWorkerTemplate
getWorkerTemplate,
WorkerReleaseHintStore
} = require('pearcord-integration-worker')
const { PearcordIntegrationWorker } = require('./integration-worker')
const { ACTION_TYPE_LIST, CATEGORY_OPTIONS } = require('pearcord-automation-export')
@@ -244,6 +248,8 @@ class PearcordPlatform extends EventEmitter {
this.deliveryReceipts = null
this.hookDispatcher = null
this.hookFailureAlerts = null
this.workerReleaseHints = null
this.appVersion = opts.appVersion || process.env.PEARCORD_APP_VERSION || '0.0.0'
this.discovery = null
this.contacts = null
this.botLimits = null
@@ -1148,6 +1154,17 @@ class PearcordPlatform extends EventEmitter {
hookFailurePrefs = await this.deliveryReceipts.exportFailurePrefsSlice()
}
const auditRecent = await this._exportAuditSyncSlice(guild.id)
let workerReleaseHint = null
await this._initWorkerReleaseHints()
const hintRow = await this.workerReleaseHints.getHint(guild.id)
if (hintRow) {
workerReleaseHint = {
workerVersion: hintRow.workerVersion,
pearUri: hintRow.pearUri,
publishedAt: hintRow.publishedAt,
publishedBy: hintRow.publishedBy
}
}
let automodConfig = null
await this._ensureGuildModules()
if (this.automod) automodConfig = await this.automod.getConfig()
@@ -1177,6 +1194,7 @@ class PearcordPlatform extends EventEmitter {
receiptRetention,
hookFailurePrefs,
auditRecent,
workerReleaseHint,
automodConfig: automodConfig
? {
enabled: !!automodConfig.enabled,
@@ -1284,6 +1302,12 @@ class PearcordPlatform extends EventEmitter {
})
}
}
if (payload.workerReleaseHint?.workerVersion) {
await this._onWorkerReleaseHintGossip({
guildId: payload.guildId,
...payload.workerReleaseHint
})
}
if (payload.automodConfig && this.automod) {
await this.automod.ingestGossip({
...payload.automodConfig,
@@ -1515,6 +1539,9 @@ class PearcordPlatform extends EventEmitter {
guildInstance.on('hook-failure', (alert) => {
this._onHookFailureAlertGossip(alert).catch(() => {})
})
guildInstance.on('worker-release-hint', (payload) => {
this._onWorkerReleaseHintGossip(payload).catch(() => {})
})
guildInstance.on('audit', async (p) => {
if (p?.guildId && p?.id) {
const existing = await this.db.get(COLLECTIONS.AUDIT_LOG, { id: p.id })
@@ -1823,12 +1850,13 @@ class PearcordPlatform extends EventEmitter {
return this.notifications.markAllRead()
}
async listAuditLog (limit = 50) {
async listAuditLog (limit = 50, opts = {}) {
const guildId = this.guild?.guild?.id
if (!guildId) return []
const rows = await this.db.find(COLLECTIONS.AUDIT_LOG, { guildId })
rows.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0))
const slice = rows.slice(0, limit)
const filtered = filterAuditEntries(rows, opts.filter || AUDIT_FILTER.ALL)
const slice = filtered.slice(0, limit)
const out = []
for (const row of slice) {
const actor = await this.db.get(COLLECTIONS.USERS, { id: row.actorId })
@@ -4329,6 +4357,78 @@ class PearcordPlatform extends EventEmitter {
return buildDeliveryMetrics(rows, { windowMs: windowMs || 24 * 60 * 60 * 1000 })
}
async getHookFailureDigest (windowMs) {
if (!this.guild?.guild) {
return buildHookFailureDigest([], [], { windowMs })
}
await this._initDeliveryReceipts(this.guild.guild.id)
const alerts = await this.hookFailureAlerts.listRecent(64)
const receipts = await this.deliveryReceipts.listRecent(512)
return buildHookFailureDigest(alerts, receipts, {
windowMs: windowMs || 24 * 60 * 60 * 1000
})
}
async _initWorkerReleaseHints () {
if (!this.workerReleaseHints) {
this.workerReleaseHints = new WorkerReleaseHintStore({
storagePath: this.storagePath
})
await this.workerReleaseHints.ready()
}
return this.workerReleaseHints
}
async getWorkerReleaseHint () {
if (!this.guild?.guild) {
return {
localVersion: this.appVersion,
remoteVersion: null,
updateAvailable: false,
pearUri: null
}
}
await this._initWorkerReleaseHints()
return this.workerReleaseHints.buildViewHint(this.guild.guild.id, this.appVersion)
}
async announceWorkerRelease ({ workerVersion, pearUri } = {}) {
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 announce worker release')
}
const version = workerVersion || this.appVersion
await this._initWorkerReleaseHints()
const row = await this.workerReleaseHints.setHint({
guildId: this.guild.guild.id,
workerVersion: version,
pearUri: pearUri || null,
publishedBy: this.identity.user?.id || null
})
const payload = {
guildId: this.guild.guild.id,
workerVersion: row.workerVersion,
pearUri: row.pearUri,
publishedAt: row.publishedAt,
publishedBy: row.publishedBy
}
if (this.guild.gossipWorkerReleaseAnnounce) {
this.guild.gossipWorkerReleaseAnnounce(payload)
}
this.emit('worker-release-hint', payload)
return this.getWorkerReleaseHint()
}
async _onWorkerReleaseHintGossip (payload) {
if (!payload?.guildId || !payload?.workerVersion) return null
if (this.guild?.guild?.id !== payload.guildId) return null
await this._initWorkerReleaseHints()
const row = await this.workerReleaseHints.ingestGossip(payload)
if (row) this.emit('worker-release-hint', row)
return row
}
async _assertAutomod (content) {
if (this.mode !== 'guild' || !this.guild?.guild) return
await this._ensureGuildModules()
@@ -5802,6 +5902,18 @@ class PearcordPlatform extends EventEmitter {
hookFailurePrefs = await this.getHookFailurePrefs()
hookFailureAlerts = await this.listHookFailureAlerts(8)
}
let hookFailureDigest = null
let workerReleaseHint = null
if (guild) {
hookFailureDigest = await this.getHookFailureDigest()
workerReleaseHint = await this.getWorkerReleaseHint()
}
const auditLogFilterOptions = [
{ id: AUDIT_FILTER.ALL, label: 'All' },
{ id: AUDIT_FILTER.AUTOMOD, label: 'Automod' },
{ id: AUDIT_FILTER.AUTOMATION, label: 'Automation' },
{ id: AUDIT_FILTER.MODERATION, label: 'Moderation' }
]
let announcementCrosspostTargets = []
let announcementCrosspostByChannel = {}
let announcementFollowing = false
@@ -5956,6 +6068,10 @@ class PearcordPlatform extends EventEmitter {
automationReceiptRetention,
hookFailurePrefs,
hookFailureAlerts,
hookFailureDigest,
workerReleaseHint,
auditLogFilterOptions,
appVersion: this.appVersion,
automationHookMeta,
integrationWorkerTemplates,
announcementCrosspostTargets,