feat(platform): createForumPost API and forumPosts view snapshot

Create forum posts as threads with starter messages, gossip channel create/
update, audit forum.post.create, and expose sorted forumPosts when a forum
channel is active. Block typing on forum channels.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-21 21:30:12 -04:00
co-authored by Cursor
parent 6dadc18c51
commit 6eca466781
+42 -1
View File
@@ -45,7 +45,13 @@ const {
openDmContent,
dmEncryptionAvailable
} = require('pearcord-crypto')
const { defaultThreadName, groupThreadsByParent, isThreadChannel } = require('pearcord-threads')
const {
defaultThreadName,
groupThreadsByParent,
isThreadChannel,
isForumChannel,
sortForumPosts
} = require('pearcord-threads')
const {
COLLECTIONS, USER_STATUS, PERMISSION, roleHasPermission, canModifyMessage,
resolveMentionedUserIds, extractFirstUrl, id, now
@@ -1930,6 +1936,7 @@ class PearcordPlatform extends EventEmitter {
if (this.mode === 'guild') {
const channels = await this.guild.listChannels()
const ch = channels.find((c) => c.id === this.activeChannelId)
if (isForumChannel(ch)) return null
if (isThreadChannel(ch) && ch.archived) return null
}
const payload = {
@@ -1968,6 +1975,35 @@ class PearcordPlatform extends EventEmitter {
return channel
}
async createForumPost ({ title, content }) {
if (!this.guild?.guild || !this.activeChannelId) throw new Error('no active forum channel')
const forum = await this.db.get(COLLECTIONS.CHANNELS, {
guildId: this.guild.guild.id,
id: this.activeChannelId
})
if (!forum || !isForumChannel(forum)) {
throw new Error('select a forum channel to create a post')
}
const postTitle = String(title || '').trim() || 'Untitled post'
const body = String(content || '').trim()
if (!body) throw new Error('post content required')
const thread = await this.guild.createForumThread({
parentChannelId: forum.id,
name: postTitle
})
this.guild.gossipChannel(thread)
await this.selectChannel(thread.id)
const message = await this.sendMessage(body)
const merged = await this.guild.setThreadRootMessage(thread.id, message.id)
this.guild.gossipChannelUpdate(merged)
await this._audit('forum.post.create', {
guildId: this.guild.guild.id,
targetId: thread.id
})
this.emit('channel', merged)
return { thread: merged, message }
}
async createThread ({ messageId, name }) {
if (!this.guild?.guild || !this.activeChannelId) throw new Error('no active text channel')
const parent = await this.db.get(COLLECTIONS.CHANNELS, {
@@ -2336,6 +2372,10 @@ class PearcordPlatform extends EventEmitter {
threadMessageCounts[ch.id] = rows.length
}
const activeChannel = channels.find((c) => c.id === this.activeChannelId) || null
const forumPosts =
activeChannel && isForumChannel(activeChannel)
? sortForumPosts(threadsByParent[activeChannel.id] || [])
: []
const channelMeta = {}
if (user) {
const alertRows = await this.db.find(COLLECTIONS.MENTION_ALERTS, { userId: user.id })
@@ -2468,6 +2508,7 @@ class PearcordPlatform extends EventEmitter {
threadsByParent,
threadMessageCounts,
threadTypingByParent,
forumPosts,
activeChannel,
needsGuild: this.onboarded && !this.guilds.length && this.mode !== 'dm',
guildUnread,