feat(contacts): broadcast and ingest friend presence on contacts mesh
Add PRESENCE_UPDATE gossip on pearcord:contacts:{userId} so accepted
friends see online/idle/dnd/offline without joining each other's guilds.
- ingestFriendPresence map with TTL-friendly updates
- broadcastPresence on setPresence and mesh rejoin (_lastSelfPresence)
- getFriendPresenceMap for platform listContacts enrichment
- simulateGossip handles PRESENCE_UPDATE for CI smokes
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -5,7 +5,7 @@ const path = require('bare-path')
|
|||||||
const EventEmitter = require('bare-events')
|
const EventEmitter = require('bare-events')
|
||||||
const b4a = require('b4a')
|
const b4a = require('b4a')
|
||||||
const { JsonStore } = require('pearcord-db/store-json')
|
const { JsonStore } = require('pearcord-db/store-json')
|
||||||
const { RPC, contactsTopic, topicToBuffer, id, now } = require('pearcord-shared')
|
const { RPC, contactsTopic, topicToBuffer, id, now, USER_STATUS } = require('pearcord-shared')
|
||||||
const { attachContactsMesh, broadcastContactsGossip } = require('./mesh')
|
const { attachContactsMesh, broadcastContactsGossip } = require('./mesh')
|
||||||
|
|
||||||
const CONTACTS_COLLECTION = '@pearcord/contacts'
|
const CONTACTS_COLLECTION = '@pearcord/contacts'
|
||||||
@@ -34,6 +34,8 @@ class PearcordContacts extends EventEmitter {
|
|||||||
this._channels = new Map()
|
this._channels = new Map()
|
||||||
this._meshJoined = false
|
this._meshJoined = false
|
||||||
this._ephemeralTopics = new Set()
|
this._ephemeralTopics = new Set()
|
||||||
|
this._friendPresence = new Map()
|
||||||
|
this._lastSelfPresence = null
|
||||||
}
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
@@ -283,7 +285,46 @@ class PearcordContacts extends EventEmitter {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ingestFriendPresence (payload) {
|
||||||
|
if (!payload?.userId || payload.userId === this.userId) return null
|
||||||
|
const row = {
|
||||||
|
userId: payload.userId,
|
||||||
|
status: payload.status || USER_STATUS.ONLINE,
|
||||||
|
customStatus: payload.customStatus || null,
|
||||||
|
at: payload.at || now()
|
||||||
|
}
|
||||||
|
this._friendPresence.set(payload.userId, row)
|
||||||
|
this.emit('friend-presence', row)
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastPresence (payload) {
|
||||||
|
if (!payload?.userId) return null
|
||||||
|
this._lastSelfPresence = {
|
||||||
|
userId: payload.userId,
|
||||||
|
status: payload.status || USER_STATUS.ONLINE,
|
||||||
|
customStatus: payload.customStatus || null,
|
||||||
|
at: payload.at || now()
|
||||||
|
}
|
||||||
|
if (this._meshJoined) {
|
||||||
|
broadcastContactsGossip(this, RPC.PRESENCE_UPDATE, this._lastSelfPresence)
|
||||||
|
}
|
||||||
|
return this._lastSelfPresence
|
||||||
|
}
|
||||||
|
|
||||||
|
getFriendPresenceMap () {
|
||||||
|
const out = {}
|
||||||
|
for (const [uid, row] of this._friendPresence) {
|
||||||
|
out[uid] = row
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (method, payload) {
|
_onGossip (method, payload) {
|
||||||
|
if (method === RPC.PRESENCE_UPDATE) {
|
||||||
|
this.ingestFriendPresence(payload)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (method === RPC.CONTACT_REQUEST) {
|
if (method === RPC.CONTACT_REQUEST) {
|
||||||
this.ingestRequest(payload).catch(() => {})
|
this.ingestRequest(payload).catch(() => {})
|
||||||
} else if (method === RPC.CONTACT_ACCEPT) {
|
} else if (method === RPC.CONTACT_ACCEPT) {
|
||||||
@@ -300,6 +341,7 @@ class PearcordContacts extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async simulateGossip (method, payload) {
|
async simulateGossip (method, payload) {
|
||||||
|
if (method === RPC.PRESENCE_UPDATE) return this.ingestFriendPresence(payload)
|
||||||
if (method === RPC.CONTACT_REQUEST) return this.ingestRequest(payload)
|
if (method === RPC.CONTACT_REQUEST) return this.ingestRequest(payload)
|
||||||
if (method === RPC.CONTACT_ACCEPT) return this.ingestAccept(payload)
|
if (method === RPC.CONTACT_ACCEPT) return this.ingestAccept(payload)
|
||||||
if (method === RPC.CONTACT_DECLINE) return this.ingestDecline(payload)
|
if (method === RPC.CONTACT_DECLINE) return this.ingestDecline(payload)
|
||||||
@@ -366,6 +408,9 @@ class PearcordContacts extends EventEmitter {
|
|||||||
})
|
})
|
||||||
await this._flushSwarm(3500)
|
await this._flushSwarm(3500)
|
||||||
this._meshJoined = true
|
this._meshJoined = true
|
||||||
|
if (this._lastSelfPresence) {
|
||||||
|
broadcastContactsGossip(this, RPC.PRESENCE_UPDATE, this._lastSelfPresence)
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user