feat(platform): slash.invoke and bot spans with guildId (v0.8.424)
Add guildId/channelId to slash.invoke; bot.message and bot.install spans with structured errors for Phase 461 observability. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -35,6 +35,8 @@ Expose a single `EventEmitter` API the UI drives over IPC (`apps/pearcord/index.
|
||||
| `onboarded`, `identity`, `storagePath`, `db`, `dbPath` | Session state |
|
||||
| `listGuilds()`, `loadGuild(guildId)`, `createGuild({ name, publicListing? })`, `joinInvite(code)` | Guild session |
|
||||
|
||||
**v0.8.424 (Phase 461):** `slash.invoke` span includes `guildId`/`channelId`; `bot.message` + `bot.install` spans with `guildId`. Bundle: `npm run test:phase461-slash-bots`. See [SLASH_COMMANDS.md](../../docs/SLASH_COMMANDS.md), [BOTS.md](../../docs/BOTS.md).
|
||||
|
||||
**v0.8.423 (Phase 460):** `embed.resolve` span uses active `guildId`/`channelId` fallback; `attachment.stage`/`attachment.read` re-verified. Bundle: `npm run test:phase460-attachments-embeds`. See [ATTACHMENTS.md](../../docs/ATTACHMENTS.md), [EMBEDS.md](../../docs/EMBEDS.md).
|
||||
|
||||
**v0.8.422 (Phase 459):** `emoji.upsert` + `sticker.create` spans include `guildId` + `span.fail`; `reaction.toggle` error log includes `guildId`/`channelId` (re-verified). Bundle: `npm run test:phase459-reactions-emojis`. See [REACTIONS.md](../../docs/REACTIONS.md), [EMOJI.md](../../docs/EMOJI.md).
|
||||
|
||||
@@ -9754,18 +9754,30 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
|
||||
async installBotFromToken (tokenInput) {
|
||||
const result = verifyInstallToken(tokenInput, {
|
||||
expectedGuildId: this.guild?.guild?.id || undefined
|
||||
})
|
||||
if (!result.ok) throw new Error(result.error || 'invalid bot token')
|
||||
const bot = payloadToBotRow(result.payload, result.issuerPublicKey)
|
||||
if (!this.guild?.guild || this.guild.guild.id !== bot.guildId) {
|
||||
throw new Error('load the target server before installing a bot')
|
||||
const guildId = this.guild?.guild?.id || null
|
||||
const span = this.log.time('bot.install', { guildId })
|
||||
try {
|
||||
const result = verifyInstallToken(tokenInput, {
|
||||
expectedGuildId: guildId || undefined
|
||||
})
|
||||
if (!result.ok) throw new Error(result.error || 'invalid bot token')
|
||||
const bot = payloadToBotRow(result.payload, result.issuerPublicKey)
|
||||
if (!this.guild?.guild || this.guild.guild.id !== bot.guildId) {
|
||||
throw new Error('load the target server before installing a bot')
|
||||
}
|
||||
await this._ingestGuildBot(bot)
|
||||
if (this.guild) this.guild.gossipBotInstall(bot)
|
||||
this.emit('bot', bot)
|
||||
span.end({ botId: bot.id, guildId: bot.guildId })
|
||||
return { bot, token: typeof tokenInput === 'string' ? tokenInput : null }
|
||||
} catch (err) {
|
||||
this.log.error('bot.install error', {
|
||||
guildId,
|
||||
error: err?.message || String(err)
|
||||
})
|
||||
span.fail(err)
|
||||
throw err
|
||||
}
|
||||
await this._ingestGuildBot(bot)
|
||||
if (this.guild) this.guild.gossipBotInstall(bot)
|
||||
this.emit('bot', bot)
|
||||
return { bot, token: typeof tokenInput === 'string' ? tokenInput : null }
|
||||
}
|
||||
|
||||
async verifyBotToken (tokenInput) {
|
||||
@@ -9773,21 +9785,35 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
|
||||
async sendBotMessage ({ botId, content, channelId } = {}) {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const bot = await this.db.get(COLLECTIONS.GUILD_BOTS, { id: botId })
|
||||
if (!bot) throw new Error('bot not found')
|
||||
if (!botHasPermission(bot.permissions, BOT_PERMISSION.SEND_MESSAGES)) {
|
||||
throw new Error('bot lacks SEND_MESSAGES permission')
|
||||
}
|
||||
const guildId = this.guild?.guild?.id || null
|
||||
const chId = channelId || this.activeChannelId
|
||||
if (!chId) throw new Error('no channel')
|
||||
const prevChannel = this.activeChannelId
|
||||
if (chId !== this.activeChannelId) await this.selectChannel(chId)
|
||||
const msg = await this.messages.send(String(content ?? ''), { authorId: bot.id })
|
||||
if (prevChannel && prevChannel !== chId) await this.selectChannel(prevChannel)
|
||||
if (this.guild) this.guild.gossipMessage(msg)
|
||||
await this._fanoutBotEvent(BOT_EVENTS.MESSAGE_CREATE, { message: msg })
|
||||
return msg
|
||||
const span = this.log.time('bot.message', { botId, guildId, channelId: chId })
|
||||
try {
|
||||
if (!this.guild?.guild) throw new Error('no guild')
|
||||
const bot = await this.db.get(COLLECTIONS.GUILD_BOTS, { id: botId })
|
||||
if (!bot) throw new Error('bot not found')
|
||||
if (!botHasPermission(bot.permissions, BOT_PERMISSION.SEND_MESSAGES)) {
|
||||
throw new Error('bot lacks SEND_MESSAGES permission')
|
||||
}
|
||||
if (!chId) throw new Error('no channel')
|
||||
const prevChannel = this.activeChannelId
|
||||
if (chId !== this.activeChannelId) await this.selectChannel(chId)
|
||||
const msg = await this.messages.send(String(content ?? ''), { authorId: bot.id })
|
||||
if (prevChannel && prevChannel !== chId) await this.selectChannel(prevChannel)
|
||||
if (this.guild) this.guild.gossipMessage(msg)
|
||||
await this._fanoutBotEvent(BOT_EVENTS.MESSAGE_CREATE, { message: msg })
|
||||
span.end({ messageId: msg?.id, guildId, channelId: chId })
|
||||
return msg
|
||||
} catch (err) {
|
||||
this.log.error('bot.message error', {
|
||||
botId,
|
||||
guildId,
|
||||
channelId: chId,
|
||||
error: err?.message || String(err)
|
||||
})
|
||||
span.fail(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async listBots () {
|
||||
@@ -9957,7 +9983,13 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
|
||||
async executeSlashCommand (content, opts = {}) {
|
||||
const span = this.log.time('slash.invoke', { content: String(content || '').slice(0, 40) })
|
||||
const guildId = this.mode === 'dm' ? DM_GUILD_ID : this.guild?.guild?.id || null
|
||||
const channelId = this.activeChannelId
|
||||
const span = this.log.time('slash.invoke', {
|
||||
content: String(content || '').slice(0, 40),
|
||||
guildId,
|
||||
channelId
|
||||
})
|
||||
try {
|
||||
if (!this.slashCommands || !this.messages) throw new Error('no channel')
|
||||
const inv = parseSlashInvocation(content)
|
||||
@@ -9993,11 +10025,13 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
const reply = `${header}\n${body}`
|
||||
const msg = await this._sendPlainMessage(reply, opts)
|
||||
span.end({ name: inv.name })
|
||||
span.end({ name: inv.name, guildId, channelId })
|
||||
return msg
|
||||
} catch (err) {
|
||||
this.log.error('slash.invoke error', {
|
||||
content: String(content || '').slice(0, 40),
|
||||
guildId,
|
||||
channelId,
|
||||
error: err?.message || String(err)
|
||||
})
|
||||
span.fail(err)
|
||||
|
||||
Reference in New Issue
Block a user