Files
easemob-sdk-test/scripts/test-easemob-msg.js
zyh a01dbdf574 fix: 修正消息发送 API 使用方式
- 使用正确的环信 SDK 4.x 消息创建 API (message.create)
- 修复消息类型参数,使用 'txt' 代替 messageType.TXT
- 测试验证双用户消息互发功能正常工作

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-03-29 23:19:12 +00:00

273 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 环信 SDK 双用户消息互发测试脚本
*
* 同时登录两个环信账号,测试双向消息收发。
* 配置从项目根目录的 config.json 文件读取。
*
* 用法:
* node scripts/test-easemob-msg.js # 默认互发测试
* node scripts/test-easemob-msg.js <自定义消息> # 指定初始消息内容
*/
const path = require('path');
const fs = require('fs');
// ──────────────────────────────────────────
// 从配置文件读取配置
// ──────────────────────────────────────────
function loadConfig() {
const configPath = path.join(__dirname, '..', 'config.json');
if (!fs.existsSync(configPath)) {
throw new Error(`配置文件不存在: ${configPath}`);
}
const content = fs.readFileSync(configPath, 'utf-8');
return JSON.parse(content);
}
const config = loadConfig();
const ACCOUNTS = config.accounts;
const APP_KEY = config.easemob.appKey;
// ──────────────────────────────────────────
// Polyfill
// ──────────────────────────────────────────
const { applyManualPolyfill } = require('./easemob-polyfill');
// ──────────────────────────────────────────
// 为账号创建连接实例并登录
// ──────────────────────────────────────────
function createConnection(account, websdk, appConfig) {
const conn = new websdk.connection({
appKey: appConfig.appKey,
debug: appConfig.debug || false,
});
const peer = account.label === 'bot' ? ACCOUNTS.tester : ACCOUNTS.bot;
// 消息统计per-account
const stats = { sent: 0, received: 0 };
conn.listen({
onOpened: () => {
console.log(`\n [${account.label}] ✅ 登录成功!`);
},
onClosed: () => {
console.log(` [${account.label}] 连接已关闭`);
},
onError: (error) => {
const errMsg = error?.message || error?.data?.error_description || JSON.stringify(error);
console.error(` [${account.label}] ❌ Error: ${errMsg}`);
},
onTextMessage: (message) => {
stats.received++;
const from = message.from || 'unknown';
const data = message.data || '';
console.log(`\n [${account.label}] 📩 收到消息 from=${from}: "${data}"`);
// 自动 echo 回复
const replyText = `[${account.label} Echo] 收到: "${data.substring(0, 30)}"`;
sendTextMessage(conn, websdk, from, replyText, account.label, stats);
},
onPresence: (message) => {
console.log(` [${account.label}] Presence: ${message?.type} from ${message?.from || ''}`);
},
});
return { conn, stats, account, peer };
}
// ──────────────────────────────────────────
// 发送文本消息
// ──────────────────────────────────────────
function sendTextMessage(conn, websdk, to, text, label, stats) {
try {
const message = websdk.message.create({
type: 'txt',
to,
chatType: 'singleChat',
msg: text,
});
const result = conn.send(message);
// conn.send 可能返回 Promise
if (result && typeof result.catch === 'function') {
result.catch((err) => {
console.error(` [${label}] ❌ 发送失败(async): ${err?.message || err}`);
});
}
stats.sent++;
console.log(` [${label}] 📤 已发送 -> ${to}: "${text}"`);
} catch (err) {
console.error(` [${label}] ❌ 发送失败: ${err.message}`);
}
}
// ──────────────────────────────────────────
// 延时工具
// ──────────────────────────────────────────
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
// ──────────────────────────────────────────
// 主流程
// ──────────────────────────────────────────
async function main() {
const customMsg = process.argv.slice(2).join(' ') || '你好,这是双向测试消息!';
console.log('=== 环信双向消息收发测试 ===');
console.log(` Bot 账号: ${ACCOUNTS.bot.user}`);
console.log(` 测试账号: ${ACCOUNTS.tester.user}`);
console.log(` 初始消息: "${customMsg}"`);
console.log('================================\n');
// Step 1: 显示配置
console.log('[Step 1] 加载配置...');
console.log(` AppKey: ${config.easemob.appKey}`);
console.log(` REST Server: ${config.easemob.restServer}`);
console.log(` Socket Server: ${config.easemob.socketServer}`);
// Step 2: Polyfill
console.log('\n[Step 2] 应用 Polyfill...');
applyManualPolyfill();
// WebSocket polyfill
try {
const wsModule = require('ws');
const OriginalWebSocket = wsModule.WebSocket || wsModule;
globalThis.WebSocket = function DebugWebSocket(url, protocols, options) {
console.log(` [WS] new WebSocket(${JSON.stringify(url)})`);
if (protocols !== undefined) {
return new OriginalWebSocket(url, protocols, options);
}
return new OriginalWebSocket(url);
};
Object.assign(globalThis.WebSocket, OriginalWebSocket);
globalThis.WebSocket.prototype = OriginalWebSocket.prototype;
console.log(' + WebSocket = ws (debug-wrapped)');
} catch (err) {
console.warn(' ! ws 包未安装:', err.message);
}
// XHR polyfill
try {
const { XMLHttpRequest } = require('xhr2');
globalThis.XMLHttpRequest = XMLHttpRequest;
const baseUrl = config.easemob.restServer;
const origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function patchedOpen(method, url, ...rest) {
let finalUrl = url;
if (typeof url === 'string' && url.startsWith('/')) {
finalUrl = baseUrl + url;
}
return origOpen.call(this, method, finalUrl, ...rest);
};
console.log(` + XMLHttpRequest = xhr2 (base: ${baseUrl})`);
} catch {
console.warn(' ! xhr2 包未安装');
}
// Step 3: 加载 SDK
console.log('\n[Step 3] 加载 easemob-websdk...');
let websdk;
try {
const rawSdk = require('easemob-websdk');
websdk = rawSdk.default || rawSdk;
console.log(' SDK 加载成功 ✓');
} catch (err) {
console.error('✗ SDK 加载失败:', err.message);
process.exit(1);
}
// Step 4: 创建两个连接
console.log('\n[Step 4] 创建双用户连接...');
const botCtx = createConnection(ACCOUNTS.bot, websdk, config.easemob);
const testerCtx = createConnection(ACCOUNTS.tester, websdk, config.easemob);
console.log(' 两个连接实例创建成功 ✓');
// Step 5: 双方登录
console.log('\n[Step 5] 双方登录...');
const loginPromises = [
(async () => {
try {
await botCtx.conn.open({ user: ACCOUNTS.bot.user, pwd: ACCOUNTS.bot.pwd });
} catch (err) {
console.error(` [bot] 登录失败: ${err.message}`);
}
})(),
(async () => {
try {
await testerCtx.conn.open({ user: ACCOUNTS.tester.user, pwd: ACCOUNTS.tester.pwd });
} catch (err) {
console.error(` [tester] 登录失败: ${err.message}`);
}
})(),
];
await Promise.all(loginPromises);
// 等待连接建立
console.log('\n 等待双方连接建立...');
await delay(5000);
// Step 6: 双向发消息
console.log('\n[Step 6] 开始双向消息测试...\n');
// tester -> bot
console.log('--- 测试 1: testuser1 -> originclaw_bot ---');
sendTextMessage(
testerCtx.conn, websdk,
ACCOUNTS.bot.user, customMsg,
testerCtx.account.label, testerCtx.stats
);
await delay(3000);
// bot -> tester
console.log('\n--- 测试 2: originclaw_bot -> testuser1 ---');
sendTextMessage(
botCtx.conn, websdk,
ACCOUNTS.tester.user, `[Bot] ${customMsg}`,
botCtx.account.label, botCtx.stats
);
await delay(3000);
// Step 7: 统计结果
console.log('\n\n=== 测试结果 ===');
console.log(` [${botCtx.account.label}] 发送: ${botCtx.stats.sent}, 接收: ${botCtx.stats.received}`);
console.log(` [${testerCtx.account.label}] 发送: ${testerCtx.stats.sent}, 接收: ${testerCtx.stats.received}`);
console.log('\n保持连接中... (Ctrl+C 退出)\n');
// 优雅退出
const cleanup = () => {
console.log('\n=== 退出统计 ===');
console.log(` [${botCtx.account.label}] 发送: ${botCtx.stats.sent}, 接收: ${botCtx.stats.received}`);
console.log(` [${testerCtx.account.label}] 发送: ${testerCtx.stats.sent}, 接收: ${testerCtx.stats.received}`);
try { botCtx.conn.close(); } catch {}
try { testerCtx.conn.close(); } catch {}
setTimeout(() => process.exit(0), 500);
};
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
}
// 防止未处理的 Promise 拒绝导致进程崩溃
process.on('unhandledRejection', (reason) => {
console.error('⚠ unhandledRejection:', reason?.message || reason);
});
// ─── 启动 ───
main().catch((err) => {
console.error('Fatal error:', err);
process.exit(1);
});