feat: 新增机器人上/下线按钮,识别「账号已在别处登录」与「等待验证码」
下线按钮 - 顶栏新增「下线 / 上线」切换:下线会断开连接、停止自动重连,并停掉定时消息与 扩地任务;需要在游戏客户端登同一账号时必须先下线,否则会被服务端以 「您已经连接到此代理服务器了」反复踢掉 - 接口 POST /api/bots/:id/offline 与 /online;上线时会清掉「已在别处登录」标记 并把重连计数归零,否则重连会一直退避到最大间隔 识别两类此前「看起来像启动不了」的情况 - 「您已经连接到此代理服务器了」:服务端踢人时发的是聊天组件对象, 原来 JSON.stringify 之后没法看,关键信息被淹没。新增 extractKickReason() 提取纯文本,命中后给出「请先在客户端退出」的可操作提示, 并直接把重连退避到最大间隔,不再频繁硬撞服务器 - 「请直接输入验证码进行验证」:账号绑定过之后服务器仍可能每次登录都要求验证码, 而原来的正则只认「完成验证 / 身份验证」,漏掉了这句话,用户只看到机器人卡在大厅。 现在补上匹配,并在控制台弹出醒目提示 + 把输入框 placeholder 换成「输入验证码后回车发送」 测试 343 -> 355,全部通过。
这个提交包含在:
@@ -290,6 +290,49 @@ test('tick 驱动全部机器人且单个异常不影响其它', () => {
|
||||
assert.deepStrictEqual(ticks, ['B']);
|
||||
});
|
||||
|
||||
// ==================== 上线 / 下线 ====================
|
||||
|
||||
test('disconnectBot 断线并停止自动重连、定时消息与扩地任务', () => {
|
||||
const { pool } = setup();
|
||||
const entry = pool.get('BotA');
|
||||
let disconnected = false;
|
||||
let taskStopped = false;
|
||||
let messengerStopped = false;
|
||||
entry.manager.disconnect = () => { disconnected = true; };
|
||||
entry.controller.stop = () => { taskStopped = true; };
|
||||
entry.messenger.stop = () => { messengerStopped = true; };
|
||||
|
||||
pool.disconnectBot('BotA');
|
||||
|
||||
assert.strictEqual(disconnected, true, '应断开连接');
|
||||
assert.strictEqual(taskStopped, true, '应停掉扩地任务');
|
||||
assert.strictEqual(messengerStopped, true, '应停掉定时消息');
|
||||
});
|
||||
|
||||
test('connectBot 清掉「已在别处登录」标记并恢复连接', () => {
|
||||
const { pool } = setup();
|
||||
const entry = pool.get('BotA');
|
||||
entry.manager.alreadyConnected = true;
|
||||
entry.manager.reconnectAttempts = 5;
|
||||
const connected = [];
|
||||
entry.manager.connect = () => connected.push('BotA');
|
||||
let messengerStarted = false;
|
||||
entry.messenger.start = () => { messengerStarted = true; };
|
||||
|
||||
pool.connectBot('BotA');
|
||||
|
||||
assert.strictEqual(entry.manager.alreadyConnected, false, '不清掉标记会一直退避到最大间隔');
|
||||
assert.strictEqual(entry.manager.reconnectAttempts, 0);
|
||||
assert.strictEqual(messengerStarted, true);
|
||||
assert.deepStrictEqual(connected, ['BotA']);
|
||||
});
|
||||
|
||||
test('上线 / 下线对不存在的机器人抛 404', () => {
|
||||
const { pool } = setup();
|
||||
assert.throws(() => pool.disconnectBot('不存在'), (err) => err.statusCode === 404);
|
||||
assert.throws(() => pool.connectBot('不存在'), (err) => err.statusCode === 404);
|
||||
});
|
||||
|
||||
test('未启用(enabled=false)的机器人不参与连接', () => {
|
||||
const { pool } = setup({ settings: { BotB: { enabled: false } } });
|
||||
const connected = [];
|
||||
|
||||
在新工单中引用
屏蔽一个用户