refactor(platform): organize mixins into category directories
Move 136 prototype mixins under mixins/domain, json-export, and runtime/* so the package root stays navigable. Manifest assign order is unchanged; apply-platform-mixins and runtime registry paths updated. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
'use strict'
|
||||
|
||||
const { isAnnouncementChannel } = require('pearcord-threads')
|
||||
|
||||
const announcementsMixin = {
|
||||
_announcementFollowHealWatermark: null,
|
||||
_announcementCrosspostHealWatermark: null,
|
||||
_announcementCrosspostGossipKeys: null,
|
||||
_announcementFollowGossipKeys: null,
|
||||
_crosspostRateByGuild: null,
|
||||
_lastCrosspostPublish: null,
|
||||
|
||||
_initAnnouncementMixinState () {
|
||||
if (!this._announcementCrosspostGossipKeys) {
|
||||
this._announcementCrosspostGossipKeys = new Set()
|
||||
}
|
||||
if (!this._announcementFollowGossipKeys) {
|
||||
this._announcementFollowGossipKeys = new Set()
|
||||
}
|
||||
if (!this._crosspostRateByGuild) {
|
||||
this._crosspostRateByGuild = new Map()
|
||||
}
|
||||
},
|
||||
|
||||
_shouldGossipAnnouncementCrosspost (row) {
|
||||
this._initAnnouncementMixinState()
|
||||
if (!row?.guildId || !row?.sourceChannelId) return true
|
||||
const ids = (row.targetChannelIds || []).join(',')
|
||||
const hash = `${row.guildId}:${row.sourceChannelId}:${ids}:${row.updatedAt || 0}`
|
||||
if (this._announcementCrosspostGossipKeys.has(hash)) return false
|
||||
this._announcementCrosspostGossipKeys.add(hash)
|
||||
if (this._announcementCrosspostGossipKeys.size > 4096) {
|
||||
const first = this._announcementCrosspostGossipKeys.values().next().value
|
||||
if (first) this._announcementCrosspostGossipKeys.delete(first)
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
_shouldGossipAnnouncementFollow (row) {
|
||||
this._initAnnouncementMixinState()
|
||||
if (!row?.guildId || !row?.channelId || !row?.userId) return true
|
||||
const hash = `${row.guildId}:${row.channelId}:${row.userId}:${row.active ? '1' : '0'}:${row.updatedAt || 0}`
|
||||
if (this._announcementFollowGossipKeys.has(hash)) return false
|
||||
this._announcementFollowGossipKeys.add(hash)
|
||||
if (this._announcementFollowGossipKeys.size > 8192) {
|
||||
const first = this._announcementFollowGossipKeys.values().next().value
|
||||
if (first) this._announcementFollowGossipKeys.delete(first)
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
_checkCrosspostPublishRateLimit (guildId) {
|
||||
this._initAnnouncementMixinState()
|
||||
const gid = guildId || this.guild?.guild?.id
|
||||
if (!gid) return true
|
||||
const cap = 12
|
||||
const windowMs = 60_000
|
||||
const now = Date.now()
|
||||
let bucket = this._crosspostRateByGuild.get(gid)
|
||||
if (!bucket || now >= bucket.resetAt) {
|
||||
bucket = { count: 0, resetAt: now + windowMs }
|
||||
}
|
||||
if (bucket.count >= cap) return false
|
||||
bucket.count++
|
||||
this._crosspostRateByGuild.set(gid, bucket)
|
||||
return true
|
||||
},
|
||||
|
||||
async _healAnnouncementCrosspostOnPartition (guildId) {
|
||||
const gid = guildId || this.guild?.guild?.id || null
|
||||
const span = this.log.time('announcement.heal', {
|
||||
spanKind: 'announcement.heal',
|
||||
guildId: gid,
|
||||
slice: 'crosspost'
|
||||
})
|
||||
try {
|
||||
if (!gid || !this.guild?.listChannels || !this.announcements) {
|
||||
span.end({ relisted: 0, skipped: true })
|
||||
return { relisted: 0, skipped: true }
|
||||
}
|
||||
const channels = await this.guild.listChannels().catch(() => [])
|
||||
let relisted = 0
|
||||
for (const ch of channels) {
|
||||
if (!isAnnouncementChannel(ch) || ch.guildId && ch.guildId !== gid) continue
|
||||
const targets = await this.announcements.getCrosspostTargets(gid, ch.id)
|
||||
const row = {
|
||||
guildId: gid,
|
||||
sourceChannelId: ch.id,
|
||||
targetChannelIds: targets,
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
if (this._shouldGossipAnnouncementCrosspost(row) && this.guild?.gossipAnnouncementCrosspost) {
|
||||
this.guild.gossipAnnouncementCrosspost(row)
|
||||
relisted++
|
||||
}
|
||||
}
|
||||
const watermark = Date.now()
|
||||
this._announcementCrosspostHealWatermark = watermark
|
||||
span.end({ relisted, watermark })
|
||||
return { relisted, watermark }
|
||||
} catch (err) {
|
||||
this.log.error('announcement.heal error', {
|
||||
guildId: gid,
|
||||
slice: 'crosspost',
|
||||
error: err?.message || String(err)
|
||||
})
|
||||
span.fail(err)
|
||||
return { relisted: 0, error: err?.message || String(err) }
|
||||
}
|
||||
},
|
||||
|
||||
async _healAnnouncementFollowsOnPartition (guildId) {
|
||||
const gid = guildId || this.guild?.guild?.id || null
|
||||
const span = this.log.time('announcement.heal', {
|
||||
spanKind: 'announcement.heal',
|
||||
guildId: gid,
|
||||
slice: 'follow'
|
||||
})
|
||||
try {
|
||||
if (!gid || !this.announcements?.store) {
|
||||
span.end({ relisted: 0, skipped: true })
|
||||
return { relisted: 0, skipped: true }
|
||||
}
|
||||
const userId = this.identity?.user?.id
|
||||
if (!userId) {
|
||||
span.end({ relisted: 0, skipped: true })
|
||||
return { relisted: 0, skipped: true }
|
||||
}
|
||||
const follows = await this.announcements.listMyFollows(userId, gid)
|
||||
let relisted = 0
|
||||
for (const row of follows) {
|
||||
if (row.guildId !== gid) continue
|
||||
if (this._shouldGossipAnnouncementFollow(row) && this.guild?.gossipAnnouncementFollow) {
|
||||
this.guild.gossipAnnouncementFollow(row)
|
||||
relisted++
|
||||
}
|
||||
}
|
||||
const watermark = Date.now()
|
||||
this._announcementFollowHealWatermark = watermark
|
||||
span.end({ relisted, watermark, followCount: follows.length })
|
||||
return { relisted, watermark, followCount: follows.length }
|
||||
} catch (err) {
|
||||
this.log.error('announcement.heal error', {
|
||||
guildId: gid,
|
||||
slice: 'follow',
|
||||
error: err?.message || String(err)
|
||||
})
|
||||
span.fail(err)
|
||||
return { relisted: 0, error: err?.message || String(err) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { announcementsMixin }
|
||||
Reference in New Issue
Block a user