Files
2026-10-22 17:07:08 +08:00

182 lines
7.6 KiB
TypeScript

import {bot_instance_context, BotContext} from "./bot_instance_init";
import {bot_config, bot_status} from "../../types/bot/instance/bot_runtime";
import {validate_code_map} from "../../utils/validate_code_map";
import {init_workflow} from "../workflow/init";
import {update_one_bot_status} from "../../service/server/update/bot_status.ts";
import type {Socket} from "socket.io-client";
const bot_context_map: Map<number, bot_instance_context<any>> = new Map();
const bot_action_lock_map = new Map<number, boolean>();
type ContextObserver = (context: bot_instance_context<any> | undefined) => void;
const bot_context_observers = new Map<number, Set<ContextObserver>>();
export function get_bot_context(bot_id: number): bot_instance_context<any> | undefined {
return bot_context_map.get(bot_id);
}
function notify_context_observer(callback: ContextObserver, context: bot_instance_context<any> | undefined) {
try {
callback(context);
} catch {
console.error('Bot context observer failed');
}
}
function notify_bot_context(bot_id: number) {
for (const callback of Array.from(bot_context_observers.get(bot_id) ?? [])) {
notify_context_observer(callback, bot_context_map.get(bot_id));
}
}
export function observe_bot_context(bot_id: number, callback: ContextObserver): () => void {
let observers = bot_context_observers.get(bot_id);
if (!observers) {
observers = new Set();
bot_context_observers.set(bot_id, observers);
}
observers.add(callback);
notify_context_observer(callback, bot_context_map.get(bot_id));
return () => {
observers.delete(callback);
if (observers.size === 0 && bot_context_observers.get(bot_id) === observers) {
bot_context_observers.delete(bot_id);
}
};
}
function remove_bot_context(bot_id: number, context: bot_instance_context<any>) {
if (bot_context_map.get(bot_id) === context) {
bot_context_map.delete(bot_id);
notify_bot_context(bot_id);
}
}
export async function start_bot_context(main_server_socket: Socket, bot_config: bot_config, node_id: number) {
if (!Number.isSafeInteger(node_id) || node_id <= 0
|| !bot_config || typeof bot_config !== "object" || Array.isArray(bot_config)
|| !bot_config.instance_config || typeof bot_config.instance_config !== "object"
|| Array.isArray(bot_config.instance_config)
|| !Number.isSafeInteger(bot_config.instance_config.bot_id) || bot_config.instance_config.bot_id <= 0
|| !Number.isSafeInteger(bot_config.instance_config.user_id) || bot_config.instance_config.user_id <= 0
|| !Array.isArray(bot_config.plugins) || !Array.isArray(bot_config.workflow)
|| !bot_config.plugins.every(item => item && typeof item === "object" && !Array.isArray(item))
|| !bot_config.workflow.every(item => item && typeof item === "object" && !Array.isArray(item))) {
return validate_code_map.web.invalid_parameter;
}
const bot_id = bot_config.instance_config.bot_id;
if (bot_action_lock_map.get(bot_id) ?? false) {
return validate_code_map.bot_context.processing;
}
bot_action_lock_map.set(bot_id, true);
let new_context: BotContext | undefined;
let context_started = false;
try {
const temp_context = bot_context_map.get(bot_id);
if (temp_context !== undefined) {
if (temp_context.bot_status === "online" || temp_context.bot_status === "connecting"
|| temp_context.bot_status === "reconnecting" || temp_context.bot_status === "ready") {
const result = await temp_context.connect();
return result === validate_code_map.bot_context.ok ? validate_code_map.bot_context.context_created : result;
} else if (temp_context.bot_status === "disconnecting") {
return validate_code_map.bot_context.processing;
} else {
await temp_context.clear_this_context();
remove_bot_context(bot_id, temp_context);
}
}
const context = new_context = new BotContext(bot_config.instance_config);
context.bot_config.plugins = structuredClone(bot_config.plugins);
context.bot_config.workflow = structuredClone(bot_config.workflow);
context.on("status_update", (status) => {
void update_one_bot_status(main_server_socket, node_id, bot_id, bot_config.instance_config.user_id, status)
.catch(error => console.error(`Bot ${bot_id} status update failed`, error));
});
const origin_clean = context.clear_this_context.bind(context);
context.clear_this_context = async () => {
try {
await origin_clean();
} finally {
remove_bot_context(bot_id, context);
}
};
const validation_result = context.validate_config(bot_config.instance_config);
if (validation_result !== validate_code_map.bot_context.ok) {
console.error(`Bot ${bot_id} configuration validation failed, code: ${validation_result}`);
return validation_result;
}
await context.plugin_manager.init(bot_config.plugins);
await init_workflow(context, bot_config.workflow);
bot_context_map.set(bot_id, context);
notify_bot_context(bot_id);
const result = await context.connect();
if (result !== validate_code_map.bot_context.ok
&& result !== validate_code_map.bot_context.instance_connected
&& result !== validate_code_map.bot_context.instance_connecting) {
console.error(`Bot ${bot_id} startup failed, code: ${result}`);
return result;
}
context_started = true;
return result === validate_code_map.bot_context.ok ? validate_code_map.bot_context.context_created : result;
} catch (error) {
console.error(`Bot ${bot_id} startup failed: ${error instanceof Error ? error.message : String(error)}`);
return validate_code_map.bot_context.error;
} finally {
if (new_context && !context_started) {
try {
await new_context.clear_this_context();
} catch (error) {
console.error(`Bot ${bot_id} cleanup failed`, error);
}
}
bot_action_lock_map.delete(bot_id);
}
}
export async function get_bot_context_status() : Promise<{
bot_id: number;
status: bot_status;
}[]> {
return Array.from(bot_context_map).map(([bot_id, context]) => {
return {
bot_id,
status: context.bot_status,
}
});
}
export async function get_bot_context_status_by_ids(bot_id: number[]) : Promise<{
bot_id: number;
status: bot_status;
}[]> {
return bot_id.map((id) => {
return {
bot_id: id,
status: bot_context_map.get(id)?.bot_status ?? "offline",
}
});
}
export async function stop_bot_context(bot_id: number) {
if (bot_action_lock_map.get(bot_id) ?? false) {
return validate_code_map.bot_context.processing;
}
bot_action_lock_map.set(bot_id, true);
try {
if (!bot_context_map.has(bot_id)) {
return validate_code_map.bot_context.instance_end_by_user;
}
const context = bot_context_map.get(bot_id);
if (context !== undefined) {
await context.clear_this_context();
}
if (context) remove_bot_context(bot_id, context);
return validate_code_map.bot_context.instance_end_by_user;
} catch (error) {
return validate_code_map.bot_context.error;
} finally {
bot_action_lock_map.delete(bot_id);
}
}