feat(automations): export/import manifest and GUILD_SYNC hooks slice
Wire pearcord-automation-export: list/upsert/delete hooks, exportAutomationManifest, importAutomationManifest, mesh gossip ingest, and automationHooks in view(). Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -24,6 +24,11 @@ const {
|
||||
APP_EVENTS,
|
||||
APP_EVENT_CATEGORIES
|
||||
} = require('pearcord-app-events')
|
||||
const {
|
||||
AutomationManifestStore,
|
||||
buildAutomationManifest,
|
||||
validateAutomationManifest
|
||||
} = require('pearcord-automation-export')
|
||||
const { INTENTS } = require('pearcord-bot-sdk')
|
||||
const { BotRateLimits } = require('pearcord-bot-limits')
|
||||
const { PearcordStepUp } = require('pearcord-step-up')
|
||||
@@ -216,6 +221,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
this._recentSoundPlays = []
|
||||
this.botEvents = null
|
||||
this.appEvents = null
|
||||
this.automationExport = null
|
||||
this.discovery = null
|
||||
this.contacts = null
|
||||
this.botLimits = null
|
||||
@@ -1107,6 +1113,14 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (this.channelPermissions) {
|
||||
permBundle = await this.channelPermissions.exportSyncBundle(guild.id)
|
||||
}
|
||||
let automationSlice = { hooks: [] }
|
||||
await this._initAutomationExport(guild.id)
|
||||
if (this.automationExport) {
|
||||
automationSlice = await this.automationExport.exportSyncSlice()
|
||||
}
|
||||
let automodConfig = null
|
||||
await this._ensureGuildModules()
|
||||
if (this.automod) automodConfig = await this.automod.getConfig()
|
||||
if (guild.iconHash && this.attachments) {
|
||||
const iconRow = await this.attachments.get(guild.iconHash).catch(() => null)
|
||||
if (iconRow && !attachments.some((a) => a.id === iconRow.id)) {
|
||||
@@ -1128,7 +1142,18 @@ class PearcordPlatform extends EventEmitter {
|
||||
guildRoles: roleBundle.guildRoles,
|
||||
memberRoleLinks: roleBundle.memberRoleLinks,
|
||||
channelOverwrites: permBundle.channelOverwrites,
|
||||
channelSettings
|
||||
channelSettings,
|
||||
automationHooks: automationSlice.hooks,
|
||||
automodConfig: automodConfig
|
||||
? {
|
||||
enabled: !!automodConfig.enabled,
|
||||
blockedKeywords: automodConfig.blockedKeywords || [],
|
||||
maxMentions: automodConfig.maxMentions ?? 5,
|
||||
spamLimit: automodConfig.spamLimit ?? 5,
|
||||
spamWindowSeconds: automodConfig.spamWindowSeconds ?? 5,
|
||||
blockInvites: automodConfig.blockInvites !== false
|
||||
}
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,6 +1192,18 @@ class PearcordPlatform extends EventEmitter {
|
||||
await this.channelPermissions.ingestSyncBundle(payload.guildId, {
|
||||
channelOverwrites: payload.channelOverwrites
|
||||
})
|
||||
await this._initAutomationExport(payload.guildId)
|
||||
if (this.automationExport && payload.automationHooks?.length) {
|
||||
await this.automationExport.ingestSyncSlice(payload.guildId, {
|
||||
hooks: payload.automationHooks
|
||||
})
|
||||
}
|
||||
if (payload.automodConfig && this.automod) {
|
||||
await this.automod.ingestGossip({
|
||||
...payload.automodConfig,
|
||||
guildId: payload.guildId
|
||||
})
|
||||
}
|
||||
}
|
||||
for (const mem of payload.members || []) {
|
||||
if (!mem?.guildId || !mem?.userId) continue
|
||||
@@ -1404,6 +1441,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
guildInstance.on('automod-config', (p) => {
|
||||
this._onAutomodConfigGossip(p)
|
||||
})
|
||||
guildInstance.on('automation-manifest', (p) => {
|
||||
this._onAutomationManifestGossip(p).catch(() => {})
|
||||
})
|
||||
guildInstance.on('screen-share', (p) => {
|
||||
this.screenShare?.ingestGossip(p).catch(() => {})
|
||||
this.emit('screen-share', p)
|
||||
@@ -3734,9 +3774,130 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
const guildId = this.guild?.guild?.id
|
||||
if (guildId && this.automod) this.automod.setGuild(guildId)
|
||||
await this._initAutomationExport(guildId)
|
||||
return { forum: this.forum, stage: this.stage, automod: this.automod }
|
||||
}
|
||||
|
||||
async _initAutomationExport (guildId) {
|
||||
if (!guildId) return
|
||||
if (!this.automationExport) {
|
||||
this.automationExport = new AutomationManifestStore({
|
||||
storagePath: this.storagePath
|
||||
})
|
||||
await this.automationExport.ready()
|
||||
}
|
||||
this.automationExport.setGuild(guildId)
|
||||
}
|
||||
|
||||
async listAutomationHooks () {
|
||||
if (!this.guild?.guild) return []
|
||||
await this._initAutomationExport(this.guild.guild.id)
|
||||
return this.automationExport.listHooks()
|
||||
}
|
||||
|
||||
async upsertAutomationHook (hook = {}) {
|
||||
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._initAutomationExport(this.guild.guild.id)
|
||||
const row = await this.automationExport.upsertHook(hook)
|
||||
this.emit('automation-hook', row)
|
||||
return row
|
||||
}
|
||||
|
||||
async deleteAutomationHook (hookId) {
|
||||
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._initAutomationExport(this.guild.guild.id)
|
||||
const ok = await this.automationExport.deleteHook(hookId)
|
||||
if (ok) this.emit('automation-hook-delete', { hookId, guildId: this.guild.guild.id })
|
||||
return ok
|
||||
}
|
||||
|
||||
async exportAutomationManifest () {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const user = this.identity.user
|
||||
if (!user) throw new Error('register first')
|
||||
const roles = await this._memberRoles()
|
||||
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
|
||||
throw new Error('no permission to export automations')
|
||||
}
|
||||
await this._ensureGuildModules()
|
||||
await this._initAutomationExport(this.guild.guild.id)
|
||||
const guild = this.guild.guild
|
||||
const automodConfig = await this.automod.getConfig()
|
||||
const slashCommands = this.slashCommands ? await this.slashCommands.list() : []
|
||||
const bots = await this.db.find(COLLECTIONS.GUILD_BOTS, { guildId: guild.id })
|
||||
const hooks = await this.automationExport.listHooks()
|
||||
return buildAutomationManifest({
|
||||
guildId: guild.id,
|
||||
guildName: guild.name,
|
||||
exportedBy: user.id,
|
||||
automodConfig,
|
||||
slashCommands,
|
||||
bots,
|
||||
hooks,
|
||||
appEventCategories: APP_EVENT_CATEGORIES.ALL
|
||||
})
|
||||
}
|
||||
|
||||
async importAutomationManifest (manifest, opts = {}) {
|
||||
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 import automations')
|
||||
}
|
||||
const check = validateAutomationManifest(manifest, {
|
||||
expectedGuildId: this.guild.guild.id
|
||||
})
|
||||
if (!check.ok) throw new Error(check.errors.join('; '))
|
||||
await this._ensureGuildModules()
|
||||
await this._initAutomationExport(this.guild.guild.id)
|
||||
const merge = opts.merge !== false
|
||||
if (manifest.automod) {
|
||||
await this.setAutomodConfig(manifest.automod)
|
||||
}
|
||||
const hooks = await this.automationExport.replaceHooks(manifest.hooks || [], { merge })
|
||||
const summary = {
|
||||
guildId: this.guild.guild.id,
|
||||
hooksImported: hooks.length,
|
||||
automodApplied: !!manifest.automod,
|
||||
importedAt: Date.now()
|
||||
}
|
||||
if (this.guild.gossipAutomationManifest) {
|
||||
this.guild.gossipAutomationManifest({
|
||||
guildId: this.guild.guild.id,
|
||||
manifest,
|
||||
summary
|
||||
})
|
||||
}
|
||||
this.emit('automation-manifest-import', summary)
|
||||
return { manifest, summary, hooks }
|
||||
}
|
||||
|
||||
async _onAutomationManifestGossip (payload) {
|
||||
if (!payload?.guildId || !payload?.manifest) return
|
||||
if (this.guild?.guild?.id !== payload.guildId) return
|
||||
const check = validateAutomationManifest(payload.manifest, {
|
||||
expectedGuildId: payload.guildId
|
||||
})
|
||||
if (!check.ok) return
|
||||
await this._initAutomationExport(payload.guildId)
|
||||
await this.automationExport.replaceHooks(payload.manifest.hooks || [], { merge: true })
|
||||
if (payload.manifest.automod) {
|
||||
await this._onAutomodConfigGossip({
|
||||
...payload.manifest.automod,
|
||||
guildId: payload.guildId
|
||||
})
|
||||
}
|
||||
this.emit('automation-manifest', payload)
|
||||
}
|
||||
|
||||
async _onAutomodConfigGossip (payload) {
|
||||
if (!payload?.guildId) return
|
||||
await this._ensureGuildModules()
|
||||
@@ -5214,6 +5375,11 @@ class PearcordPlatform extends EventEmitter {
|
||||
this.automod.setGuild(guild.id)
|
||||
automodConfig = await this.automod.getConfig()
|
||||
}
|
||||
let automationHooks = []
|
||||
if (guild) {
|
||||
await this._initAutomationExport(guild.id)
|
||||
automationHooks = await this.automationExport.listHooks()
|
||||
}
|
||||
let announcementCrosspostTargets = []
|
||||
let announcementCrosspostByChannel = {}
|
||||
let announcementFollowing = false
|
||||
@@ -5362,6 +5528,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
myStageRole,
|
||||
stagePendingRequests,
|
||||
automodConfig,
|
||||
automationHooks,
|
||||
announcementCrosspostTargets,
|
||||
announcementCrosspostByChannel,
|
||||
announcementFollowing,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pearcord-announcements": "file:../pearcord-announcements",
|
||||
"pearcord-app-events": "file:../pearcord-app-events",
|
||||
"pearcord-attachments": "file:../pearcord-attachments",
|
||||
"pearcord-automation-export": "file:../pearcord-automation-export",
|
||||
"pearcord-automod": "file:../pearcord-automod",
|
||||
"pearcord-boosts": "file:../pearcord-boosts",
|
||||
"pearcord-bot-auth": "file:../pearcord-bot-auth",
|
||||
|
||||
Reference in New Issue
Block a user