fix(p2p): author profiles on messages, DM users map, and download export
Gossip profile on every send, bundle author and attachmentMetas on messages, build DM usersById from contacts, and export attachments via download-cache before IPC to avoid stuck downloads. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -875,17 +875,22 @@ class PearcordPlatform extends EventEmitter {
|
||||
this.attachments?.ingestGossip(p).catch(() => {})
|
||||
this.emit('attachment', p)
|
||||
})
|
||||
dmInstance.on('user', (u) => {
|
||||
void this._ingestUserProfile(u).catch(() => {})
|
||||
})
|
||||
dmInstance.setMessageHandler(async (msg) => {
|
||||
const existing = await this.db.get(COLLECTIONS.MESSAGES, {
|
||||
channelId: msg.channelId,
|
||||
id: msg.id
|
||||
})
|
||||
if (existing) return existing
|
||||
await this.db.insert(COLLECTIONS.MESSAGES, msg)
|
||||
const plain = this._messagePlaintext(msg)
|
||||
await dmInstance.touchLastMessage(msg.channelId, { ...msg, content: plain })
|
||||
await this._ingestMessageGossipExtras(msg)
|
||||
const row = this._messageRowForStore(msg)
|
||||
await this.db.insert(COLLECTIONS.MESSAGES, row)
|
||||
const plain = this._messagePlaintext(row)
|
||||
await dmInstance.touchLastMessage(row.channelId, { ...row, content: plain })
|
||||
await this._maybeNotify({
|
||||
message: { ...msg, content: plain },
|
||||
message: { ...row, content: plain },
|
||||
mode: 'dm',
|
||||
guildId: null,
|
||||
guildName: null,
|
||||
@@ -895,8 +900,8 @@ class PearcordPlatform extends EventEmitter {
|
||||
members: [],
|
||||
usersById: {}
|
||||
})
|
||||
this.emit('message', msg)
|
||||
return msg
|
||||
this.emit('message', row)
|
||||
return row
|
||||
})
|
||||
dmInstance.on('message', (msg) => this.emit('message', msg))
|
||||
dmInstance.on('message-delete', (p) => this.emit('message-delete', p))
|
||||
@@ -3373,7 +3378,8 @@ class PearcordPlatform extends EventEmitter {
|
||||
|
||||
_wireGuild (guildInstance) {
|
||||
this._ingestInboundGuildMessage = async (msg) => {
|
||||
const row = enrichMessageRow(msg)
|
||||
await this._ingestMessageGossipExtras(msg)
|
||||
const row = enrichMessageRow(this._messageRowForStore(msg))
|
||||
const existing = await this.db.get(COLLECTIONS.MESSAGES, {
|
||||
channelId: row.channelId,
|
||||
id: row.id
|
||||
@@ -3830,6 +3836,8 @@ class PearcordPlatform extends EventEmitter {
|
||||
|
||||
async _collectUsersForSync (members) {
|
||||
const byId = new Map()
|
||||
const self = this._userProfileForSync(this.identity?.user)
|
||||
if (self) byId.set(self.id, self)
|
||||
for (const mem of members || []) {
|
||||
if (!mem?.userId) continue
|
||||
const u = await this.db.get(COLLECTIONS.USERS, { id: mem.userId })
|
||||
@@ -3861,10 +3869,79 @@ class PearcordPlatform extends EventEmitter {
|
||||
return out
|
||||
}
|
||||
|
||||
_activeDmMesh () {
|
||||
return this.dm || this._dmBg || null
|
||||
}
|
||||
|
||||
_gossipSelfUserProfile () {
|
||||
const row = this._userProfileForSync(this.identity?.user)
|
||||
if (!row || !this.guild) return
|
||||
this.guild.gossipUserUpsert(row)
|
||||
if (!row) return
|
||||
if (this.guild) this.guild.gossipUserUpsert(row)
|
||||
const dm = this._activeDmMesh()
|
||||
if (dm?.gossipUserUpsert) dm.gossipUserUpsert(row)
|
||||
}
|
||||
|
||||
_enrichOutboundMessage (msg, { attachmentMetas } = {}) {
|
||||
const out = { ...msg }
|
||||
const author = this._userProfileForSync(this.identity?.user)
|
||||
if (author) out.author = author
|
||||
if (attachmentMetas?.length) out.attachmentMetas = attachmentMetas
|
||||
return out
|
||||
}
|
||||
|
||||
async _ingestMessageGossipExtras (msg) {
|
||||
if (msg?.author?.id) await this._ingestUserProfile(msg.author)
|
||||
if (msg?.attachmentMetas?.length && this.attachments) {
|
||||
for (const att of msg.attachmentMetas) {
|
||||
await this.attachments.ingestGossip(att).catch(() => {})
|
||||
}
|
||||
this.emit('attachment', msg.attachmentMetas[msg.attachmentMetas.length - 1])
|
||||
}
|
||||
}
|
||||
|
||||
_messageRowForStore (msg) {
|
||||
if (!msg) return msg
|
||||
const { author, attachmentMetas, ...row } = msg
|
||||
return row
|
||||
}
|
||||
|
||||
async _buildDmUsersById (messages = [], dmConversations = []) {
|
||||
const map = {}
|
||||
const self = this.identity?.user
|
||||
if (self?.id) map[self.id] = self
|
||||
if (this.contacts) {
|
||||
const rows = await this.contacts.listAccepted().catch(() => [])
|
||||
for (const c of rows) {
|
||||
if (!c?.peerUserId) continue
|
||||
map[c.peerUserId] = {
|
||||
id: c.peerUserId,
|
||||
username: c.peerUsername || null,
|
||||
displayName: c.peerDisplayName || c.peerUsername || null
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const dm of dmConversations || []) {
|
||||
const peer = dm.peerUserId
|
||||
if (!peer) continue
|
||||
const label = dm.name || dm.peerDisplayName
|
||||
if (label && !map[peer]?.displayName && !map[peer]?.username) {
|
||||
map[peer] = { id: peer, username: label, displayName: label }
|
||||
}
|
||||
}
|
||||
const peer = this._dmPeerId()
|
||||
if (peer && this.dm?.channel?.name && !map[peer]) {
|
||||
map[peer] = {
|
||||
id: peer,
|
||||
username: this.dm.channel.name,
|
||||
displayName: this.dm.channel.name
|
||||
}
|
||||
}
|
||||
for (const m of messages || []) {
|
||||
if (!m?.authorId || map[m.authorId]) continue
|
||||
const u = await this.db.get(COLLECTIONS.USERS, { id: m.authorId }).catch(() => null)
|
||||
if (u) map[m.authorId] = u
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
_gossipGuildPresenceSnapshot () {
|
||||
@@ -8861,6 +8938,28 @@ class PearcordPlatform extends EventEmitter {
|
||||
return buf
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist attachment bytes under storage for UI download / open-in-folder flows.
|
||||
*/
|
||||
async exportAttachmentForDownload (attachmentId) {
|
||||
const row = await this.attachments?.get(attachmentId)
|
||||
if (!row) throw new Error('attachment not found')
|
||||
const { buf } = await this.readAttachmentBytesWithSource(attachmentId)
|
||||
if (!buf?.length) throw new Error('attachment file empty')
|
||||
const fs = require('bare-fs')
|
||||
const dir = path.join(this.storagePath, 'download-cache')
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
const safe = String(row.filename || 'file').replace(/[/\\]/g, '_').slice(0, 120) || 'file'
|
||||
const filePath = path.join(dir, `${row.id}-${safe}`)
|
||||
fs.writeFileSync(filePath, buf)
|
||||
return {
|
||||
filePath,
|
||||
filename: row.filename || safe,
|
||||
mimeType: row.mimeType || 'application/octet-stream',
|
||||
size: buf.length
|
||||
}
|
||||
}
|
||||
|
||||
async readAttachmentPreview (attachmentId) {
|
||||
const guildId = this.mode === 'dm' ? DM_GUILD_ID : this.guild?.guild?.id || null
|
||||
const channelId = this.activeChannelId
|
||||
@@ -11878,9 +11977,16 @@ class PearcordPlatform extends EventEmitter {
|
||||
const row = await this.attachments.linkMessage(aid, msg.id)
|
||||
linked.push(row)
|
||||
if (this.guild) this.guild.gossipAttachmentMeta(row)
|
||||
else if (this.dm) this.dm.gossipAttachmentMeta(row)
|
||||
else {
|
||||
const dm = this._activeDmMesh()
|
||||
if (dm?.gossipAttachmentMeta) dm.gossipAttachmentMeta(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
this._gossipSelfUserProfile()
|
||||
const gossipMsg = this._enrichOutboundMessage(msg, {
|
||||
attachmentMetas: linked.length ? linked : undefined
|
||||
})
|
||||
await this._recordSlowmodeSend()
|
||||
if (this.mode === 'guild' && this.guild) {
|
||||
const members = await this.guild.listMembers()
|
||||
@@ -11902,11 +12008,16 @@ class PearcordPlatform extends EventEmitter {
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.mode === 'dm' && this.dm) {
|
||||
await this.dm.touchLastMessage(msg.channelId, { ...msg, content })
|
||||
this.dm.gossipMessage(msg)
|
||||
if (this.mode === 'dm') {
|
||||
const dm = this._activeDmMesh()
|
||||
if (dm) {
|
||||
const previewPlain =
|
||||
this._messagePlaintext(msg) || (linked.length ? '📎 Attachment' : '')
|
||||
await dm.touchLastMessage(msg.channelId, { ...msg, content: previewPlain })
|
||||
dm.gossipMessage(gossipMsg)
|
||||
}
|
||||
} else if (this.guild) {
|
||||
this.guild.gossipMessage(msg)
|
||||
this.guild.gossipMessage(gossipMsg)
|
||||
this._queueLinkEmbed(msg).catch(() => {})
|
||||
this._fanoutBotEvent(BOT_EVENTS.MESSAGE_CREATE, { message: msg })
|
||||
if (this.mode === 'guild') {
|
||||
@@ -13740,8 +13851,15 @@ class PearcordPlatform extends EventEmitter {
|
||||
if (rows.length) attachmentsByMessage[m.id] = rows
|
||||
}
|
||||
}
|
||||
const usersById =
|
||||
let usersById =
|
||||
guild && members.length ? await this._usersById(members) : {}
|
||||
if (user?.id) usersById = { ...usersById, [user.id]: user }
|
||||
if (this.mode === 'dm') {
|
||||
usersById = {
|
||||
...usersById,
|
||||
...(await this._buildDmUsersById(messages, dmConversations))
|
||||
}
|
||||
}
|
||||
const memberTimeouts = {}
|
||||
let communicationDisabled = false
|
||||
if (guild && this.moderation) {
|
||||
|
||||
Reference in New Issue
Block a user