182 lines
7.6 KiB
TypeScript
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);
|
|
}
|
|
}
|