refactor: 控制台输入框不再区分聊天与指令,只留一个入口

聊天和指令本来就是同一个东西:以 / 开头按游戏指令发送,否则按公屏消息发送。
服务端 /api/bots/:id/chat 一直就是这么分流的,前端多一个模式下拉框纯属多余。

- 删掉「公屏聊天 / 游戏指令」下拉框与重复的「快捷公屏消息」输入框
- 只保留:一个输入框 + 快捷发送按钮 + 移动测试按钮
- 接口返回新增 kind 字段(chat / command)说明实际走的是哪条路
- Toast 区分「消息已发送」与「指令已发送:/xxx」
- 快捷发送补上 /u restore confirm(配合定时消息常用)
- 空内容提示从「消息为空」改为「内容为空」(不再只针对聊天)

测试 325 -> 326,全部通过。
这个提交包含在:
lyizai
2026-09-28 01:00:05 +08:00
父节点 a83dea9677
当前提交 f1316bf58c
共修改 7 个文件,包含 82 行新增和 52 行删除
+17 -4
查看文件
@@ -477,18 +477,31 @@ class BotPool extends EventEmitter {
* @param {string} message 聊天正文(以 `/` 开头时按指令发送)
* @returns {{ok: boolean, message: string}}
*/
/**
* 向某机器人发送内容:聊天与指令是同一个入口,**以 `/` 开头按指令发送**,否则按公屏消息发送。
*
* @param {string} id
* @param {string} message 内容(可以带或不带前导 `/`)
* @returns {{ok: boolean, kind: string, message: string}}
*/
sendChat(id, message) {
const entry = this.require(id);
const text = String(message || '').trim();
if (!text) return { ok: false, message: '消息为空' };
if (!text) return { ok: false, kind: 'chat', message: '内容为空' };
const isCommand = text.startsWith('/');
const kind = isCommand ? 'command' : 'chat';
if (!entry.manager.canSendChat()) {
if (entry.manager.state !== 'online' || !entry.manager.bot) {
return { ok: false, message: `${entry.connection.username} 当前未连接(状态:${entry.manager.state}),消息未发送` };
return { ok: false, kind, message: `${entry.connection.username} 当前未连接(状态:${entry.manager.state}),未发送` };
}
return { ok: false, message: `${entry.connection.username} 未完成登录验证,已拒绝发送公屏消息(服务器要求验证时会自动放行,用于发送验证码)` };
return { ok: false, kind, message: `${entry.connection.username} 未完成登录验证,已拒绝发送(服务器要求验证时会自动放行,用于发送验证码)` };
}
const ok = entry.manager.sendChat(text);
return { ok, message: ok ? '已发送' : '发送失败' };
return {
ok,
kind,
message: ok ? (isCommand ? '指令已发送:' + text : '消息已发送') : '发送失败'
};
}
/**
+11 -26
查看文件
@@ -709,8 +709,8 @@ function renderStatus(status) {
const canOperate = bot.canOperate === true;
$('consoleHint').textContent = canOperate
? '登录验证通过:可以直接向该机器人发送指令或公屏消息。'
: '该机器人尚未通过游戏内登录验证,发送指令 / 消息会被拒绝。';
? '以 / 开头按游戏指令发送,否则按公屏消息发送。'
: '该机器人尚未通过游戏内登录验证,发送会被拒绝(服务器要求验证时会自动放行,用于发验证码)。';
$('consoleHint').style.color = canOperate ? 'var(--muted)' : 'var(--yellow)';
// ===== 任务卡片 =====
@@ -1990,13 +1990,9 @@ function bindEvents() {
$('cmdInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter') sendConsole();
});
$('btnQuickChat').addEventListener('click', sendQuickChat);
$('quickChatInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter') sendQuickChat();
});
// 快捷发送:把预设内容填进唯一的输入框并立即发送
for (const btn of document.querySelectorAll('[data-cmd]')) {
btn.addEventListener('click', () => {
$('sendMode').value = 'command';
$('cmdInput').value = btn.dataset.cmd;
sendConsole();
});
@@ -2028,33 +2024,22 @@ function bindEvents() {
});
$('btnLogout').addEventListener('click', doLogout);
/**
* 发送控制台内容。
*
* 聊天和指令不区分模式:**以 `/` 开头按游戏指令发送,否则按公屏消息发送** ——
* 服务端 `/api/bots/:id/chat` 就是这么分流的,前端不需要多一个下拉框。
*/
async function sendConsole() {
const text = $('cmdInput').value.trim();
if (!text || !selectedBotId) return;
const mode = $('sendMode').value;
const path = mode === 'command'
? `/api/bots/${encodeURIComponent(selectedBotId)}/command`
: `/api/bots/${encodeURIComponent(selectedBotId)}/chat`;
const body = mode === 'command' ? { command: text } : { message: text };
try {
await api(path, { method: 'POST', body: JSON.stringify(body) });
$('cmdInput').value = '';
toast('已发送');
} catch (err) {
toast('发送失败:' + err.message, true);
}
}
async function sendQuickChat() {
const text = $('quickChatInput').value.trim();
if (!text || !selectedBotId) return;
try {
await api(`/api/bots/${encodeURIComponent(selectedBotId)}/chat`, {
method: 'POST',
body: JSON.stringify({ message: text })
});
$('quickChatInput').value = '';
toast('公屏消息已发送');
$('cmdInput').value = '';
toast(text.startsWith('/') ? '指令已发送:' + text : '消息已发送');
} catch (err) {
toast('发送失败:' + err.message, true);
}
+9 -15
查看文件
@@ -267,27 +267,21 @@
<section class="column col-right" id="panelConfig">
<div class="card">
<div class="card-head">
<h2>控制台 / 聊天框</h2>
<h2>控制台</h2>
<span class="mono small" id="consoleTarget">-</span>
</div>
<p class="hint" id="consoleHint">登录验证通过后可向该机器人发送指令或公屏消息。</p>
<p class="hint" id="consoleHint">以 / 开头按游戏指令发送,否则按公屏消息发送。登录验证通过后才允许发送。</p>
<div class="row row-compact">
<select id="sendMode" class="input input-sm">
<option value="chat">公屏聊天</option>
<option value="command">游戏指令</option>
</select>
<input id="cmdInput" class="input input-grow" type="text" placeholder="输入内容后回车发送">
<input id="cmdInput" class="input input-grow" type="text" placeholder="输入内容或 /指令,回车发送">
<button id="btnSendCmd" class="btn btn-sm btn-primary">发送</button>
</div>
<div class="row row-compact">
<button class="btn btn-sm" data-cmd="u return">/u return</button>
<button class="btn btn-sm" data-cmd="u claimmode">/u claimmode</button>
<button class="btn btn-sm" data-cmd="spawn">/spawn</button>
<button class="btn btn-sm" data-cmd="list">/list</button>
</div>
<div class="row row-compact">
<input id="quickChatInput" class="input input-grow" type="text" placeholder="快捷公屏消息">
<button id="btnQuickChat" class="btn btn-sm">公屏发送</button>
<span class="field-label">快捷发送</span>
<button class="btn btn-sm" data-cmd="/u return">/u return</button>
<button class="btn btn-sm" data-cmd="/u claimmode">/u claimmode</button>
<button class="btn btn-sm" data-cmd="/u restore confirm">/u restore confirm</button>
<button class="btn btn-sm" data-cmd="/spawn">/spawn</button>
<button class="btn btn-sm" data-cmd="/list">/list</button>
</div>
<div class="row row-compact">
<span class="field-label">移动测试</span>
+5 -3
查看文件
@@ -957,12 +957,14 @@ class SingleBotPool {
sendChat(id, message) {
const entry = this.require(id);
const text = String(message || '').trim();
if (!text) return { ok: false, message: '消息为空' };
if (!text) return { ok: false, kind: 'chat', message: '内容为空' };
const isCommand = text.startsWith('/');
const kind = isCommand ? 'command' : 'chat';
if (!entry.manager.canSendChat()) {
return { ok: false, message: '未完成登录验证,已拒绝发送公屏消息' };
return { ok: false, kind, message: '未完成登录验证,已拒绝发送' };
}
const ok = entry.manager.sendChat(text);
return { ok, message: ok ? '已发送' : '发送失败' };
return { ok, kind, message: ok ? (isCommand ? '指令已发送:' + text : '消息已发送') : '发送失败' };
}
}