fix: 修复转换微信语音发送逻辑

This commit is contained in:
Wxw-Gu
2026-08-27 14:43:09 +08:00
parent b26d4dd1a7
commit 0d68217dda
18 changed files with 1020 additions and 125 deletions
@@ -11,6 +11,7 @@ vi.mock('electron', () => ({
}))
import {
buildPersonalWechatVoiceDiagnostic,
buildPersonalWechatOneBotRequest,
findWechatImagePath,
findPersonalWechatRuntime,
@@ -32,6 +33,37 @@ afterEach(() => {
})
describe('personal WeChat OneBot request', () => {
it('keeps voice diagnostics to the safe metadata allowlist', () => {
const diagnostic = buildPersonalWechatVoiceDiagnostic('request-1', 'completed', {
input_bytes: 10,
upload_result: '0',
error: undefined,
aesKey: 'secret',
cdnKey: 'secret',
token: 'secret',
runtime_log: 'private'
})
expect(diagnostic).toMatchObject({ request_id: 'request-1', upload_result: '0' })
expect(diagnostic).not.toHaveProperty('aesKey')
expect(diagnostic).not.toHaveProperty('cdnKey')
expect(diagnostic).not.toHaveProperty('token')
expect(diagnostic).not.toHaveProperty('runtime_log')
})
it('redacts secrets embedded in diagnostic errors', () => {
const diagnostic = buildPersonalWechatVoiceDiagnostic('request-2', 'failed', {
error:
'upload failed aesKey=secret-value Bearer bearer-secret token:token-value {"cdnKey":"json-secret"}'
})
expect(diagnostic.error).toContain('aesKey=[redacted]')
expect(diagnostic.error).toContain('Bearer [redacted]')
expect(diagnostic.error).toContain('token:[redacted]')
expect(diagnostic.error).not.toContain('secret-value')
expect(diagnostic.error).not.toContain('bearer-secret')
expect(diagnostic.error).not.toContain('token-value')
expect(diagnostic.error).not.toContain('json-secret')
})
it('builds a private text message request', () => {
expect(
buildPersonalWechatOneBotRequest({
+8
View File
@@ -87,6 +87,14 @@ describe('personal WeChat runtime security invariants', () => {
for (const source of [runtimeManagerSource, preparationScriptSource]) {
expect(source).not.toContain('pilk==')
expect(source).not.toMatch(/['"]pip['"]/)
expect(source).toContain('voiceAudioDataAddr = Memory.alloc(audioLen + 1);')
expect(source).toContain('上传前按语音长度重新分配')
}
const senderSource = readFileSync(
resolve('src/main/services/personal-wechat-send-service.ts'),
'utf8'
)
expect(senderSource).toContain('buildRuntimePythonPath(preflight.runtime.root)')
})
})
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import {
VOICE_MIN_PCM_BYTES,
validateVoicePcm,
validateVoiceSilk,
validateVoiceSilkMetadata
} from '../../src/main/voice-pipeline/voice-quality'
describe('voice quality gates', () => {
it('accepts a complete 20ms PCM frame', () => {
expect(validateVoicePcm(new Uint8Array(VOICE_MIN_PCM_BYTES))).toMatchObject({
pcmSize: VOICE_MIN_PCM_BYTES,
sampleRate: 16_000,
channels: 1,
durationMs: 20
})
})
it('rejects empty, odd-sized and sub-frame PCM', () => {
expect(() => validateVoicePcm(new Uint8Array())).toThrow('PCM 为空')
expect(() => validateVoicePcm(new Uint8Array(3))).toThrow('长度无效')
expect(() => validateVoicePcm(new Uint8Array(VOICE_MIN_PCM_BYTES - 2))).toThrow('过短')
})
it('rejects a header-only Silk payload', () => {
expect(() => validateVoiceSilkMetadata(10, 20)).toThrow('只有文件头')
expect(() => validateVoiceSilk(new TextEncoder().encode('\x02#!SILK_V3'), 20)).toThrow(
'只有文件头'
)
})
it('accepts valid Silk metadata', () => {
expect(validateVoiceSilkMetadata(128, 240)).toEqual({ silkSize: 128, durationMs: 240 })
const silk = new Uint8Array(128)
silk.set(new TextEncoder().encode('\x02#!SILK_V3'))
expect(validateVoiceSilk(silk, 240)).toEqual({ silkSize: 128, durationMs: 240 })
})
})