feat(v0.8.645): Phase 670 sticker/GIF mesh hardening and platform spans
Sticker and GIF rate limits, gossip dedupe, partition heal, pack reorder, announcement sticker fanout, DM sticker notifications, and extended sticker.send/sticker.create span metadata. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Application facade: one `PearcordPlatform` class that wires identity, database, guild mesh, DMs, invites, voice, discovery, bots, and dozens of feature modules for the Pearcord desktop sidecar and companion.
|
Application facade: one `PearcordPlatform` class that wires identity, database, guild mesh, DMs, invites, voice, discovery, bots, and dozens of feature modules for the Pearcord desktop sidecar and companion.
|
||||||
|
|
||||||
|
**Phase 670 (v0.8.645):** Sticker/GIF depth — rate limits, gossip dedupe, partition heal, `reorderStickerPacks`, `packName`/`slotCount` spans, announcement sticker fanout, `_maybeNotifyDmSticker`. Bundle: `npm run test:ci-phase670`.
|
||||||
|
|
||||||
**Phase 669 (v0.8.644):** Reaction depth — `_checkReactionToggleRateLimit`, `_shouldGossipReaction` dedupe, `_healReactionListOnPartition`, `removeMemberReaction` + audit, `reaction.toggle` span `emojiName`, `notifyOnReaction` DM path, `reactionTotalsByMessage` for thread badges, frequent-reaction pref recording. Bundle: `npm run test:ci-phase669`.
|
**Phase 669 (v0.8.644):** Reaction depth — `_checkReactionToggleRateLimit`, `_shouldGossipReaction` dedupe, `_healReactionListOnPartition`, `removeMemberReaction` + audit, `reaction.toggle` span `emojiName`, `notifyOnReaction` DM path, `reactionTotalsByMessage` for thread badges, frequent-reaction pref recording. Bundle: `npm run test:ci-phase669`.
|
||||||
|
|
||||||
**Phase 668 (v0.8.643):** Composer embed preview — `prefetchComposerEmbed`, `clearComposerEmbedPreview`, `composerEmbedPreview` view slice; guild embed resolve rate limit; DM gossip bandwidth cap; URL-hash dedupe; domain block/allowlist; automation embed opt-out; scheduled/webhook embed queue guards. Bundle: `npm run test:ci-phase668`.
|
**Phase 668 (v0.8.643):** Composer embed preview — `prefetchComposerEmbed`, `clearComposerEmbedPreview`, `composerEmbedPreview` view slice; guild embed resolve rate limit; DM gossip bandwidth cap; URL-hash dedupe; domain block/allowlist; automation embed opt-out; scheduled/webhook embed queue guards. Bundle: `npm run test:ci-phase668`.
|
||||||
|
|||||||
@@ -592,6 +592,12 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
this._reactionGossipKeys = new Set()
|
this._reactionGossipKeys = new Set()
|
||||||
/** Per-channel reaction toggle rate limit (Phase 669). */
|
/** Per-channel reaction toggle rate limit (Phase 669). */
|
||||||
this._reactionToggleRateByChannel = new Map()
|
this._reactionToggleRateByChannel = new Map()
|
||||||
|
/** Dedupe sticker gossip fanout (Phase 670). */
|
||||||
|
this._stickerGossipKeys = new Set()
|
||||||
|
/** Per-channel sticker send rate limit (Phase 670). */
|
||||||
|
this._stickerSendRateByChannel = new Map()
|
||||||
|
/** Per-channel GIF attachment send rate limit (Phase 670). */
|
||||||
|
this._gifSendRateByChannel = new Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
_getGuildOpenSlowThresholdMs () {
|
_getGuildOpenSlowThresholdMs () {
|
||||||
@@ -1528,7 +1534,10 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
const reactionHeal = await this._healReactionListOnPartition(gid).catch(() => ({
|
const reactionHeal = await this._healReactionListOnPartition(gid).catch(() => ({
|
||||||
relisted: 0
|
relisted: 0
|
||||||
}))
|
}))
|
||||||
return { voiceApplied, emojiSlots, automodReconciled, voiceNoteHeal, attachmentHeal, embedHeal, reactionHeal }
|
const stickerHeal = await this._healStickerRegistryOnPartition(gid).catch(() => ({
|
||||||
|
relisted: 0
|
||||||
|
}))
|
||||||
|
return { voiceApplied, emojiSlots, automodReconciled, voiceNoteHeal, attachmentHeal, embedHeal, reactionHeal, stickerHeal }
|
||||||
}
|
}
|
||||||
|
|
||||||
async _healReactionListOnPartition (guildId) {
|
async _healReactionListOnPartition (guildId) {
|
||||||
@@ -1549,6 +1558,31 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _healStickerRegistryOnPartition (guildId) {
|
||||||
|
const gid = guildId || this.guild?.guild?.id
|
||||||
|
if (!gid || !this.stickerRegistry) return { relisted: 0, skipped: true }
|
||||||
|
const span = this.log.time('sticker.heal', {
|
||||||
|
spanKind: 'sticker.heal',
|
||||||
|
guildId: gid
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const rows = (await this.stickerRegistry.list().catch(() => [])).filter(
|
||||||
|
(r) => !r.guildId || r.guildId === gid
|
||||||
|
)
|
||||||
|
if (this.guild?.gossipStickerUpsert) {
|
||||||
|
for (const row of rows.slice(0, 64)) {
|
||||||
|
if (this._shouldGossipSticker(row)) this.guild.gossipStickerUpsert(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span.end({ relisted: rows.length, rowCount: rows.length })
|
||||||
|
return { relisted: rows.length, rowCount: rows.length }
|
||||||
|
} catch (err) {
|
||||||
|
this.log.error('sticker.heal error', { error: err?.message || String(err) })
|
||||||
|
span.fail(err)
|
||||||
|
return { relisted: 0, error: err?.message || String(err) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async _healStaleEmbedCache (_guildId) {
|
async _healStaleEmbedCache (_guildId) {
|
||||||
if (!this.embeds?.healStaleEntries) return { healed: 0, skipped: true }
|
if (!this.embeds?.healStaleEntries) return { healed: 0, skipped: true }
|
||||||
const span = this.log.time('embed.heal', {
|
const span = this.log.time('embed.heal', {
|
||||||
@@ -8896,6 +8930,9 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
const reactionHeal = await this._healReactionListOnPartition(guildId).catch(() => ({
|
const reactionHeal = await this._healReactionListOnPartition(guildId).catch(() => ({
|
||||||
relisted: 0
|
relisted: 0
|
||||||
}))
|
}))
|
||||||
|
const stickerHeal = await this._healStickerRegistryOnPartition(guildId).catch(() => ({
|
||||||
|
relisted: 0
|
||||||
|
}))
|
||||||
const settingsDeviceSync = await this._healSettingsMeshDeviceSync(guildId).catch(() => null)
|
const settingsDeviceSync = await this._healSettingsMeshDeviceSync(guildId).catch(() => null)
|
||||||
const req = this.requestGuildSyncFromHost()
|
const req = this.requestGuildSyncFromHost()
|
||||||
const burst = this.requestGuildSyncBurst({ clearPushHalt: false, forcePush: true })
|
const burst = this.requestGuildSyncBurst({ clearPushHalt: false, forcePush: true })
|
||||||
@@ -8920,6 +8957,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
contactsReconciled,
|
contactsReconciled,
|
||||||
extendedHeal,
|
extendedHeal,
|
||||||
reactionHeal,
|
reactionHeal,
|
||||||
|
stickerHeal,
|
||||||
settingsDeviceSync,
|
settingsDeviceSync,
|
||||||
syncRequested: !!req?.requested,
|
syncRequested: !!req?.requested,
|
||||||
syncFanoutAckCount: req?.ackCount ?? 0,
|
syncFanoutAckCount: req?.ackCount ?? 0,
|
||||||
@@ -13205,6 +13243,78 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
.catch(() => null)
|
.catch(() => null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_checkStickerSendRateLimit (channelId) {
|
||||||
|
const key = channelId || this.activeChannelId || 'unknown'
|
||||||
|
const windowMs = 60_000
|
||||||
|
const cap = Math.max(4, Number(process.env.PEARCORD_STICKER_SEND_RATE_PER_MIN) || 30)
|
||||||
|
const now = Date.now()
|
||||||
|
let bucket = this._stickerSendRateByChannel.get(key)
|
||||||
|
if (!bucket || now - bucket.resetAt > windowMs) {
|
||||||
|
bucket = { count: 0, resetAt: now }
|
||||||
|
}
|
||||||
|
if (bucket.count >= cap) return false
|
||||||
|
bucket.count++
|
||||||
|
this._stickerSendRateByChannel.set(key, bucket)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkGifSendRateLimit (channelId) {
|
||||||
|
const key = channelId || this.activeChannelId || 'unknown'
|
||||||
|
const windowMs = 60_000
|
||||||
|
const cap = Math.max(4, Number(process.env.PEARCORD_GIF_SEND_RATE_PER_MIN) || 20)
|
||||||
|
const now = Date.now()
|
||||||
|
let bucket = this._gifSendRateByChannel.get(key)
|
||||||
|
if (!bucket || now - bucket.resetAt > windowMs) {
|
||||||
|
bucket = { count: 0, resetAt: now }
|
||||||
|
}
|
||||||
|
if (bucket.count >= cap) return false
|
||||||
|
bucket.count++
|
||||||
|
this._gifSendRateByChannel.set(key, bucket)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_shouldGossipSticker (row) {
|
||||||
|
if (!row?.guildId || !row?.name) return true
|
||||||
|
const hash = `${row.guildId}:${row.name}:${row.updatedAt || 0}`
|
||||||
|
if (this._stickerGossipKeys.has(hash)) return false
|
||||||
|
this._stickerGossipKeys.add(hash)
|
||||||
|
if (this._stickerGossipKeys.size > 4096) {
|
||||||
|
const first = this._stickerGossipKeys.values().next().value
|
||||||
|
if (first) this._stickerGossipKeys.delete(first)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async _recordFrequentStickerPref (name) {
|
||||||
|
if (!this.userSettings || !name) return
|
||||||
|
const prefs = await this.userSettings.getPrefs().catch(() => null)
|
||||||
|
if (!prefs) return
|
||||||
|
const token = String(name).trim().toLowerCase()
|
||||||
|
const prev = Array.isArray(prefs.frequentStickers) ? prefs.frequentStickers : []
|
||||||
|
const next = [token, ...prev.filter((e) => e !== token)].slice(0, 8)
|
||||||
|
if (next.join('|') === prev.join('|')) return
|
||||||
|
await this.userSettings.setPrefs({ frequentStickers: next }, { gossip: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
async _maybeNotifyDmSticker ({ message, stickerNames }) {
|
||||||
|
if (!this.notifications || !message || !(stickerNames || []).length) return
|
||||||
|
const prefs = await this.notifications.getPrefs().catch(() => null)
|
||||||
|
if (prefs?.notifyOnSticker === false) return
|
||||||
|
const userId = this.identity.user?.id
|
||||||
|
if (!userId || message.authorId === userId) return
|
||||||
|
if (this.mode !== 'dm') return
|
||||||
|
const names = (stickerNames || []).slice(0, 3).map((n) => `:${n}:`).join(' ')
|
||||||
|
await this.notifications
|
||||||
|
.ingest({
|
||||||
|
kind: 'message',
|
||||||
|
channelId: message.channelId,
|
||||||
|
messageId: message.id,
|
||||||
|
authorId: message.authorId,
|
||||||
|
title: `Sticker message ${names}`
|
||||||
|
})
|
||||||
|
.catch(() => null)
|
||||||
|
}
|
||||||
|
|
||||||
/** Re-resolve embed for an existing message (read-only fetch; ignores slowmode send gates). */
|
/** Re-resolve embed for an existing message (read-only fetch; ignores slowmode send gates). */
|
||||||
async retryLinkEmbedForMessage (messageId) {
|
async retryLinkEmbedForMessage (messageId) {
|
||||||
if (!this.embeds || !messageId) throw new Error('embed retry requires messageId')
|
if (!this.embeds || !messageId) throw new Error('embed retry requires messageId')
|
||||||
@@ -17599,25 +17709,39 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
if (this.mode !== 'guild' || !this.guild?.guild) {
|
if (this.mode !== 'guild' || !this.guild?.guild) {
|
||||||
throw new Error('stickers are only supported in guild channels')
|
throw new Error('stickers are only supported in guild channels')
|
||||||
}
|
}
|
||||||
|
if (!this._checkStickerSendRateLimit(this.activeChannelId)) {
|
||||||
|
throw new Error('sticker send rate limited')
|
||||||
|
}
|
||||||
|
const perms = await this._myPermissions(this.activeChannelId).catch(() => null)
|
||||||
|
if (perms && perms.sendMessages === false) {
|
||||||
|
throw new Error('missing permission: send stickers')
|
||||||
|
}
|
||||||
await this._ensureGuildModules()
|
await this._ensureGuildModules()
|
||||||
if (!this.stickerRegistry) await this._initStickerRegistry(this.guild.guild.id)
|
if (!this.stickerRegistry) await this._initStickerRegistry(this.guild.guild.id)
|
||||||
const out = []
|
const out = []
|
||||||
|
const packNames = []
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const row = await this.stickerRegistry.get(name)
|
const row = await this.stickerRegistry.get(name)
|
||||||
if (!row) throw new Error(`unknown sticker: ${name}`)
|
if (!row) throw new Error(`unknown sticker: ${name}`)
|
||||||
out.push(row.name)
|
out.push(row.name)
|
||||||
|
if (row.packName) packNames.push(row.packName)
|
||||||
|
await this._recordFrequentStickerPref(row.name)
|
||||||
}
|
}
|
||||||
span.end({
|
span.end({
|
||||||
resolved: out.length,
|
resolved: out.length,
|
||||||
names: out,
|
names: out,
|
||||||
|
packName: packNames[0] || null,
|
||||||
|
packNames,
|
||||||
spanKind: 'sticker.send',
|
spanKind: 'sticker.send',
|
||||||
activeChannelId: this.activeChannelId,
|
activeChannelId: this.activeChannelId,
|
||||||
|
guildId: this.guild.guild.id,
|
||||||
guildCount: (this.guilds || []).length
|
guildCount: (this.guilds || []).length
|
||||||
})
|
})
|
||||||
return out
|
return out
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log.error('sticker.send error', {
|
this.log.error('sticker.send error', {
|
||||||
channelId: this.activeChannelId || null,
|
channelId: this.activeChannelId || null,
|
||||||
|
guildId: this.guild?.guild?.id || null,
|
||||||
error: err?.message || String(err)
|
error: err?.message || String(err)
|
||||||
})
|
})
|
||||||
span.fail(err)
|
span.fail(err)
|
||||||
@@ -18410,16 +18534,19 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
data: image?.data || null
|
data: image?.data || null
|
||||||
})
|
})
|
||||||
if (att) this.guild.gossipAttachmentMeta(att)
|
if (att) this.guild.gossipAttachmentMeta(att)
|
||||||
this.guild.gossipStickerUpsert(row)
|
if (this._shouldGossipSticker(row)) this.guild.gossipStickerUpsert(row)
|
||||||
await this._audit('sticker.create', {
|
await this._audit('sticker.create', {
|
||||||
guildId: this.guild.guild.id,
|
guildId: this.guild.guild.id,
|
||||||
targetId: row.name,
|
targetId: row.name,
|
||||||
meta: { name: row.name }
|
meta: { name: row.name, packName: row.packName || null }
|
||||||
})
|
})
|
||||||
this.emit('guild-sticker', row)
|
this.emit('guild-sticker', row)
|
||||||
|
const slotCount = (await this.stickerRegistry.list().catch(() => [])).length
|
||||||
span.end({
|
span.end({
|
||||||
name: row.name,
|
name: row.name,
|
||||||
guildId,
|
guildId,
|
||||||
|
packName: row.packName || null,
|
||||||
|
slotCount,
|
||||||
spanKind: 'sticker.create',
|
spanKind: 'sticker.create',
|
||||||
activeChannelId: this.activeChannelId,
|
activeChannelId: this.activeChannelId,
|
||||||
guildCount: (this.guilds || []).length
|
guildCount: (this.guilds || []).length
|
||||||
@@ -18436,6 +18563,29 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async reorderStickerPacks (packNames = []) {
|
||||||
|
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 manage sticker packs')
|
||||||
|
}
|
||||||
|
if (!this.stickerRegistry) await this._initStickerRegistry(this.guild.guild.id)
|
||||||
|
const names = (packNames || []).map((n) => String(n || '').trim().toLowerCase()).filter(Boolean)
|
||||||
|
const packs = await this.stickerRegistry.listPacks()
|
||||||
|
for (let i = 0; i < names.length; i++) {
|
||||||
|
const pack = packs.find((p) => p.name === names[i])
|
||||||
|
if (!pack) continue
|
||||||
|
const row = await this.stickerRegistry.upsertPack({
|
||||||
|
...pack,
|
||||||
|
sortOrder: i
|
||||||
|
})
|
||||||
|
this.guild.gossipStickerPackUpsert(row)
|
||||||
|
}
|
||||||
|
return { reordered: names.length }
|
||||||
|
}
|
||||||
|
|
||||||
async removeGuildSticker (name) {
|
async removeGuildSticker (name) {
|
||||||
const guildId = this.guild?.guild?.id || null
|
const guildId = this.guild?.guild?.id || null
|
||||||
const span = this.log.time('sticker.delete', { name: name || null, guildId })
|
const span = this.log.time('sticker.delete', { name: name || null, guildId })
|
||||||
@@ -20623,8 +20773,17 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
await this._assertNotTimedOut()
|
await this._assertNotTimedOut()
|
||||||
await this._assertSlowmode()
|
await this._assertSlowmode()
|
||||||
const stickerNames = await this._normalizeStickerNames(opts.stickerNames)
|
const stickerNames = await this._normalizeStickerNames(opts.stickerNames)
|
||||||
if (String(content || '').trim()) await this._assertAutomod(content)
|
|
||||||
const attachmentIds = opts.attachmentIds || []
|
const attachmentIds = opts.attachmentIds || []
|
||||||
|
if (attachmentIds.length && this.attachments) {
|
||||||
|
for (const aid of attachmentIds) {
|
||||||
|
const att = await this.attachments.get(aid).catch(() => null)
|
||||||
|
const mt = String(att?.mimeType || '').toLowerCase()
|
||||||
|
if (mt.includes('gif') && !this._checkGifSendRateLimit(this.activeChannelId)) {
|
||||||
|
throw new Error('gif send rate limited')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (String(content || '').trim()) await this._assertAutomod(content)
|
||||||
if (this.attachments && opts.attachmentUiById) {
|
if (this.attachments && opts.attachmentUiById) {
|
||||||
for (const [aid, meta] of Object.entries(opts.attachmentUiById)) {
|
for (const [aid, meta] of Object.entries(opts.attachmentUiById)) {
|
||||||
if (!attachmentIds.includes(aid)) continue
|
if (!attachmentIds.includes(aid)) continue
|
||||||
@@ -20706,10 +20865,13 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.emit('message', msg)
|
this.emit('message', msg)
|
||||||
|
if (stickerNames.length) {
|
||||||
|
await this._maybeNotifyDmSticker({ message: msg, stickerNames }).catch(() => null)
|
||||||
|
}
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
async _insertGuildMessage ({ guildId, channelId, content, authorId, hubMirror }) {
|
async _insertGuildMessage ({ guildId, channelId, content, authorId, hubMirror, stickerNames }) {
|
||||||
if (!this.messages || !guildId || !channelId) throw new Error('invalid message insert')
|
if (!this.messages || !guildId || !channelId) throw new Error('invalid message insert')
|
||||||
const card = hubMirror ? buildHubMirrorCard(hubMirror) : null
|
const card = hubMirror ? buildHubMirrorCard(hubMirror) : null
|
||||||
const body = card ? formatHubMirrorFallback(card) : content
|
const body = card ? formatHubMirrorFallback(card) : content
|
||||||
@@ -20719,7 +20881,8 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
this.messages.setChannel({ channelId, guildId })
|
this.messages.setChannel({ channelId, guildId })
|
||||||
const row = await this.messages.send(body || '', {
|
const row = await this.messages.send(body || '', {
|
||||||
authorId: authorId || this.identity.user?.id,
|
authorId: authorId || this.identity.user?.id,
|
||||||
hubMirrorJson
|
hubMirrorJson,
|
||||||
|
stickerNames: (stickerNames || []).length ? stickerNames : undefined
|
||||||
})
|
})
|
||||||
this.messages.setChannel({ channelId: prevChannel, guildId: prevGuild })
|
this.messages.setChannel({ channelId: prevChannel, guildId: prevGuild })
|
||||||
const enriched = enrichMessageRow(row)
|
const enriched = enrichMessageRow(row)
|
||||||
@@ -20862,6 +21025,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
if (!targets.length) return []
|
if (!targets.length) return []
|
||||||
const channels = await this.guild.listChannels()
|
const channels = await this.guild.listChannels()
|
||||||
const body = formatCrosspostBody(sourceCh.name, sourceMsg.content)
|
const body = formatCrosspostBody(sourceCh.name, sourceMsg.content)
|
||||||
|
const stickerNames = (sourceMsg.stickerNames || []).slice(0, 3)
|
||||||
const out = []
|
const out = []
|
||||||
for (const targetId of targets) {
|
for (const targetId of targets) {
|
||||||
if (targetId === sourceMsg.channelId) continue
|
if (targetId === sourceMsg.channelId) continue
|
||||||
@@ -20872,7 +21036,8 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
guildId: sourceMsg.guildId,
|
guildId: sourceMsg.guildId,
|
||||||
channelId: targetId,
|
channelId: targetId,
|
||||||
content: body,
|
content: body,
|
||||||
authorId: sourceMsg.authorId
|
authorId: sourceMsg.authorId,
|
||||||
|
stickerNames: stickerNames.length ? stickerNames : undefined
|
||||||
})
|
})
|
||||||
out.push(row)
|
out.push(row)
|
||||||
} catch {
|
} catch {
|
||||||
@@ -24058,6 +24223,10 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
const rootStickerNames = {}
|
||||||
|
for (const [msgId, root] of Object.entries(threadRootMessages || {})) {
|
||||||
|
if ((root?.stickerNames || []).length) rootStickerNames[msgId] = root.stickerNames
|
||||||
|
}
|
||||||
const threadsPanel =
|
const threadsPanel =
|
||||||
this.mode === 'guild' && guild
|
this.mode === 'guild' && guild
|
||||||
? buildThreadsPanel({
|
? buildThreadsPanel({
|
||||||
@@ -24068,6 +24237,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
threadLastActivity,
|
threadLastActivity,
|
||||||
rootMessages: threadRootMessages,
|
rootMessages: threadRootMessages,
|
||||||
reactionTotalsByMessage,
|
reactionTotalsByMessage,
|
||||||
|
rootStickerNames,
|
||||||
activeChannelId: this.activeChannelId,
|
activeChannelId: this.activeChannelId,
|
||||||
includeArchived: this._threadsPanelIncludeArchived,
|
includeArchived: this._threadsPanelIncludeArchived,
|
||||||
threadFilter: this._threadsPanelFilter,
|
threadFilter: this._threadsPanelFilter,
|
||||||
@@ -24086,6 +24256,7 @@ class PearcordPlatform extends EventEmitter {
|
|||||||
threadLastActivity,
|
threadLastActivity,
|
||||||
rootMessages: threadRootMessages,
|
rootMessages: threadRootMessages,
|
||||||
reactionTotalsByMessage,
|
reactionTotalsByMessage,
|
||||||
|
rootStickerNames,
|
||||||
activeChannelId: this.activeChannelId,
|
activeChannelId: this.activeChannelId,
|
||||||
includeArchived: false,
|
includeArchived: false,
|
||||||
threadFilter: 'active',
|
threadFilter: 'active',
|
||||||
|
|||||||
Reference in New Issue
Block a user