'use strict' const { COLLECTIONS } = require('pearcord-shared') const INVITE_GOSSIP_WINDOW = 4096 const invitesDiscoveryMixin = { _inviteGossipKeys: null, _invitesHealWatermark: null, _discoveryHealWatermark: null, _inviteGossipKey (payload) { if (!payload?.code) return '' return `invite:${payload.guildId}:${payload.code}:${payload.expiresAt || 0}:${payload.uses || 0}` }, _shouldGossipInvite (payload) { const hash = this._inviteGossipKey(payload) if (!hash) return true if (!this._inviteGossipKeys) this._inviteGossipKeys = new Set() if (this._inviteGossipKeys.has(hash)) return false this._inviteGossipKeys.add(hash) if (this._inviteGossipKeys.size > INVITE_GOSSIP_WINDOW) { const first = this._inviteGossipKeys.values().next().value if (first) this._inviteGossipKeys.delete(first) } return true }, async listGuildInvites (guildId) { const gid = guildId || this.guild?.guild?.id || null if (!gid) return [] const rows = await this.db.find(COLLECTIONS.INVITES, { guildId: gid }) rows.sort((a, b) => (Number(b.expiresAt) || 0) - (Number(a.expiresAt) || 0)) return rows }, async _healInvitesOnPartition (guildId) { const gid = guildId || this.guild?.guild?.id || null const span = this.log.time('invite.heal', { spanKind: 'invite.heal', guildId: gid }) try { if (!gid) { span.end({ relisted: 0, skipped: true }) return { relisted: 0, skipped: true } } const rows = await this.db.find(COLLECTIONS.INVITES, { guildId: gid }) const now = Date.now() let pruned = 0 for (const row of rows) { if (row.expiresAt && row.expiresAt < now - 7 * 24 * 60 * 60 * 1000) { await this.db.delete(COLLECTIONS.INVITES, { code: row.code }).catch(() => {}) pruned += 1 } } const relisted = rows.length - pruned const watermark = Date.now() this._invitesHealWatermark = watermark span.end({ relisted, pruned, watermark, inviteCount: relisted }) return { relisted, pruned, watermark } } catch (err) { this.log.error('invite.heal error', { guildId: gid, error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } }, async _healDiscoveryListingsOnPartition (guildId) { const span = this.log.time('discovery.heal', { spanKind: 'discovery.heal', guildId: guildId || null }) try { if (!this.discovery) { span.end({ relisted: 0, skipped: true }) return { relisted: 0, skipped: true } } const rows = await this.discovery.listPublicListings({ prune: true }) const watermark = Date.now() this._discoveryHealWatermark = watermark span.end({ relisted: rows.length, watermark, discoveryListingCount: rows.length }) return { relisted: rows.length, watermark } } catch (err) { this.log.error('discovery.heal error', { guildId, error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } } } module.exports = { invitesDiscoveryMixin, INVITE_GOSSIP_WINDOW }