feat(platform): Phase 490 bot span metadata (v0.8.453)

Add bot.token span, bot.verify span, and messageSent on bot.message.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-24 06:45:01 -04:00
co-authored by Cursor
parent 921965f833
commit 8496ff0a97
2 changed files with 74 additions and 33 deletions
+2
View File
@@ -39,6 +39,8 @@ Expose a single `EventEmitter` API the UI drives over IPC (`apps/pearcord/index.
**v0.8.430 (Phase 467):** `voice.leave` span `end` includes `leavingIsStage`; `screen.start` span `end` includes `label`. Bundle: `npm run test:phase467-voice-stage`. See [VOICE.md](../../docs/VOICE.md), [STAGE_CHANNELS.md](../../docs/STAGE_CHANNELS.md). **v0.8.430 (Phase 467):** `voice.leave` span `end` includes `leavingIsStage`; `screen.start` span `end` includes `label`. Bundle: `npm run test:phase467-voice-stage`. See [VOICE.md](../../docs/VOICE.md), [STAGE_CHANNELS.md](../../docs/STAGE_CHANNELS.md).
**v0.8.453 (Phase 490):** `bot.token`/`bot.verify`/`bot.install`/`bot.message` span metadata extend. UI bot compositor + hints. Bundle: `npm run test:phase490-bots`. See [BOTS.md](../../docs/BOTS.md).
**v0.8.452 (Phase 489):** `slash.register` `customCount`; `slash.delete` `deleted`; `slash.invoke` `replyLen`/`commandFound`. UI slash compositor + hints. Bundle: `npm run test:phase489-slash`. See [SLASH_COMMANDS.md](../../docs/SLASH_COMMANDS.md). **v0.8.452 (Phase 489):** `slash.register` `customCount`; `slash.delete` `deleted`; `slash.invoke` `replyLen`/`commandFound`. UI slash compositor + hints. Bundle: `npm run test:phase489-slash`. See [SLASH_COMMANDS.md](../../docs/SLASH_COMMANDS.md).
**v0.8.451 (Phase 488):** `webhook.create` `channelType`/`hasAvatar`; `webhook.execute` `messageSent`. UI webhook compositor split + hints. Bundle: `npm run test:phase488-webhooks`. See [WEBHOOKS.md](../../docs/WEBHOOKS.md). **v0.8.451 (Phase 488):** `webhook.create` `channelType`/`hasAvatar`; `webhook.execute` `messageSent`. UI webhook compositor split + hints. Bundle: `npm run test:phase488-webhooks`. See [WEBHOOKS.md](../../docs/WEBHOOKS.md).
+72 -33
View File
@@ -9968,37 +9968,55 @@ class PearcordPlatform extends EventEmitter {
botId = null, botId = null,
ttlMs = 7 * 86400000 ttlMs = 7 * 86400000
} = {}) { } = {}) {
if (!this.guild?.guild) throw new Error('no guild') const guildId = this.guild?.guild?.id || null
const user = this.identity.user const span = this.log.time('bot.token', { guildId })
if (!user) throw new Error('register first') try {
await this._assertStepUp() if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles() const user = this.identity.user
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) { if (!user) throw new Error('register first')
throw new Error('no permission to create bot install tokens') await this._assertStepUp()
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
throw new Error('no permission to create bot install tokens')
}
if (!this.botLimits) {
this.botLimits = new BotRateLimits({ storagePath: this.storagePath })
await this.botLimits.ready()
}
await this.botLimits.assertBotRegister(this.guild.guild.id)
const { token, payload } = createInstallToken({
keyPair: this.identity.keyPair,
guildId: this.guild.guild.id,
guildName: this.guild.guild.name,
guildTopic: this.guild.guild.topic,
botId: botId || id(),
name,
intents,
permissions,
botPublicKey,
issuedBy: user.id,
ttlMs
})
const bot = payloadToBotRow(payload, this.identity.snapshot().publicKey)
await this._ingestGuildBot(bot)
this.guild.gossipBotInstall(bot)
this.emit('bot', bot)
span.end({
guildId,
botId: bot.id,
botName: String(bot.name || '').slice(0, 32),
permissionMask: Number(bot.permissions) || 0,
tokenIssued: !!token
})
return { token, bot, expiresAt: payload.expiresAt }
} catch (err) {
this.log.error('bot.token error', {
guildId,
error: err?.message || String(err)
})
span.fail(err)
throw err
} }
if (!this.botLimits) {
this.botLimits = new BotRateLimits({ storagePath: this.storagePath })
await this.botLimits.ready()
}
await this.botLimits.assertBotRegister(this.guild.guild.id)
const { token, payload } = createInstallToken({
keyPair: this.identity.keyPair,
guildId: this.guild.guild.id,
guildName: this.guild.guild.name,
guildTopic: this.guild.guild.topic,
botId: botId || id(),
name,
intents,
permissions,
botPublicKey,
issuedBy: user.id,
ttlMs
})
const bot = payloadToBotRow(payload, this.identity.snapshot().publicKey)
await this._ingestGuildBot(bot)
this.guild.gossipBotInstall(bot)
this.emit('bot', bot)
return { token, bot, expiresAt: payload.expiresAt }
} }
async installBotFromToken (tokenInput) { async installBotFromToken (tokenInput) {
@@ -10020,7 +10038,8 @@ class PearcordPlatform extends EventEmitter {
botId: bot.id, botId: bot.id,
guildId: bot.guildId, guildId: bot.guildId,
botName: String(bot.name || '').slice(0, 32), botName: String(bot.name || '').slice(0, 32),
permissionMask: Number(bot.permissions) || 0 permissionMask: Number(bot.permissions) || 0,
installed: true
}) })
return { bot, token: typeof tokenInput === 'string' ? tokenInput : null } return { bot, token: typeof tokenInput === 'string' ? tokenInput : null }
} catch (err) { } catch (err) {
@@ -10034,7 +10053,26 @@ class PearcordPlatform extends EventEmitter {
} }
async verifyBotToken (tokenInput) { async verifyBotToken (tokenInput) {
return verifyInstallToken(tokenInput) const guildId = this.guild?.guild?.id || null
const span = this.log.time('bot.verify', { guildId })
try {
const result = verifyInstallToken(tokenInput, {
expectedGuildId: guildId || undefined
})
span.end({
guildId,
ok: !!result.ok,
botId: result.payload?.botId || null
})
return result
} catch (err) {
this.log.error('bot.verify error', {
guildId,
error: err?.message || String(err)
})
span.fail(err)
throw err
}
} }
async sendBotMessage ({ botId, content, channelId } = {}) { async sendBotMessage ({ botId, content, channelId } = {}) {
@@ -10060,7 +10098,8 @@ class PearcordPlatform extends EventEmitter {
guildId, guildId,
channelId: chId, channelId: chId,
contentLength: String(content ?? '').length, contentLength: String(content ?? '').length,
botId botId,
messageSent: !!msg?.id
}) })
return msg return msg
} catch (err) { } catch (err) {