feat(platform): forum archive search and show-archived toggle (v0.8.20)

Add searchForumPosts, setForumIncludeArchived, forum search scope in
_resolveSearchResults (fix channel db.get with guildId), guild search thread
titles, and pearcord-forum-search dependency.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 00:13:51 -04:00
co-authored by Cursor
parent 24ba5b1f45
commit 7635295cfb
2 changed files with 85 additions and 10 deletions
+84 -10
View File
@@ -75,11 +75,13 @@ const {
const { const {
defaultThreadName, defaultThreadName,
groupThreadsByParent, groupThreadsByParent,
listForumPostsForParent,
isThreadChannel, isThreadChannel,
isForumChannel, isForumChannel,
isAnnouncementChannel, isAnnouncementChannel,
sortForumPosts sortForumPosts
} = require('pearcord-threads') } = require('pearcord-threads')
const { forumPostHaystack } = require('pearcord-forum-search')
const { const {
classifyMessageLayout, classifyMessageLayout,
LAYOUT LAYOUT
@@ -321,6 +323,7 @@ class PearcordPlatform extends EventEmitter {
this.boosts = new PearcordBoosts({ storagePath: this.storagePath }) this.boosts = new PearcordBoosts({ storagePath: this.storagePath })
await this.boosts.ready() await this.boosts.ready()
this._forumTagFilter = null this._forumTagFilter = null
this._forumIncludeArchived = false
this.guilds = await this.listGuilds() this.guilds = await this.listGuilds()
void this._finishSessionStartup() void this._finishSessionStartup()
this.emit('user', user) this.emit('user', user)
@@ -825,10 +828,32 @@ class PearcordPlatform extends EventEmitter {
setSearch ({ query, scope }) { setSearch ({ query, scope }) {
this._searchQuery = String(query || '').trim() this._searchQuery = String(query || '').trim()
this._searchScope = scope === 'guild' ? 'guild' : 'channel' if (scope === 'guild' || scope === 'forum') {
this._searchScope = scope
} else {
this._searchScope = 'channel'
}
return { query: this._searchQuery, scope: this._searchScope } return { query: this._searchQuery, scope: this._searchScope }
} }
async _resolveSearchResults () {
const q = this._searchQuery
if (!q || !this.guild?.guild?.id) return []
if (this._searchScope === 'forum') {
const guildId = this.guild.guild.id
const ch = await this.db.get(COLLECTIONS.CHANNELS, {
guildId,
id: this.activeChannelId
})
if (!ch || !isForumChannel(ch)) return []
return this.searchForumPosts(guildId, ch.id, q)
}
if (this._searchScope === 'guild') {
return this.searchGuildMessages(this.guild.guild.id, q)
}
return []
}
setDiscoveryFilter ({ query, sort, minMembers, tag }) { setDiscoveryFilter ({ query, sort, minMembers, tag }) {
if (query !== undefined) this._discoveryFilter.query = String(query || '').trim() if (query !== undefined) this._discoveryFilter.query = String(query || '').trim()
if (sort === 'members' || sort === 'name' || sort === 'newest') { if (sort === 'members' || sort === 'name' || sort === 'newest') {
@@ -868,6 +893,52 @@ class PearcordPlatform extends EventEmitter {
return { guildId, tags: row.discoveryTags } return { guildId, tags: row.discoveryTags }
} }
setForumIncludeArchived (include) {
this._forumIncludeArchived = !!include
this.emit('forum-filter')
}
async searchForumPosts (guildId, forumChannelId, query, opts = {}) {
const q = String(query || '').trim().toLowerCase()
if (!q || !guildId || !forumChannelId) return []
const limit = opts.limit || 50
const includeArchived = opts.includeArchived !== false
const channels = await this.guild?.listChannels?.() || await this.db.find(COLLECTIONS.CHANNELS, { guildId })
const posts = listForumPostsForParent(channels, forumChannelId, { includeArchived })
const forumCh = channels.find((c) => c.id === forumChannelId)
const hits = []
for (const post of posts) {
const tags = this.forum ? await this.forum.getPostTags(guildId, post.id) : []
let content = ''
if (post.rootMessageId) {
const root = await this.db.get(COLLECTIONS.MESSAGES, {
channelId: post.id,
id: post.rootMessageId
})
content = root ? this._messagePlaintext(enrichMessageRow(root)) : ''
} else {
const rows = await this.db.find(COLLECTIONS.MESSAGES, { channelId: post.id })
if (rows[0]) content = this._messagePlaintext(enrichMessageRow(rows[0]))
}
const haystack = forumPostHaystack({
title: post.name,
content,
tags,
archived: post.archived
})
if (!haystack.includes(q)) continue
hits.push({
...post,
tags,
forumChannelId,
forumChannelName: forumCh?.name || 'forum',
searchHaystack: haystack,
snippet: String(content || '').slice(0, 160)
})
}
return hits.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)).slice(0, limit)
}
async searchGuildMessages (guildId, query, opts = {}) { async searchGuildMessages (guildId, query, opts = {}) {
const q = String(query || '').trim().toLowerCase() const q = String(query || '').trim().toLowerCase()
if (!q || !guildId) return [] if (!q || !guildId) return []
@@ -894,11 +965,16 @@ class PearcordPlatform extends EventEmitter {
if (!m?.id || seen.has(m.id)) return false if (!m?.id || seen.has(m.id)) return false
seen.add(m.id) seen.add(m.id)
const enriched = enrichMessageRow(m) const enriched = enrichMessageRow(m)
const ch = channelById.get(m.channelId)
const hay = [ const hay = [
ch?.name || '',
String(enriched.content || ''), String(enriched.content || ''),
enriched.hubMirror ? formatHubMirrorFallback(enriched.hubMirror) : '', enriched.hubMirror ? formatHubMirrorFallback(enriched.hubMirror) : '',
...(enriched.stickerNames || []).map((n) => `:${n}:`) ...(enriched.stickerNames || []).map((n) => `:${n}:`),
].join(' ').toLowerCase() ch?.archived ? 'archived' : ''
]
.join(' ')
.toLowerCase()
return searchable.has(m.channelId) && hay.includes(q) return searchable.has(m.channelId) && hay.includes(q)
}) })
.sort((a, b) => b.createdAt - a.createdAt) .sort((a, b) => b.createdAt - a.createdAt)
@@ -3754,7 +3830,9 @@ class PearcordPlatform extends EventEmitter {
const activeChannel = channels.find((c) => c.id === this.activeChannelId) || null const activeChannel = channels.find((c) => c.id === this.activeChannelId) || null
let forumPosts = let forumPosts =
activeChannel && isForumChannel(activeChannel) activeChannel && isForumChannel(activeChannel)
? sortForumPosts(threadsByParent[activeChannel.id] || []) ? listForumPostsForParent(channels, activeChannel.id, {
includeArchived: this._forumIncludeArchived
})
: [] : []
let forumPaletteTags = [] let forumPaletteTags = []
let forumUsedTags = [] let forumUsedTags = []
@@ -4021,12 +4099,8 @@ class PearcordPlatform extends EventEmitter {
stepUpUnlocked, stepUpUnlocked,
searchQuery: this._searchQuery, searchQuery: this._searchQuery,
searchScope: this._searchScope, searchScope: this._searchScope,
searchResults: forumIncludeArchived: this._forumIncludeArchived,
this._searchScope === 'guild' && searchResults: await this._resolveSearchResults(),
this._searchQuery &&
this.guild?.guild?.id
? await this.searchGuildMessages(this.guild.guild.id, this._searchQuery)
: [],
contacts: this.contacts contacts: this.contacts
? { ? {
...(await this.listContacts()), ...(await this.listContacts()),
+1
View File
@@ -39,6 +39,7 @@
"pearcord-device-sync": "file:../pearcord-device-sync", "pearcord-device-sync": "file:../pearcord-device-sync",
"pearcord-step-up": "file:../pearcord-step-up", "pearcord-step-up": "file:../pearcord-step-up",
"pearcord-forum": "file:../pearcord-forum", "pearcord-forum": "file:../pearcord-forum",
"pearcord-forum-search": "file:../pearcord-forum-search",
"pearcord-stage": "file:../pearcord-stage", "pearcord-stage": "file:../pearcord-stage",
"pearcord-automod": "file:../pearcord-automod", "pearcord-automod": "file:../pearcord-automod",
"pearcord-screen-share": "file:../pearcord-screen-share", "pearcord-screen-share": "file:../pearcord-screen-share",