mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
补齐别的格式
This commit is contained in:
@@ -186,6 +186,33 @@ body {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quoted-message {
|
||||||
|
border-left: 3px solid rgba(79, 70, 229, 0.35);
|
||||||
|
background: rgba(79, 70, 229, 0.06);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent .quoted-message {
|
||||||
|
background: rgba(37, 99, 235, 0.08);
|
||||||
|
border-left-color: rgba(37, 99, 235, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quoted-sender {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #374151;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quoted-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.message-link-card {
|
.message-link-card {
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|||||||
@@ -186,6 +186,33 @@ body {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quoted-message {
|
||||||
|
border-left: 3px solid rgba(79, 70, 229, 0.35);
|
||||||
|
background: rgba(79, 70, 229, 0.06);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent .quoted-message {
|
||||||
|
background: rgba(37, 99, 235, 0.08);
|
||||||
|
border-left-color: rgba(37, 99, 235, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quoted-sender {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #374151;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quoted-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.message-link-card {
|
.message-link-card {
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|||||||
@@ -1634,7 +1634,13 @@ class ExportService {
|
|||||||
if (type === '6') return title ? `[文件] ${title}` : '[文件]'
|
if (type === '6') return title ? `[文件] ${title}` : '[文件]'
|
||||||
if (type === '19') return this.formatForwardChatRecordContent(normalizedContent)
|
if (type === '19') return this.formatForwardChatRecordContent(normalizedContent)
|
||||||
if (type === '33' || type === '36') return title ? `[小程序] ${title}` : '[小程序]'
|
if (type === '33' || type === '36') return title ? `[小程序] ${title}` : '[小程序]'
|
||||||
if (type === '57') return title || '[引用消息]'
|
if (type === '57') {
|
||||||
|
const quoteDisplay = this.extractQuotedReplyDisplay(content)
|
||||||
|
if (quoteDisplay) {
|
||||||
|
return this.buildQuotedReplyText(quoteDisplay)
|
||||||
|
}
|
||||||
|
return title || '[引用消息]'
|
||||||
|
}
|
||||||
if (type === '5' || type === '49') return title ? `[链接] ${title}` : '[链接]'
|
if (type === '5' || type === '49') return title ? `[链接] ${title}` : '[链接]'
|
||||||
return title ? `[链接] ${title}` : '[链接]'
|
return title ? `[链接] ${title}` : '[链接]'
|
||||||
}
|
}
|
||||||
@@ -1643,6 +1649,10 @@ class ExportService {
|
|||||||
case 266287972401: return this.cleanSystemMessage(content) // 拍一拍
|
case 266287972401: return this.cleanSystemMessage(content) // 拍一拍
|
||||||
case 244813135921: {
|
case 244813135921: {
|
||||||
// 引用消息
|
// 引用消息
|
||||||
|
const quoteDisplay = this.extractQuotedReplyDisplay(content)
|
||||||
|
if (quoteDisplay) {
|
||||||
|
return this.buildQuotedReplyText(quoteDisplay)
|
||||||
|
}
|
||||||
const title = this.extractXmlValue(content, 'title')
|
const title = this.extractXmlValue(content, 'title')
|
||||||
return title || '[引用消息]'
|
return title || '[引用消息]'
|
||||||
}
|
}
|
||||||
@@ -1676,7 +1686,13 @@ class ExportService {
|
|||||||
if (xmlType === '6') return title ? `[文件] ${title}` : '[文件]'
|
if (xmlType === '6') return title ? `[文件] ${title}` : '[文件]'
|
||||||
if (xmlType === '19') return this.formatForwardChatRecordContent(normalizedContent)
|
if (xmlType === '19') return this.formatForwardChatRecordContent(normalizedContent)
|
||||||
if (xmlType === '33' || xmlType === '36') return title ? `[小程序] ${title}` : '[小程序]'
|
if (xmlType === '33' || xmlType === '36') return title ? `[小程序] ${title}` : '[小程序]'
|
||||||
if (xmlType === '57') return title || '[引用消息]'
|
if (xmlType === '57') {
|
||||||
|
const quoteDisplay = this.extractQuotedReplyDisplay(content)
|
||||||
|
if (quoteDisplay) {
|
||||||
|
return this.buildQuotedReplyText(quoteDisplay)
|
||||||
|
}
|
||||||
|
return title || '[引用消息]'
|
||||||
|
}
|
||||||
if (xmlType === '5' || xmlType === '49') return title ? `[链接] ${title}` : '[链接]'
|
if (xmlType === '5' || xmlType === '49') return title ? `[链接] ${title}` : '[链接]'
|
||||||
|
|
||||||
// 有 title 就返回 title
|
// 有 title 就返回 title
|
||||||
@@ -1801,6 +1817,10 @@ class ExportService {
|
|||||||
return `[小程序]${appName}`
|
return `[小程序]${appName}`
|
||||||
}
|
}
|
||||||
if (subType === 57) {
|
if (subType === 57) {
|
||||||
|
const quoteDisplay = this.extractQuotedReplyDisplay(safeContent)
|
||||||
|
if (quoteDisplay) {
|
||||||
|
return this.buildQuotedReplyText(quoteDisplay)
|
||||||
|
}
|
||||||
return title || '[引用消息]'
|
return title || '[引用消息]'
|
||||||
}
|
}
|
||||||
if (title) {
|
if (title) {
|
||||||
@@ -1812,6 +1832,151 @@ class ExportService {
|
|||||||
return '[其他消息]'
|
return '[其他消息]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private formatQuotedReferencePreview(content: string, type?: string): string {
|
||||||
|
const safeContent = content || ''
|
||||||
|
const referType = Number.parseInt(String(type || ''), 10)
|
||||||
|
if (!Number.isFinite(referType)) {
|
||||||
|
const sanitized = this.sanitizeQuotedContent(safeContent)
|
||||||
|
return sanitized || '[消息]'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referType === 49) {
|
||||||
|
const normalized = this.normalizeAppMessageContent(safeContent)
|
||||||
|
const title =
|
||||||
|
this.extractXmlValue(normalized, 'title') ||
|
||||||
|
this.extractXmlValue(normalized, 'filename') ||
|
||||||
|
this.extractXmlValue(normalized, 'appname')
|
||||||
|
if (title) return this.stripSenderPrefix(title)
|
||||||
|
|
||||||
|
const subTypeRaw = this.extractAppMessageType(normalized)
|
||||||
|
const subType = subTypeRaw ? parseInt(subTypeRaw, 10) : 0
|
||||||
|
if (subType === 6) return '[文件]'
|
||||||
|
if (subType === 19) return '[聊天记录]'
|
||||||
|
if (subType === 33 || subType === 36) return '[小程序]'
|
||||||
|
return '[链接]'
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formatPlainExportContent(safeContent, referType, { exportVoiceAsText: false }) || '[消息]'
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveQuotedSenderUsername(fromusr?: string, chatusr?: string): string {
|
||||||
|
const normalizedChatUsr = String(chatusr || '').trim()
|
||||||
|
const normalizedFromUsr = String(fromusr || '').trim()
|
||||||
|
|
||||||
|
if (normalizedChatUsr) {
|
||||||
|
return normalizedChatUsr
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalizedFromUsr.endsWith('@chatroom')) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedFromUsr
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildQuotedReplyText(display: {
|
||||||
|
replyText: string
|
||||||
|
quotedSender?: string
|
||||||
|
quotedPreview: string
|
||||||
|
}): string {
|
||||||
|
const quoteLabel = display.quotedSender
|
||||||
|
? `${display.quotedSender}:${display.quotedPreview}`
|
||||||
|
: display.quotedPreview
|
||||||
|
if (display.replyText) {
|
||||||
|
return `${display.replyText}[引用 ${quoteLabel}]`
|
||||||
|
}
|
||||||
|
return `[引用 ${quoteLabel}]`
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractQuotedReplyDisplay(content: string): {
|
||||||
|
replyText: string
|
||||||
|
quotedSender?: string
|
||||||
|
quotedPreview: string
|
||||||
|
} | null {
|
||||||
|
try {
|
||||||
|
const normalized = this.normalizeAppMessageContent(content || '')
|
||||||
|
const referMsgStart = normalized.indexOf('<refermsg>')
|
||||||
|
const referMsgEnd = normalized.indexOf('</refermsg>')
|
||||||
|
if (referMsgStart === -1 || referMsgEnd === -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const referMsgXml = normalized.substring(referMsgStart, referMsgEnd + 11)
|
||||||
|
const quoteInfo = this.parseQuoteMessage(normalized)
|
||||||
|
const replyText = this.stripSenderPrefix(this.extractXmlValue(normalized, 'title') || '')
|
||||||
|
const quotedPreview = this.formatQuotedReferencePreview(
|
||||||
|
this.extractXmlValue(referMsgXml, 'content'),
|
||||||
|
this.extractXmlValue(referMsgXml, 'type')
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!replyText && !quotedPreview) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
replyText,
|
||||||
|
quotedSender: quoteInfo.sender || undefined,
|
||||||
|
quotedPreview: quotedPreview || '[消息]'
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resolveQuotedReplyDisplayWithNames(args: {
|
||||||
|
content: string
|
||||||
|
isGroup: boolean
|
||||||
|
displayNamePreference: ExportOptions['displayNamePreference']
|
||||||
|
getContact: (username: string) => Promise<{ success: boolean; contact?: any; error?: string }>
|
||||||
|
groupNicknamesMap: Map<string, string>
|
||||||
|
cleanedMyWxid: string
|
||||||
|
rawMyWxid?: string
|
||||||
|
myDisplayName?: string
|
||||||
|
}): Promise<{
|
||||||
|
replyText: string
|
||||||
|
quotedSender?: string
|
||||||
|
quotedPreview: string
|
||||||
|
} | null> {
|
||||||
|
const base = this.extractQuotedReplyDisplay(args.content)
|
||||||
|
if (!base) return null
|
||||||
|
if (base.quotedSender) return base
|
||||||
|
|
||||||
|
const normalized = this.normalizeAppMessageContent(args.content || '')
|
||||||
|
const referMsgStart = normalized.indexOf('<refermsg>')
|
||||||
|
const referMsgEnd = normalized.indexOf('</refermsg>')
|
||||||
|
if (referMsgStart === -1 || referMsgEnd === -1) {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
const referMsgXml = normalized.substring(referMsgStart, referMsgEnd + 11)
|
||||||
|
const quotedSenderUsername = this.resolveQuotedSenderUsername(
|
||||||
|
this.extractXmlValue(referMsgXml, 'fromusr'),
|
||||||
|
this.extractXmlValue(referMsgXml, 'chatusr')
|
||||||
|
)
|
||||||
|
if (!quotedSenderUsername) {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
const isQuotedSelf = this.isSameWxid(quotedSenderUsername, args.cleanedMyWxid)
|
||||||
|
const fallbackDisplayName = isQuotedSelf
|
||||||
|
? (args.myDisplayName || quotedSenderUsername)
|
||||||
|
: quotedSenderUsername
|
||||||
|
|
||||||
|
const profile = await this.resolveExportDisplayProfile(
|
||||||
|
quotedSenderUsername,
|
||||||
|
args.displayNamePreference,
|
||||||
|
args.getContact,
|
||||||
|
args.groupNicknamesMap,
|
||||||
|
fallbackDisplayName,
|
||||||
|
isQuotedSelf ? [args.rawMyWxid, args.cleanedMyWxid] : []
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
quotedSender: profile.displayName || fallbackDisplayName || base.quotedSender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private parseDurationSeconds(value: string): number | null {
|
private parseDurationSeconds(value: string): number | null {
|
||||||
const numeric = Number(value)
|
const numeric = Number(value)
|
||||||
if (!Number.isFinite(numeric) || numeric <= 0) return null
|
if (!Number.isFinite(numeric) || numeric <= 0) return null
|
||||||
@@ -2493,6 +2658,15 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getExportPlatformMessageId(msg: { serverIdRaw?: unknown; serverId?: unknown }): string | undefined {
|
||||||
|
const value = this.normalizeUnsignedIntToken(msg.serverIdRaw ?? msg.serverId)
|
||||||
|
return value !== '0' ? value : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
private getExportReplyToMessageId(content: string): string | undefined {
|
||||||
|
return this.extractChatLabReplyToMessageId(content)
|
||||||
|
}
|
||||||
|
|
||||||
private extractArkmeAppMessageMeta(content: string, localType: number): Record<string, any> | null {
|
private extractArkmeAppMessageMeta(content: string, localType: number): Record<string, any> | null {
|
||||||
if (!content) return null
|
if (!content) return null
|
||||||
|
|
||||||
@@ -4944,6 +5118,20 @@ class ExportService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
if (quotedReplyDisplay) {
|
||||||
|
content = this.buildQuotedReplyText(quotedReplyDisplay)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取发送者信息用于名称显示
|
// 获取发送者信息用于名称显示
|
||||||
const senderWxid = msg.senderUsername
|
const senderWxid = msg.senderUsername
|
||||||
const contact = senderWxid
|
const contact = senderWxid
|
||||||
@@ -4987,6 +5175,12 @@ class ExportService {
|
|||||||
senderAvatarKey: msg.senderUsername
|
senderAvatarKey: msg.senderUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const platformMessageId = this.getExportPlatformMessageId(msg)
|
||||||
|
if (platformMessageId) msgObj.platformMessageId = platformMessageId
|
||||||
|
|
||||||
|
const replyToMessageId = this.getExportReplyToMessageId(msg.content)
|
||||||
|
if (replyToMessageId) msgObj.replyToMessageId = replyToMessageId
|
||||||
|
|
||||||
const appMsgMeta = this.extractArkmeAppMessageMeta(msg.content, msg.localType)
|
const appMsgMeta = this.extractArkmeAppMessageMeta(msg.content, msg.localType)
|
||||||
if (appMsgMeta) {
|
if (appMsgMeta) {
|
||||||
if (
|
if (
|
||||||
@@ -4996,6 +5190,10 @@ class ExportService {
|
|||||||
Object.assign(msgObj, appMsgMeta)
|
Object.assign(msgObj, appMsgMeta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (quotedReplyDisplay) {
|
||||||
|
if (quotedReplyDisplay.quotedSender) msgObj.quotedSender = quotedReplyDisplay.quotedSender
|
||||||
|
if (quotedReplyDisplay.quotedPreview) msgObj.quotedContent = quotedReplyDisplay.quotedPreview
|
||||||
|
}
|
||||||
|
|
||||||
if (options.format === 'arkme-json') {
|
if (options.format === 'arkme-json') {
|
||||||
const contactCardMeta = this.extractArkmeContactCardMeta(msg.content, msg.localType)
|
const contactCardMeta = this.extractArkmeContactCardMeta(msg.content, msg.localType)
|
||||||
@@ -5193,6 +5391,8 @@ class ExportService {
|
|||||||
senderID,
|
senderID,
|
||||||
source: message.source
|
source: message.source
|
||||||
}
|
}
|
||||||
|
if (message.platformMessageId) compactMessage.platformMessageId = message.platformMessageId
|
||||||
|
if (message.replyToMessageId) compactMessage.replyToMessageId = message.replyToMessageId
|
||||||
if (message.locationLat != null) compactMessage.locationLat = message.locationLat
|
if (message.locationLat != null) compactMessage.locationLat = message.locationLat
|
||||||
if (message.locationLng != null) compactMessage.locationLng = message.locationLng
|
if (message.locationLng != null) compactMessage.locationLng = message.locationLng
|
||||||
if (message.locationPoiname) compactMessage.locationPoiname = message.locationPoiname
|
if (message.locationPoiname) compactMessage.locationPoiname = message.locationPoiname
|
||||||
@@ -5830,6 +6030,20 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
if (quotedReplyDisplay) {
|
||||||
|
enrichedContentValue = this.buildQuotedReplyText(quotedReplyDisplay)
|
||||||
|
}
|
||||||
|
|
||||||
// 调试日志
|
// 调试日志
|
||||||
if (msg.localType === 3 || msg.localType === 47) {
|
if (msg.localType === 3 || msg.localType === 47) {
|
||||||
}
|
}
|
||||||
@@ -6077,6 +6291,20 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
if (quotedReplyDisplay) {
|
||||||
|
enrichedContentValue = this.buildQuotedReplyText(quotedReplyDisplay)
|
||||||
|
}
|
||||||
|
|
||||||
appendRow(useCompactColumns
|
appendRow(useCompactColumns
|
||||||
? [
|
? [
|
||||||
i + 1,
|
i + 1,
|
||||||
@@ -6430,6 +6658,20 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
if (quotedReplyDisplay) {
|
||||||
|
enrichedContentValue = this.buildQuotedReplyText(quotedReplyDisplay)
|
||||||
|
}
|
||||||
|
|
||||||
let senderRole: string
|
let senderRole: string
|
||||||
let senderWxid: string
|
let senderWxid: string
|
||||||
let senderNickname: string
|
let senderNickname: string
|
||||||
@@ -6724,7 +6966,7 @@ class ExportService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const lines: string[] = []
|
const lines: string[] = []
|
||||||
lines.push('id,MsgSvrID,type_name,is_sender,talker,msg,src,CreateTime')
|
lines.push('id,MsgSvrID,ReplyToMsgSvrID,type_name,is_sender,talker,msg,src,CreateTime')
|
||||||
const senderProfileCache = new Map<string, ExportDisplayProfile>()
|
const senderProfileCache = new Map<string, ExportDisplayProfile>()
|
||||||
|
|
||||||
for (let i = 0; i < totalMessages; i++) {
|
for (let i = 0; i < totalMessages; i++) {
|
||||||
@@ -6790,15 +7032,31 @@ class ExportService {
|
|||||||
msg.senderUsername,
|
msg.senderUsername,
|
||||||
msg.isSend
|
msg.isSend
|
||||||
) || '')
|
) || '')
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
const finalMsgText = quotedReplyDisplay
|
||||||
|
? this.buildQuotedReplyText(quotedReplyDisplay)
|
||||||
|
: msgText
|
||||||
const src = this.getWeCloneSource(msg, typeName, mediaItem)
|
const src = this.getWeCloneSource(msg, typeName, mediaItem)
|
||||||
|
const platformMessageId = this.getExportPlatformMessageId(msg) || ''
|
||||||
|
const replyToMessageId = this.getExportReplyToMessageId(msg.content) || ''
|
||||||
|
|
||||||
const row = [
|
const row = [
|
||||||
i + 1,
|
i + 1,
|
||||||
i + 1,
|
platformMessageId,
|
||||||
|
replyToMessageId,
|
||||||
typeName,
|
typeName,
|
||||||
msg.isSend ? 1 : 0,
|
msg.isSend ? 1 : 0,
|
||||||
talker,
|
talker,
|
||||||
msgText,
|
finalMsgText,
|
||||||
src,
|
src,
|
||||||
this.formatIsoTimestamp(msg.createTime)
|
this.formatIsoTimestamp(msg.createTime)
|
||||||
]
|
]
|
||||||
@@ -7270,8 +7528,18 @@ class ExportService {
|
|||||||
|
|
||||||
const timeText = this.formatTimestamp(msg.createTime)
|
const timeText = this.formatTimestamp(msg.createTime)
|
||||||
const typeName = this.getMessageTypeName(msg.localType)
|
const typeName = this.getMessageTypeName(msg.localType)
|
||||||
|
const quotedReplyDisplay = await this.resolveQuotedReplyDisplayWithNames({
|
||||||
|
content: msg.content,
|
||||||
|
isGroup,
|
||||||
|
displayNamePreference: options.displayNamePreference,
|
||||||
|
getContact: getContactCached,
|
||||||
|
groupNicknamesMap,
|
||||||
|
cleanedMyWxid,
|
||||||
|
rawMyWxid,
|
||||||
|
myDisplayName: myInfo.displayName || cleanedMyWxid
|
||||||
|
})
|
||||||
|
|
||||||
let textContent = this.formatHtmlMessageText(
|
let textContent = quotedReplyDisplay?.replyText || this.formatHtmlMessageText(
|
||||||
msg.content,
|
msg.content,
|
||||||
msg.localType,
|
msg.localType,
|
||||||
cleanedMyWxid,
|
cleanedMyWxid,
|
||||||
@@ -7302,7 +7570,7 @@ class ExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkCard = this.extractHtmlLinkCard(msg.content, msg.localType)
|
const linkCard = quotedReplyDisplay ? null : this.extractHtmlLinkCard(msg.content, msg.localType)
|
||||||
|
|
||||||
let mediaHtml = ''
|
let mediaHtml = ''
|
||||||
if (mediaItem?.kind === 'image') {
|
if (mediaItem?.kind === 'image') {
|
||||||
@@ -7318,25 +7586,40 @@ class ExportService {
|
|||||||
mediaHtml = `<video class="message-media video" controls preload="metadata"${posterAttr} src="${this.escapeAttribute(encodeURI(mediaItem.relativePath))}"></video>`
|
mediaHtml = `<video class="message-media video" controls preload="metadata"${posterAttr} src="${this.escapeAttribute(encodeURI(mediaItem.relativePath))}"></video>`
|
||||||
}
|
}
|
||||||
|
|
||||||
const textHtml = linkCard
|
const textHtml = quotedReplyDisplay
|
||||||
? `<div class="message-text"><a class="message-link-card" href="${this.escapeAttribute(linkCard.url)}" target="_blank" rel="noopener noreferrer">${this.renderTextWithEmoji(linkCard.title).replace(/\r?\n/g, '<br />')}</a></div>`
|
? (() => {
|
||||||
: (textContent
|
const quotedSenderHtml = quotedReplyDisplay.quotedSender
|
||||||
? `<div class="message-text">${this.renderTextWithEmoji(textContent).replace(/\r?\n/g, '<br />')}</div>`
|
? `<div class="quoted-sender">${this.escapeHtml(quotedReplyDisplay.quotedSender)}</div>`
|
||||||
: '')
|
: ''
|
||||||
|
const quotedPreviewHtml = `<div class="quoted-text">${this.renderTextWithEmoji(quotedReplyDisplay.quotedPreview).replace(/\r?\n/g, '<br />')}</div>`
|
||||||
|
const replyTextHtml = textContent
|
||||||
|
? `<div class="message-text">${this.renderTextWithEmoji(textContent).replace(/\r?\n/g, '<br />')}</div>`
|
||||||
|
: ''
|
||||||
|
return `<div class="quoted-message">${quotedSenderHtml}${quotedPreviewHtml}</div>${replyTextHtml}`
|
||||||
|
})()
|
||||||
|
: (linkCard
|
||||||
|
? `<div class="message-text"><a class="message-link-card" href="${this.escapeAttribute(linkCard.url)}" target="_blank" rel="noopener noreferrer">${this.renderTextWithEmoji(linkCard.title).replace(/\r?\n/g, '<br />')}</a></div>`
|
||||||
|
: (textContent
|
||||||
|
? `<div class="message-text">${this.renderTextWithEmoji(textContent).replace(/\r?\n/g, '<br />')}</div>`
|
||||||
|
: ''))
|
||||||
const senderNameHtml = isGroup
|
const senderNameHtml = isGroup
|
||||||
? `<div class="sender-name">${this.escapeHtml(resolvedSenderName)}</div>`
|
? `<div class="sender-name">${this.escapeHtml(resolvedSenderName)}</div>`
|
||||||
: ''
|
: ''
|
||||||
const timeHtml = `<div class="message-time">${this.escapeHtml(timeText)}</div>`
|
const timeHtml = `<div class="message-time">${this.escapeHtml(timeText)}</div>`
|
||||||
const messageBody = `${timeHtml}${senderNameHtml}<div class="message-content">${mediaHtml}${textHtml}</div>`
|
const messageBody = `${timeHtml}${senderNameHtml}<div class="message-content">${mediaHtml}${textHtml}</div>`
|
||||||
|
const platformMessageId = this.getExportPlatformMessageId(msg)
|
||||||
|
const replyToMessageId = this.getExportReplyToMessageId(msg.content)
|
||||||
|
|
||||||
// Compact JSON object
|
// Compact JSON object
|
||||||
const itemObj = {
|
const itemObj: Record<string, any> = {
|
||||||
i: i + 1, // index
|
i: i + 1, // index
|
||||||
t: msg.createTime, // timestamp
|
t: msg.createTime, // timestamp
|
||||||
s: isSenderMe ? 1 : 0, // isSend
|
s: isSenderMe ? 1 : 0, // isSend
|
||||||
a: avatarHtml, // avatar HTML
|
a: avatarHtml, // avatar HTML
|
||||||
b: messageBody // body HTML
|
b: messageBody // body HTML
|
||||||
}
|
}
|
||||||
|
if (platformMessageId) itemObj.p = platformMessageId
|
||||||
|
if (replyToMessageId) itemObj.r = replyToMessageId
|
||||||
|
|
||||||
writeBuf.push(JSON.stringify(itemObj))
|
writeBuf.push(JSON.stringify(itemObj))
|
||||||
|
|
||||||
@@ -7384,8 +7667,10 @@ class ExportService {
|
|||||||
// Render Item Function
|
// Render Item Function
|
||||||
const renderItem = (item, index) => {
|
const renderItem = (item, index) => {
|
||||||
const isSenderMe = item.s === 1;
|
const isSenderMe = item.s === 1;
|
||||||
|
const platformIdAttr = item.p ? \` data-platform-message-id="\${item.p}"\` : '';
|
||||||
|
const replyToAttr = item.r ? \` data-reply-to-message-id="\${item.r}"\` : '';
|
||||||
return \`
|
return \`
|
||||||
<div class="message \${isSenderMe ? 'sent' : 'received'}" data-index="\${item.i}">
|
<div class="message \${isSenderMe ? 'sent' : 'received'}" data-index="\${item.i}"\${platformIdAttr}\${replyToAttr}>
|
||||||
<div class="message-row">
|
<div class="message-row">
|
||||||
<div class="avatar">\${item.a}</div>
|
<div class="avatar">\${item.a}</div>
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
|
|||||||
Reference in New Issue
Block a user