feat(platform): contributeGuildBoost for members + myBoostSlots view

Any guild member may contribute boosts via personal slots (not Manage Guild).
Extract _gossipGuildBoostState for shared gossip after add/contribute. Expose
myBoostSlots on view for server settings UI (used, remaining, canContribute).

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-21 23:08:50 -04:00
co-authored by Cursor
parent c5f971a756
commit 45571368a3
+57 -13
View File
@@ -31,7 +31,7 @@ const {
PearcordAnnouncements,
formatCrosspostBody
} = require('pearcord-announcements')
const { PearcordBoosts } = require('pearcord-boosts')
const { PearcordBoosts, MAX_BOOSTS, MAX_SLOTS_PER_USER } = require('pearcord-boosts')
const { PearcordStickerRegistry } = require('pearcord-stickers')
const {
PearcordAnnouncementHub,
@@ -2689,18 +2689,7 @@ class PearcordPlatform extends EventEmitter {
return row
}
async addGuildBoost (amount = 1) {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
throw new Error('no permission to boost guild')
}
await this._ensureGuildModules()
const user = this.identity?.user
const attribution = user
? { userId: user.id, username: user.username, displayName: user.displayName }
: null
const row = await this.boosts.addBoost(this.guild.guild.id, amount, attribution)
async _gossipGuildBoostState (row, attribution) {
this.guild.gossipGuildBoost({
guildId: row.guildId,
boostCount: row.boostCount,
@@ -2718,6 +2707,48 @@ class PearcordPlatform extends EventEmitter {
updatedAt: Date.now()
})
}
}
async contributeGuildBoost (amount = 1) {
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 (!roles) throw new Error('not a guild member')
await this._ensureGuildModules()
const attribution = {
userId: user.id,
username: user.username,
displayName: user.displayName
}
const row = await this.boosts.contributeBoost(
this.guild.guild.id,
attribution,
amount
)
this._gossipGuildBoostState(row, attribution)
await this._audit('guild.boost.contribute', {
guildId: row.guildId,
targetId: String(row.boostCount),
meta: { userId: attribution.userId, slots: (row.ledger || []).find((e) => e.userId === attribution.userId)?.slots }
})
this.emit('guild-boost', row)
return row
}
async addGuildBoost (amount = 1) {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
throw new Error('no permission to boost guild')
}
await this._ensureGuildModules()
const user = this.identity?.user
const attribution = user
? { userId: user.id, username: user.username, displayName: user.displayName }
: null
const row = await this.boosts.addBoost(this.guild.guild.id, amount, attribution)
this._gossipGuildBoostState(row, attribution)
await this._audit('guild.boost', {
guildId: row.guildId,
targetId: String(row.boostCount),
@@ -3517,8 +3548,20 @@ class PearcordPlatform extends EventEmitter {
remoteAnnouncementHubSubs = await this.announcementHub.listSubscriptions(guild.id)
}
let guildBoost = null
let myBoostSlots = null
if (guild && this.boosts) {
guildBoost = await this.boosts.get(guild.id)
if (user?.id) {
const used = await this.boosts.getUserSlots(guild.id, user.id)
const remaining = Math.max(0, MAX_SLOTS_PER_USER - used)
const guildFull = (guildBoost.boostCount || 0) >= MAX_BOOSTS
myBoostSlots = {
used,
max: MAX_SLOTS_PER_USER,
remaining,
canContribute: remaining > 0 && !guildFull
}
}
}
return {
onboarded: this.onboarded,
@@ -3569,6 +3612,7 @@ class PearcordPlatform extends EventEmitter {
announcementHubPublishers,
remoteAnnouncementHubSubs,
guildBoost,
myBoostSlots,
myVoiceChannelId,
voiceMedia: this.voiceMedia?.snapshot() || null,
screenShare: this.screenShare?.snapshot() || null,