'use strict'; const assert = require('assert'); const fs = require('fs'); const os = require('os'); const path = require('path'); const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-inbound-media-')); process.env.QIWEI_OUTPUTS_DIR = path.join(tempRoot, 'outputs'); const { AgentWorkbenchDb } = require('../mcp/src/core/agent-workbench-db'); const { AgentWorkbenchService } = require('../mcp/src/core/agent-workbench-service'); const { evaluatePolledMessage } = require('../mcp/src/core/agent-poller-policy'); const { InboundMediaService, mediaMessageDescriptor, sniffMedia } = require('../mcp/src/core/inbound-media'); async function main() { const imageMessage = { msgType: 14, senderId: 'contact-1', timestamp: 1785920000, msgData: { fileId: 'file-1', fileAesKey: 'key-1', fileSize: 68, width: 1, height: 1 }, }; const imageCandidate = evaluatePolledMessage(imageMessage, { allowedSenders: ['contact-1'] }); assert.equal(imageCandidate.eligible, true); assert.equal(imageCandidate.content, '[图片]'); assert.equal(imageCandidate.media.kind, 'image'); assert.equal(imageCandidate.generateReply, false); const voiceCandidate = evaluatePolledMessage({ msgType: 16, senderId: 'contact-1', timestamp: 1785920001, msgData: { fileId: 'voice-1', fileAesKey: 'key-2', voiceTime: 6 }, }, { allowedSenders: ['contact-1'] }); assert.equal(voiceCandidate.eligible, true); assert.equal(voiceCandidate.content, '[语音]'); assert.equal(voiceCandidate.generateReply, false); const analyzedVoice = mediaMessageDescriptor({ msgType: 16, msgData: { transcript: '我明天下午有时间' }, }); assert.equal(analyzedVoice.mediaOnly, false); assert.equal(analyzedVoice.content, '[语音] 我明天下午有时间'); const groupCandidate = evaluatePolledMessage({ ...imageMessage, fromRoomId: 'room-1' }, { allowedSenders: ['contact-1'] }); assert.equal(groupCandidate.eligible, false); assert.equal(groupCandidate.reason, 'group_message'); const db = new AgentWorkbenchDb(path.join(tempRoot, 'workbench.db')); let agentCalls = 0; const service = new AgentWorkbenchService({ db, agent: { run: async () => { agentCalls += 1; throw new Error('agent must not run'); } }, qiwei: {}, config: { qiwei: { allowedSenders: ['contact-1'] }, agent: {} }, }); const stored = await service.ingestInbound({ externalId: 'image-message-1', contactId: 'contact-1', contactName: '测试联系人', content: imageCandidate.content, timestamp: new Date(1785920000 * 1000).toISOString(), raw: imageMessage, contentType: imageCandidate.media.contentType, payload: imageCandidate.media.payload, }, { generateReply: false }); assert.equal(stored.status, 'media_received'); assert.equal(agentCalls, 0); const second = await service.ingestInbound({ externalId: 'image-message-2', contactId: 'contact-1', contactName: '测试联系人', content: imageCandidate.content, timestamp: new Date((1785920000 + 1) * 1000).toISOString(), raw: { ...imageMessage, msgServerId: 'image-message-2' }, contentType: imageCandidate.media.contentType, payload: imageCandidate.media.payload, }, { generateReply: false }); assert.equal(second.status, 'media_received'); const rows = db.listMessages(stored.conversation.id); assert.equal(rows.length, 2); assert.equal(rows[0].content_type, 'image'); assert.equal(JSON.parse(rows[0].raw_json).msgData.fileId, 'file-1'); if (typeof service.stopBackgroundWorkers === 'function') service.stopBackgroundWorkers(); db.close(); const png = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=', 'base64'); assert.equal(sniffMedia(png, 'application/octet-stream').contentType, 'image/png'); let downloadCalls = 0; const resolver = new InboundMediaService({ qiwei: { call: async (method, params, options) => { assert.equal(method, '/cloud/wxWorkDownload'); assert.equal(params.fileType, 1); assert.equal(options.sanitizeResponse, false); return { cloudUrl: 'https://media.example.test/image.png' }; }, }, fetchImpl: async url => { downloadCalls += 1; assert.equal(url, 'https://media.example.test/image.png'); return new Response(png, { status: 200, headers: { 'content-type': 'image/png' } }); }, }); const resolved = await resolver.resolve('message-1', imageMessage); assert.equal(downloadCalls, 1); assert.equal(resolved.contentType, 'image/png'); assert.equal(fs.readFileSync(resolved.filePath).equals(png), true); const dashboardApp = fs.readFileSync(path.join(__dirname, '..', 'mcp', 'src', 'dashboard', 'app.js'), 'utf8'); const dashboardServer = fs.readFileSync(path.join(__dirname, '..', 'mcp', 'src', 'dashboard', 'server.js'), 'utf8'); const agentService = fs.readFileSync(path.join(__dirname, '..', 'mcp', 'src', 'dashboard', 'agent-service.js'), 'utf8'); const workbenchService = fs.readFileSync(path.join(__dirname, '..', 'mcp', 'src', 'core', 'agent-workbench-service.js'), 'utf8'); assert.match(dashboardApp, /data-media-retry/); assert.match(dashboardApp, /\/api\/agent\/messages\/\$\{encodeURIComponent\(message\.id\)\}\/media/); assert.match(dashboardServer, /\/api\\\/agent\\\/messages\\\/\(\[\^\/\]\+\)\\\/media/); assert.match(agentService, /function publicMessageRaw/); assert.match(workbenchService, /media_received_without_auto_reply/); fs.rmSync(tempRoot, { recursive: true, force: true }); console.log('agent inbound media smoke: passed'); } main().catch(error => { try { fs.rmSync(tempRoot, { recursive: true, force: true }); } catch {} console.error(error); process.exit(1); });