'use strict' const permissionsMixin = { _permissionOverwriteGossipKeys: null, _channelOverwriteHealWatermark: null, _guildRolesHealWatermark: null, _initPermissionMixinState () { if (!this._permissionOverwriteGossipKeys) { this._permissionOverwriteGossipKeys = new Set() } }, _shouldGossipChannelOverwrite (row) { this._initPermissionMixinState() if (!row?.guildId || !row?.channelId) return true const hash = `${row.guildId}:${row.channelId}:${row.targetType}:${row.targetId}:${row.allow}:${row.deny}:${row.updatedAt || 0}` if (this._permissionOverwriteGossipKeys.has(hash)) return false this._permissionOverwriteGossipKeys.add(hash) if (this._permissionOverwriteGossipKeys.size > 8192) { const first = this._permissionOverwriteGossipKeys.values().next().value if (first) this._permissionOverwriteGossipKeys.delete(first) } return true }, async _healChannelOverwritesOnPartition (guildId) { const gid = guildId || this.guild?.guild?.id || null const span = this.log.time('permission.heal', { spanKind: 'permission.heal', guildId: gid, slice: 'overwrite' }) try { if (!gid || !this.channelPermissions?.listForGuild) { span.end({ relisted: 0, skipped: true }) return { relisted: 0, skipped: true } } const rows = await this.channelPermissions.listForGuild(gid).catch(() => []) let relisted = 0 for (const row of rows) { if (row.guildId && row.guildId !== gid) continue if (this._shouldGossipChannelOverwrite(row) && this.guild?.gossipChannelOverwriteUpsert) { this.guild.gossipChannelOverwriteUpsert(row) relisted++ } } const watermark = Date.now() this._channelOverwriteHealWatermark = watermark span.end({ relisted, watermark, overwriteCount: rows.length }) return { relisted, watermark, overwriteCount: rows.length } } catch (err) { this.log.error('permission.heal error', { guildId: gid, slice: 'overwrite', error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } }, async _healGuildRolesOnPartition (guildId) { const gid = guildId || this.guild?.guild?.id || null const span = this.log.time('permission.heal', { spanKind: 'permission.heal', guildId: gid, slice: 'roles' }) try { if (!gid || !this.guildRoles?.listRoles) { span.end({ relisted: 0, skipped: true }) return { relisted: 0, skipped: true } } const roles = await this.guildRoles.listRoles(gid).catch(() => []) let relisted = 0 for (const row of roles) { if (row.guildId && row.guildId !== gid) continue if (this.guild?.gossipRoleUpsert) { this.guild.gossipRoleUpsert(row) relisted++ } } const watermark = Date.now() this._guildRolesHealWatermark = watermark span.end({ relisted, watermark, roleCount: roles.length }) return { relisted, watermark, roleCount: roles.length } } catch (err) { this.log.error('permission.heal error', { guildId: gid, slice: 'roles', error: err?.message || String(err) }) span.fail(err) return { relisted: 0, error: err?.message || String(err) } } } } module.exports = { permissionsMixin }