mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-04 03:01:42 +08:00
fix: 修复导出日期、语音和联系人名字问题
- 导出的 HTML:点月份先看日期,选中日期后才跳到对应消息。 - 语音转文字:不同账号、不同聊天里的语音不会再串到一起。 - 联系人名字:正常昵称显示不变;昵称异常时使用可见的备注或账号,导出文件名也能正常使用。
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
displayContactName,
|
||||
filesystemSafeName,
|
||||
isVisibleName,
|
||||
visibleNamePart
|
||||
} from '../../src/shared/contact-name'
|
||||
|
||||
describe('contact name safety', () => {
|
||||
it('recognizes private-use, zero-width, and control-only labels as invisible', () => {
|
||||
expect(isVisibleName('\uE000\uE000')).toBe(false)
|
||||
expect(isVisibleName('\u200B\u200D')).toBe(false)
|
||||
expect(displayContactName({ m_nsNickName: '\uE000\uE000', remark: '备注名', wxid: 'wxid_a' })).toBe(
|
||||
'备注名'
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves visible emoji and removes only non-rendering code points', () => {
|
||||
expect(visibleNamePart(' 👩💻\u200B ')).toBe('👩💻')
|
||||
expect(
|
||||
displayContactName({
|
||||
m_nsNickName: String.fromCodePoint(0xE0020),
|
||||
wechatId: 'wechat-alias'
|
||||
})
|
||||
).toBe(
|
||||
'wechat-alias'
|
||||
)
|
||||
})
|
||||
|
||||
it('sanitizes reserved path characters, trailing dots, device names, and length', () => {
|
||||
expect(filesystemSafeName('a/b:c*?"<>|. ')).toMatch(/^a_b_c_+$/)
|
||||
expect(filesystemSafeName('CON')).toBe('CON_')
|
||||
expect(filesystemSafeName('\uE000\uE000', '未命名联系人')).toBe('未命名联系人')
|
||||
expect(Array.from(filesystemSafeName('😀'.repeat(100))).length).toBe(80)
|
||||
})
|
||||
})
|
||||
@@ -51,4 +51,66 @@ describe('VoiceService batch lookup', () => {
|
||||
expect(resolveVoice).toHaveBeenNthCalledWith(1, 'session', 10, 100, '1000')
|
||||
expect(resolveVoice).toHaveBeenNthCalledWith(2, 'session', 11, 101, '1001')
|
||||
})
|
||||
|
||||
it('keeps same local ids isolated by session and server identity, then reuses the exact cache entries', async () => {
|
||||
const getVoiceDataBatch = vi
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{ success: true, hex: '0102' },
|
||||
{ success: true, hex: '0304' },
|
||||
{ success: true, hex: '0506' }
|
||||
])
|
||||
const service = new VoiceService({ getVoiceDataBatch } as never, '/accounts/fixture-a')
|
||||
const decoder = (service as unknown as {
|
||||
decoderRegistry: { decode: ReturnType<typeof vi.fn> }
|
||||
}).decoderRegistry
|
||||
vi.spyOn(decoder, 'decode').mockImplementation(async (source: { data: Buffer }) => ({
|
||||
pcm: Buffer.from([source.data[0]]),
|
||||
sampleRate: 16_000,
|
||||
channels: 1
|
||||
}))
|
||||
|
||||
const references = [
|
||||
{ sessionId: 'session-a', localId: 7, createTime: 100, svrId: 'same-second-a' },
|
||||
{ sessionId: 'session-b', localId: 7, createTime: 100, svrId: 'same-second-a' },
|
||||
{ sessionId: 'session-a', localId: 7, createTime: 100, svrId: 'same-second-b' }
|
||||
]
|
||||
const first = await service.resolveVoices(references)
|
||||
expect(first.every((item) => item.success)).toBe(true)
|
||||
expect(new Set(first.map((item) => item.data)).size).toBe(3)
|
||||
expect(getVoiceDataBatch).toHaveBeenCalledOnce()
|
||||
|
||||
const second = await service.resolveVoices(references)
|
||||
expect(second).toEqual(first)
|
||||
expect(getVoiceDataBatch).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('versions the in-memory namespace per account so a restarted or switched account cannot reuse old audio', async () => {
|
||||
const client = {
|
||||
getVoiceDataBatch: vi.fn().mockResolvedValue([{ success: true, hex: '0102' }]),
|
||||
getAccountRoot: () => '/accounts/fixture-a'
|
||||
}
|
||||
const first = new VoiceService(client as never)
|
||||
const second = new VoiceService(
|
||||
{ ...client, getAccountRoot: () => '/accounts/fixture-b' } as never
|
||||
)
|
||||
const decode = (service: VoiceService, value: number): void => {
|
||||
const decoder = (service as unknown as {
|
||||
decoderRegistry: { decode: ReturnType<typeof vi.fn> }
|
||||
}).decoderRegistry
|
||||
vi.spyOn(decoder, 'decode').mockResolvedValue({
|
||||
pcm: Buffer.from([value]),
|
||||
sampleRate: 16_000,
|
||||
channels: 1
|
||||
})
|
||||
}
|
||||
decode(first, 1)
|
||||
decode(second, 2)
|
||||
const firstCache = (first as unknown as { voiceCache: Map<string, string> }).voiceCache
|
||||
const secondCache = (second as unknown as { voiceCache: Map<string, string> }).voiceCache
|
||||
await expect(first.resolveVoices([{ sessionId: 'session', localId: 1, createTime: 2 }])).resolves.toHaveLength(1)
|
||||
await expect(second.resolveVoices([{ sessionId: 'session', localId: 1, createTime: 2 }])).resolves.toHaveLength(1)
|
||||
expect([...firstCache.keys()][0]).not.toBe([...secondCache.keys()][0])
|
||||
expect(client.getVoiceDataBatch).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user