Files
Pawer/cloudfunctions/messageThread/index.js

107 lines
3.5 KiB
JavaScript

const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const _ = db.command
const SERVER_MESSAGE_LIMIT = 50
function timeText(ts) {
if (!ts) return ''
const d = new Date(ts)
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
const sameDay = new Date().toDateString() === d.toDateString()
if (sameDay) return `${hh}:${mm}`
return `${d.getMonth() + 1}/${d.getDate()} ${hh}:${mm}`
}
async function userToPeer(u) {
return { id: u._id, name: u.nickname || '用户', avatarKey: u.avatarKey || 'gradient-avatar-1', avatarUrl: u.avatarUrl || '' }
}
function normalizeLimit(value) {
const limit = Number(value || SERVER_MESSAGE_LIMIT)
if (!Number.isFinite(limit)) return SERVER_MESSAGE_LIMIT
return Math.max(1, Math.min(Math.floor(limit), SERVER_MESSAGE_LIMIT))
}
function messageToClient(m, openid) {
return {
_id: m._id,
fromMe: m.fromOpenid === openid,
content: m.content,
type: m.type || 'text',
createdAt: m.createdAt,
timeText: timeText(m.createdAt)
}
}
exports.main = async event => {
const { OPENID } = cloud.getWXContext()
if (!OPENID) throw new Error('no openid in wx context')
let convId = event.conversationId || ''
let peer = null
const pageSize = normalizeLimit(event.pageSize || event.limit)
// Open by target user — resolve peer and find an existing conversation.
if (!convId && event.targetUserId) {
const tRes = await db.collection('users').doc(event.targetUserId).get().catch(() => null)
const t = tRes && tRes.data
if (t) {
peer = await userToPeer(t)
const ex = await db.collection('conversations')
.where({ type: 'single', memberOpenids: _.all([OPENID, t.openid]) })
.limit(1)
.get()
.catch(() => ({ data: [] }))
if (ex.data.length) convId = ex.data[0]._id
}
}
// Open by conversation — resolve the other member as peer.
if (convId && !peer) {
const c = await db.collection('conversations').doc(convId).get().catch(() => null)
if (c && c.data) {
const otherOpenid = (c.data.memberOpenids || []).find(o => o !== OPENID)
if (otherOpenid) {
const oRes = await db.collection('users').where({ openid: otherOpenid }).limit(1).get()
if (oRes.data[0]) peer = await userToPeer(oRes.data[0])
}
if (!peer) {
peer = { id: '', name: c.data.title || '对话', avatarKey: c.data.avatarKey || 'gradient-avatar-1', avatarUrl: '' }
}
}
}
let messages = []
let nextCursor = ''
if (convId) {
const query = { conversationId: convId }
const cursor = Number(event.cursor || 0)
if (Number.isFinite(cursor) && cursor > 0) query.createdAt = _.lt(cursor)
const res = await db.collection('messages')
.where(query)
.orderBy('createdAt', 'desc')
.limit(pageSize + 1)
.get()
.catch(() => ({ data: [] }))
const page = res.data.slice(0, pageSize)
messages = page.reverse().map(m => messageToClient(m, OPENID))
if (res.data.length > pageSize && messages[0]) {
nextCursor = String(messages[0].createdAt || '')
}
// Mark this conversation read for me only when the client asks for it.
// Silent polling and loading older history should not write the conversation.
if (event.markRead !== false) {
await db.collection('conversations').doc(convId).update({
data: { unreadOpenids: _.pull(OPENID), [`unreadCounts.${OPENID}`]: 0 }
}).catch(() => {})
}
}
return { conversationId: convId, peer, messages, nextCursor }
}