feat: send test messages during bot connection checks

This commit is contained in:
xmanrui 2026-08-19 03:52:52 +08:00
parent 94da24538f
commit fb61c4235f
78 changed files with 3460 additions and 210 deletions

View file

@ -240,6 +240,35 @@ export class FeishuRuntime {
}
}
async sendConnectionTest(text) {
if (!this.#status.ready || !this.#client) {
const error = new Error('飞书机器人尚未连接');
error.code = 'test-target-unavailable';
throw error;
}
const ownerOpenId = this.#ownerOpenIds[0];
if (!ownerOpenId || ownerOpenId === '*') {
const error = new Error('飞书机器人没有可用的测试消息接收人');
error.code = 'test-target-unavailable';
throw error;
}
if (typeof text !== 'string' || !text.trim()) {
throw new TypeError('Feishu connection test text is required');
}
const response = await this.#client.im.v1.message.create({
params: { receive_id_type: 'open_id' },
data: {
receive_id: ownerOpenId,
msg_type: 'text',
content: JSON.stringify({ text }),
},
});
if (response?.code && response.code !== 0) {
throw new Error(`Feishu connection test failed: ${response.msg || response.code}`);
}
return { sent: true };
}
async stop({ preserveError = false } = {}) {
const error = preserveError ? this.#status.lastError : null;
const abortController = this.#abortController;

View file

@ -1,4 +1,5 @@
import { randomUUID } from 'node:crypto';
import { connectionTestMessage } from '../shared/connection-test.mjs';
import { RegistrationManager } from './registration-manager.mjs';
import { REQUIRED_TENANT_SCOPES } from './plugin-controller.mjs';
@ -339,6 +340,26 @@ export class MultiBotDshFeishuController {
});
}
async sendConnectionTest(botId) {
this.#assertOpen();
return this.#withBotTransition(botId, async () => {
const config = this.#requireBot(botId);
const runtime = this.#runtimes.get(botId);
if (!isConnected(connectionStatus(runtime))
|| typeof runtime.sendConnectionTest !== 'function') {
const error = new Error('飞书机器人尚未连接');
error.code = 'test-target-unavailable';
throw error;
}
return runtime.sendConnectionTest(
connectionTestMessage(
`${config.botName}(${maskedAppId(config.appId)})`,
'飞书机器人',
),
);
});
}
async disconnectBot(botId) {
this.#assertOpen();
// An operational pause only: credentials/config remain durable, so the