fix: исправление подвисания Telegram бота и ошибок AI Watchdog

- telegram.uc: добавлены таймауты --max-time 35 и --connect-timeout 10 для всех запросов curl,
  ограничение длины сообщений до 3900 символов (предотвращение 400 Bad Request от Telegram API),
  безопасная обработка callback_query в try...catch с гарантированным вызовом answerCallbackQuery,
  защита основного цикла worker от падений при сбоях сети
- api.uc: добавлены таймауты --max-time и --connect-timeout для всех curl вызовов,
  устраняющие зависание подпроцессов при опросе Clash API и проверках соединения через прокси
- watchdog.uc: добавлена функция safe_reload_firewall, устранившая ошибки
  'left-hand side is not a function' в процессах ai_heal_nftables и ai_heal_qos
This commit is contained in:
Dushnilin
2026-08-04 19:03:01 +03:00
parent ff627cfb52
commit 1b6cda9c8d
3 changed files with 52 additions and 34 deletions
+11 -11
View File
@@ -53,7 +53,7 @@ function process_running_by_pidfile(pidfile) {
}
function get_clash_proxies_data() {
let args = [ "curl", "-s", get_clash_url("proxies") ];
let args = [ "curl", "-s", "-m", "5", "--connect-timeout", "3", get_clash_url("proxies") ];
let res = command_capture(command_from_args(args));
if (res && res.status == 0 && res.output != "") {
try {
@@ -154,7 +154,7 @@ function clash_request(method, endpoint, payload) {
let payload_path = "/tmp/clash_payload_" + method + "_" + time() + "_" + sprintf("%04x", int(Math.random() * 65536)) + ".json";
let res = null;
try {
let args = [ "curl", "-s", "-X", method ];
let args = [ "curl", "-s", "-m", "5", "--connect-timeout", "3", "-X", method ];
if (payload) {
fs.writefile(payload_path, sprintf("%J", payload));
push(args, "-H", "Content-Type: application/json", "-d", "@" + payload_path);
@@ -186,7 +186,7 @@ function clash_request(method, endpoint, payload) {
}
function get_clash_connections() {
let args = [ "curl", "-s", get_clash_url("connections") ];
let args = [ "curl", "-s", "-m", "5", "--connect-timeout", "3", get_clash_url("connections") ];
let res = command_capture(command_from_args(args));
if (res && res.status == 0 && res.output != "") {
try {
@@ -197,24 +197,24 @@ function get_clash_connections() {
}
function check_connection() {
let res_direct = command_capture("curl -I -s --connect-timeout 5 https://www.google.com");
let direct_ok = (res_direct.status == 0) ? true : false;
let res_direct = command_capture("curl -I -s -m 8 --connect-timeout 4 https://www.google.com");
let direct_ok = (res_direct && res_direct.status == 0) ? true : false;
let res_proxy = command_capture("curl -I -s --connect-timeout 5 --proxy http://127.0.0.1:4534 https://www.google.com");
let proxy_ok = (res_proxy.status == 0) ? true : false;
let res_proxy = command_capture("curl -I -s -m 8 --connect-timeout 4 --proxy http://127.0.0.1:4534 https://www.google.com");
let proxy_ok = (res_proxy && res_proxy.status == 0) ? true : false;
return { direct: direct_ok, proxy: proxy_ok };
}
function run_speedtest() {
// Direct speedtest
let res_direct = command_capture("curl -s -w '%{speed_download}' -o /dev/null --connect-timeout 8 https://speed.cloudflare.com/__down?bytes=5242880");
let direct_speed = double(res_direct.output || 0);
let res_direct = command_capture("curl -s -m 15 --connect-timeout 6 -w '%{speed_download}' -o /dev/null https://speed.cloudflare.com/__down?bytes=5242880");
let direct_speed = double(res_direct ? res_direct.output || 0 : 0);
let direct_mbps = (direct_speed * 8) / 1000000;
// Proxy speedtest
let res_proxy = command_capture("curl -s -w '%{speed_download}' -o /dev/null --connect-timeout 8 --proxy http://127.0.0.1:4534 https://speed.cloudflare.com/__down?bytes=5242880");
let proxy_speed = double(res_proxy.output || 0);
let res_proxy = command_capture("curl -s -m 15 --connect-timeout 6 --proxy http://127.0.0.1:4534 -w '%{speed_download}' -o /dev/null https://speed.cloudflare.com/__down?bytes=5242880");
let proxy_speed = double(res_proxy ? res_proxy.output || 0 : 0);
let proxy_mbps = (proxy_speed * 8) / 1000000;
return { direct_mbps: direct_mbps, proxy_mbps: proxy_mbps };
+37 -23
View File
@@ -48,7 +48,7 @@ function tg_request(token, method, payload) {
let res = null;
try {
fs.writefile(payload_path, sprintf("%J", payload));
let args = [ "curl", "-s", "-X", "POST", "-H", "Content-Type: application/json", "-d", "@" + payload_path ];
let args = [ "curl", "-s", "-m", "35", "--connect-timeout", "10", "-X", "POST", "-H", "Content-Type: application/json", "-d", "@" + payload_path ];
let proxy = get_proxy_args();
for (let p in proxy) push(args, p);
push(args, url);
@@ -64,6 +64,8 @@ function tg_request(token, method, payload) {
}
function send_message(token, chat_id, text, parse_mode, keyboard) {
text = as_string(text);
if (length(text) > 3900) text = substr(text, 0, 3900) + "\n... (сообщение сокращено)";
let payload = { chat_id: int(chat_id), text: text };
if (parse_mode) payload.parse_mode = parse_mode;
if (keyboard) payload.reply_markup = { inline_keyboard: keyboard };
@@ -71,6 +73,8 @@ function send_message(token, chat_id, text, parse_mode, keyboard) {
}
function edit_message(token, chat_id, message_id, text, parse_mode, keyboard) {
text = as_string(text);
if (length(text) > 3900) text = substr(text, 0, 3900) + "\n... (сообщение сокращено)";
let payload = { chat_id: int(chat_id), message_id: int(message_id), text: text };
if (parse_mode) payload.parse_mode = parse_mode;
if (keyboard) payload.reply_markup = { inline_keyboard: keyboard };
@@ -80,7 +84,7 @@ function edit_message(token, chat_id, message_id, text, parse_mode, keyboard) {
function send_document(token, chat_id, file_path) {
if (!token) return null;
let url = "https://api.telegram.org/bot" + token + "/sendDocument";
let args = [ "curl", "-s", "-X", "POST", "-F", "chat_id=" + chat_id, "-F", "document=@" + file_path ];
let args = [ "curl", "-s", "-m", "60", "--connect-timeout", "10", "-X", "POST", "-F", "chat_id=" + chat_id, "-F", "document=@" + file_path ];
let proxy = get_proxy_args();
for (let p in proxy) push(args, p);
push(args, url);
@@ -913,8 +917,9 @@ function handle_sec_clear(token, chat_id, msg_id, sec_name, list_type) {
function exec_doctor(token, chat_id) {
send_message(token, chat_id, "⏳ <b>Запуск диагностики и авто-исправления...</b>", "HTML");
let res = command_capture("/usr/bin/tachyon doctor");
let report = res.output || "Нет вывода диагностики.";
let res = command_capture(command_from_args([ "/usr/bin/tachyon", "doctor" ]));
let report = res ? (res.output || "Нет вывода диагностики.") : "Ошибка запуска диагностики.";
if (length(report) > 3500) report = substr(report, 0, 3500) + "\n... (отчёт сокращён)";
send_message(token, chat_id, "🩺 <b>Результаты Tachyon Doctor:</b>\n\n<pre>" + escape_html(report) + "</pre>", "HTML", [[{text:"⬅️ Назад", callback_data:"/menu"}]]);
}
@@ -1655,8 +1660,9 @@ function exec_ai_heal(token, chat_id, msg_id) {
function exec_ai_status_full(token, chat_id, msg_id) {
send_message(token, chat_id, "🔍 <b>Сбор полного AI Watchdog статуса...</b>", "HTML");
let res = command_capture("/usr/bin/tachyon ai_status_full");
let data = res.output || "Нет данных.";
let res = command_capture(command_from_args([ "/usr/bin/tachyon", "ai_status_full" ]));
let data = res ? (res.output || "Нет данных.") : "Ошибка получения статуса.";
if (length(data) > 3500) data = substr(data, 0, 3500) + "\n... (данные сокращены)";
let text = "🤖 <b>AI Watchdog — Полный статус:</b>\n\n<pre>" + escape_html(data) + "</pre>";
let keyboard = [[{ text: "⬅️ Назад", callback_data: "/watchdog" }]];
if (msg_id) edit_message(token, chat_id, msg_id, text, "HTML", keyboard);
@@ -2059,7 +2065,11 @@ function process_updates(token, admin_ids) {
tg_request(token, "answerCallbackQuery", { callback_query_id: cb.id, text: "Access Denied" });
continue;
}
dispatch_command(token, chat_id, cb.data, cb.message ? cb.message.message_id : null);
try {
dispatch_command(token, chat_id, cb.data, cb.message ? cb.message.message_id : null);
} catch (e) {
send_message(token, chat_id, "❌ Ошибка выполнения команды: " + escape_html(as_string(e)), "HTML");
}
tg_request(token, "answerCallbackQuery", { callback_query_id: cb.id });
continue;
}
@@ -2321,22 +2331,26 @@ function worker() {
let last_update_check = 0;
while (true) {
cfg = settings();
if (cfg.enabled != "1") break;
process_updates(cfg.bot_token, cfg.admin_ids);
let now = time();
let tm = clock(now); // [year, mon, day, hour, min, sec]
let daily_hour = int(cfg.daily_report_hour || "8");
if (cfg.daily_report_enabled == "1" && tm[3] == daily_hour && tm[2] != last_report_day) {
last_report_day = tm[2];
send_daily_digest(cfg.bot_token, cfg.admin_ids);
}
if (now - last_update_check > 3600) { // check every hour
check_notified_updates(cfg.bot_token, cfg.admin_ids);
last_update_check = now;
try {
cfg = settings();
if (cfg.enabled != "1") break;
process_updates(cfg.bot_token, cfg.admin_ids);
let now = time();
let tm = clock(now); // [year, mon, day, hour, min, sec]
let daily_hour = int(cfg.daily_report_hour || "8");
if (cfg.daily_report_enabled == "1" && tm[3] == daily_hour && tm[2] != last_report_day) {
last_report_day = tm[2];
send_daily_digest(cfg.bot_token, cfg.admin_ids);
}
if (now - last_update_check > 3600) { // check every hour
check_notified_updates(cfg.bot_token, cfg.admin_ids);
last_update_check = now;
}
} catch (e) {
command_success_from_args(["logger", "-t", "tachyon-telegram", "[err] Worker loop error: " + as_string(e)]);
}
sleep(poll_interval * 1000);
@@ -473,6 +473,10 @@ function safe_proxy_restart(reason) {
return true;
}
function safe_reload_firewall() {
bg_system("/usr/bin/tachyon reload_firewall </dev/null >/dev/null 2>&1 &");
}
function ai_heal_nftables() {
let cfg = settings();
let routing_mode = cfg.routing_mode || "nftables";