feat(v0.8.58): GUILD_SYNC auditRecent and hook failure mute prefs

- _exportAuditSyncSlice: up to 32 automod/automation/moderation audits
- Ingest auditRecent on GUILD_SYNC; sync hookFailurePrefs
- getHookFailurePrefs / setHookFailurePrefs (MANAGE_GUILD)
- _notifyHookFailure respects muteNotifications
- view.hookFailurePrefs; push GUILD_SYNC after prefs/retention save

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 04:05:11 -04:00
co-authored by Cursor
parent 6bf4576439
commit 4c8d177c4b
+81
View File
@@ -1137,6 +1137,7 @@ class PearcordPlatform extends EventEmitter {
}
let automationSlice = { hooks: [] }
let receiptRetention = null
let hookFailurePrefs = null
await this._initAutomationExport(guild.id)
if (this.automationExport) {
automationSlice = await this.automationExport.exportSyncSlice()
@@ -1144,7 +1145,9 @@ class PearcordPlatform extends EventEmitter {
await this._initDeliveryReceipts(guild.id)
if (this.deliveryReceipts) {
receiptRetention = await this.deliveryReceipts.exportRetentionSlice()
hookFailurePrefs = await this.deliveryReceipts.exportFailurePrefsSlice()
}
const auditRecent = await this._exportAuditSyncSlice(guild.id)
let automodConfig = null
await this._ensureGuildModules()
if (this.automod) automodConfig = await this.automod.getConfig()
@@ -1172,6 +1175,8 @@ class PearcordPlatform extends EventEmitter {
channelSettings,
automationHooks: automationSlice.hooks,
receiptRetention,
hookFailurePrefs,
auditRecent,
automodConfig: automodConfig
? {
enabled: !!automodConfig.enabled,
@@ -1185,6 +1190,31 @@ class PearcordPlatform extends EventEmitter {
}
}
async _exportAuditSyncSlice (guildId, limit = 32) {
if (!guildId) return []
const rows = await this.db.find(COLLECTIONS.AUDIT_LOG, { guildId })
return rows
.filter((row) => {
const action = String(row.action || '')
return (
action.startsWith('automod.') ||
action.startsWith('automation.') ||
action.startsWith('member.ban') ||
action.startsWith('member.kick')
)
})
.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0))
.slice(0, limit)
.map((row) => ({
id: row.id,
guildId: row.guildId,
actorId: row.actorId,
action: row.action,
targetId: row.targetId || null,
createdAt: row.createdAt || 0
}))
}
async _pushGuildSyncToMesh () {
if (!this.guild?.guild) return 0
const bundle = await this._buildGuildSyncBundle()
@@ -1233,6 +1263,27 @@ class PearcordPlatform extends EventEmitter {
payload.receiptRetention
)
}
if (payload.hookFailurePrefs) {
await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestFailurePrefsSlice(
payload.guildId,
payload.hookFailurePrefs
)
}
for (const row of payload.auditRecent || []) {
if (!row?.id || row.guildId !== payload.guildId) continue
const existing = await this.db.get(COLLECTIONS.AUDIT_LOG, { id: row.id })
if (!existing) {
await this.db.insert(COLLECTIONS.AUDIT_LOG, {
id: row.id,
guildId: row.guildId,
actorId: row.actorId,
action: row.action,
targetId: row.targetId || null,
createdAt: row.createdAt || Date.now()
})
}
}
if (payload.automodConfig && this.automod) {
await this.automod.ingestGossip({
...payload.automodConfig,
@@ -2457,6 +2508,11 @@ class PearcordPlatform extends EventEmitter {
if (!this.notifications || !this.identity.user) return
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) return
if (this.guild?.guild?.id) {
await this._initDeliveryReceipts(this.guild.guild.id)
const prefs = await this.deliveryReceipts.getFailurePrefs()
if (prefs.muteNotifications) return
}
const guildName = this.guild?.guild?.name || 'Server'
const hookName = alert?.hookName || receipt?.hookName || 'Hook'
const record = {
@@ -2536,12 +2592,34 @@ class PearcordPlatform extends EventEmitter {
}
const prefs = await this.deliveryReceipts.setRetentionPrefs(patch)
this.emit('automation-receipt-retention', prefs)
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return {
...prefs,
maxAgeDays: Math.round((prefs.maxAgeMs || 0) / (24 * 60 * 60 * 1000))
}
}
async getHookFailurePrefs () {
if (!this.guild?.guild) {
return { muteNotifications: false }
}
await this._initDeliveryReceipts(this.guild.guild.id)
return this.deliveryReceipts.getFailurePrefs()
}
async setHookFailurePrefs (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 automations')
}
await this._initDeliveryReceipts(this.guild.guild.id)
const prefs = await this.deliveryReceipts.setFailurePrefs(partial)
this.emit('hook-failure-prefs', prefs)
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
return prefs
}
listIntegrationWorkerTemplates () {
return listWorkerTemplates()
}
@@ -5688,6 +5766,7 @@ class PearcordPlatform extends EventEmitter {
let automationDelivery = { total: 0, delivered: 0, failed: 0, skipped: 0, recent: [] }
let automationDeliveryMetrics = null
let automationReceiptRetention = { maxCount: 256, maxAgeMs: DEFAULT_MAX_AGE_MS, maxAgeDays: 7 }
let hookFailurePrefs = { muteNotifications: false }
const integrationWorkerTemplates = listWorkerTemplates()
const automationHookMeta = {
actionTypes: ACTION_TYPE_LIST,
@@ -5703,6 +5782,7 @@ class PearcordPlatform extends EventEmitter {
automationDelivery = summarizeReceipts(receiptRows)
automationDeliveryMetrics = await this.getAutomationDeliveryMetrics()
automationReceiptRetention = await this.getAutomationReceiptRetention()
hookFailurePrefs = await this.getHookFailurePrefs()
hookFailureAlerts = await this.listHookFailureAlerts(8)
}
let announcementCrosspostTargets = []
@@ -5857,6 +5937,7 @@ class PearcordPlatform extends EventEmitter {
automationDelivery,
automationDeliveryMetrics,
automationReceiptRetention,
hookFailurePrefs,
hookFailureAlerts,
automationHookMeta,
integrationWorkerTemplates,