feat: перенос демона Telegram-бота с Go на Ucode, добавление вкладки настроек LuCI и локализации
This commit is contained in:
@@ -681,8 +681,93 @@ function createSettingsContent(section, capabilities) {
|
||||
o.rmempty = false;
|
||||
}
|
||||
|
||||
function createTelegramContent(section) {
|
||||
let o = section.option(
|
||||
form.Flag,
|
||||
"enabled",
|
||||
_("Enable Telegram Bot"),
|
||||
_("Enables the background daemon that polls Telegram for commands and sends notifications."),
|
||||
);
|
||||
o.default = "0";
|
||||
o.rmempty = false;
|
||||
|
||||
o = section.option(
|
||||
form.Value,
|
||||
"bot_token",
|
||||
_("Telegram Bot Token"),
|
||||
_("Enter the API token obtained from @BotFather."),
|
||||
);
|
||||
o.depends("enabled", "1");
|
||||
o.password = true;
|
||||
o.rmempty = false;
|
||||
o.validate = function (section_id, value) {
|
||||
if (this.section.formvalue(section_id, "enabled") === "1" && !value) {
|
||||
return _("Token is required when the bot is enabled");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
o = section.option(
|
||||
form.Value,
|
||||
"admin_ids",
|
||||
_("Administrator Chat IDs"),
|
||||
_("Comma-separated list of Telegram user IDs authorized to control the bot."),
|
||||
);
|
||||
o.depends("enabled", "1");
|
||||
o.rmempty = false;
|
||||
o.validate = function (section_id, value) {
|
||||
if (this.section.formvalue(section_id, "enabled") === "1") {
|
||||
if (!value) {
|
||||
return _("At least one Admin Chat ID is required");
|
||||
}
|
||||
if (!/^[0-9]+(,[0-9]+)*$/.test(value)) {
|
||||
return _("Must be a comma-separated list of numeric IDs");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
o = section.option(
|
||||
form.Value,
|
||||
"poll_interval",
|
||||
_("Polling Interval (seconds)"),
|
||||
_("How often to check Telegram for new messages (default: 5)."),
|
||||
);
|
||||
o.depends("enabled", "1");
|
||||
o.default = "5";
|
||||
o.rmempty = false;
|
||||
o.validate = function (section_id, value) {
|
||||
const val = parseInt(value);
|
||||
if (isNaN(val) || val < 1) {
|
||||
return _("Polling interval must be at least 1 second");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
o = section.option(
|
||||
form.Flag,
|
||||
"notify_crash",
|
||||
_("Notify on Core Crashes"),
|
||||
_("Send a Telegram notification if sing-box or nftables rules crash and get auto-restored."),
|
||||
);
|
||||
o.depends("enabled", "1");
|
||||
o.default = "1";
|
||||
o.rmempty = false;
|
||||
|
||||
o = section.option(
|
||||
form.Flag,
|
||||
"notify_restart",
|
||||
_("Notify on Service Restarts"),
|
||||
_("Send a Telegram notification if the Tachyon service is restarted by the watchdog."),
|
||||
);
|
||||
o.depends("enabled", "1");
|
||||
o.default = "1";
|
||||
o.rmempty = false;
|
||||
}
|
||||
|
||||
const EntryPoint = {
|
||||
createSettingsContent,
|
||||
createTelegramContent,
|
||||
};
|
||||
|
||||
return baseclass.extend(EntryPoint);
|
||||
|
||||
@@ -431,6 +431,18 @@ const EntryPoint = {
|
||||
};
|
||||
settings.createSettingsContent(settingsSection, uiCapabilities);
|
||||
|
||||
const telegramSection = tachyonMap.section(
|
||||
form.TypedSection,
|
||||
"telegram",
|
||||
_("Telegram Bot"),
|
||||
);
|
||||
telegramSection.anonymous = true;
|
||||
telegramSection.addremove = false;
|
||||
telegramSection.cfgsections = function () {
|
||||
return ["telegram"];
|
||||
};
|
||||
settings.createTelegramContent(telegramSection);
|
||||
|
||||
const diagnosticSection = tachyonMap.section(
|
||||
form.TypedSection,
|
||||
"diagnostic",
|
||||
|
||||
@@ -1877,6 +1877,57 @@ msgstr "Tailscale endpoint"
|
||||
msgid "Tailscale hostname"
|
||||
msgstr "Hostname Tailscale"
|
||||
|
||||
msgid "Telegram Bot"
|
||||
msgstr "Telegram бот"
|
||||
|
||||
msgid "Enable Telegram Bot"
|
||||
msgstr "Включить Telegram бот"
|
||||
|
||||
msgid "Enables the background daemon that polls Telegram for commands and sends notifications."
|
||||
msgstr "Включает фоновую службу, которая опрашивает Telegram на предмет команд и отправляет уведомления."
|
||||
|
||||
msgid "Telegram Bot Token"
|
||||
msgstr "Токен Telegram-бота"
|
||||
|
||||
msgid "Enter the API token obtained from @BotFather."
|
||||
msgstr "Введите API токен, полученный от @BotFather."
|
||||
|
||||
msgid "Token is required when the bot is enabled"
|
||||
msgstr "Токен обязателен при включенном боте"
|
||||
|
||||
msgid "Administrator Chat IDs"
|
||||
msgstr "Chat ID администраторов"
|
||||
|
||||
msgid "Comma-separated list of Telegram user IDs authorized to control the bot."
|
||||
msgstr "Список ID пользователей Telegram (через запятую), которым разрешено управлять ботом."
|
||||
|
||||
msgid "At least one Admin Chat ID is required"
|
||||
msgstr "Требуется указать как минимум один Chat ID администратора"
|
||||
|
||||
msgid "Must be a comma-separated list of numeric IDs"
|
||||
msgstr "Должен быть списком числовых ID через запятую"
|
||||
|
||||
msgid "Polling Interval (seconds)"
|
||||
msgstr "Интервал опроса (секунды)"
|
||||
|
||||
msgid "How often to check Telegram for new messages (default: 5)."
|
||||
msgstr "Как часто опрашивать Telegram о наличии новых сообщений (по умолчанию: 5)."
|
||||
|
||||
msgid "Polling interval must be at least 1 second"
|
||||
msgstr "Интервал опроса должен быть не менее 1 секунды"
|
||||
|
||||
msgid "Notify on Core Crashes"
|
||||
msgstr "Уведомлять о падениях ядра"
|
||||
|
||||
msgid "Send a Telegram notification if sing-box or nftables rules crash and get auto-restored."
|
||||
msgstr "Отправлять уведомление в Telegram, если sing-box или правила nftables падают и восстанавливаются."
|
||||
|
||||
msgid "Notify on Service Restarts"
|
||||
msgstr "Уведомлять о перезапуске служб"
|
||||
|
||||
msgid "Send a Telegram notification if the Tachyon service is restarted by the watchdog."
|
||||
msgstr "Отправлять уведомление в Telegram, если служба Tachyon перезапускается сторожевым таймером."
|
||||
|
||||
msgid "Test latency"
|
||||
msgstr "Тестирование задержки"
|
||||
|
||||
|
||||
@@ -158,3 +158,15 @@ config settings 'settings'
|
||||
# option tailscale_accept_routes '0'
|
||||
# option tailscale_advertise_exit_node '1'
|
||||
# #list tailscale_advertise_routes '192.168.1.0/24'
|
||||
|
||||
#config telegram 'telegram'
|
||||
# option enabled '0'
|
||||
# option bot_token ''
|
||||
# option admin_ids ''
|
||||
# option poll_interval '5'
|
||||
# option notify_crash '1'
|
||||
# option notify_restart '1'
|
||||
# option notify_server_switch '1'
|
||||
# option notify_subscription '1'
|
||||
# option notify_cert '1'
|
||||
# option notify_dns_leak '1'
|
||||
|
||||
@@ -197,6 +197,7 @@ function command_spec(command) {
|
||||
doctor: [ "diagnostics/runtime.uc", "doctor", 1 ],
|
||||
ai_doctor: [ "diagnostics/runtime.uc", "ai-doctor", 0 ],
|
||||
watchdog: [ "service/watchdog.uc", "", 1 ],
|
||||
telegram: [ "service/telegram.uc", "", 2 ],
|
||||
validate_nfqws_strategy_json: [ "diagnostics/runtime.uc", "validate-nfqws-strategy-json", 1 ],
|
||||
validate_nfqws2_strategy_json: [ "diagnostics/runtime.uc", "validate-nfqws2-strategy-json", 1 ],
|
||||
validate_byedpi_strategy_json: [ "providers/byedpi/validator.uc", "validate-json", 1 ],
|
||||
|
||||
@@ -112,6 +112,7 @@ const ZAPRET2_UC = LIB_DIR + "/providers/zapret2/runtime.uc";
|
||||
const BYEDPI_UC = LIB_DIR + "/providers/byedpi/runtime.uc";
|
||||
const PACKAGES_UC = LIB_DIR + "/core/packages.uc";
|
||||
const WATCHDOG_UC = LIB_DIR + "/service/watchdog.uc";
|
||||
const TELEGRAM_UC = LIB_DIR + "/service/telegram.uc";
|
||||
|
||||
let start_subscription_update_lock_held = false;
|
||||
let subscription_caches_prepared = getenv("TACHYON_SUBSCRIPTION_CACHES_PREPARED") || "0";
|
||||
@@ -787,6 +788,11 @@ function start_impl() {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = module_status(TELEGRAM_UC, [ "start-runtime" ]);
|
||||
if (status != 0) {
|
||||
log_message("Failed to start Telegram Bot runtime", "warn");
|
||||
}
|
||||
|
||||
module_background(DIAGNOSTICS_UC, [ "get-system-info" ]);
|
||||
return 0;
|
||||
}
|
||||
@@ -797,6 +803,7 @@ function stop_main() {
|
||||
log_message("Stopping Tachyon", "info");
|
||||
module_success(DNS_FAILOVER_UC, [ "stop-runtime" ]);
|
||||
module_success(WATCHDOG_UC, [ "stop-runtime" ]);
|
||||
module_success(TELEGRAM_UC, [ "stop-runtime" ]);
|
||||
module_success(PRIORITY_UC, [ "stop-runtime" ]);
|
||||
module_success(SUBSCRIPTION_CACHE_UC, [ "stop-deferred-bootstrap-worker" ]);
|
||||
module_success(UPDATES_UC, [ "stop-list-update" ]);
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
let fs = require("fs");
|
||||
let uci_core = require("core.uci");
|
||||
let common = require("core.common");
|
||||
|
||||
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
|
||||
const LIB_DIR = getenv("TACHYON_LIB") || "/usr/lib/tachyon";
|
||||
const PID_FILE = "/var/run/tachyon_telegram.pid";
|
||||
const OFFSET_FILE = "/var/run/tachyon_telegram_offset";
|
||||
|
||||
let as_string = common.as_string;
|
||||
let shell_quote = common.shell_quote;
|
||||
|
||||
function command_from_args(args) {
|
||||
let result = [];
|
||||
for (let arg in args)
|
||||
push(result, shell_quote(arg));
|
||||
return join(" ", result);
|
||||
}
|
||||
|
||||
function command_status(command) {
|
||||
let status = int(system(command));
|
||||
return status > 255 ? int(status / 256) : status;
|
||||
}
|
||||
|
||||
function command_success_from_args(args) {
|
||||
return command_status(command_from_args(args) + " >/dev/null 2>&1") == 0;
|
||||
}
|
||||
|
||||
function command_capture(command) {
|
||||
let pipe = fs.popen(command, "r");
|
||||
if (!pipe)
|
||||
return { status: 1, output: "" };
|
||||
|
||||
let data = pipe.read("all");
|
||||
let status = pipe.close();
|
||||
status = status > 255 ? int(status / 256) : status;
|
||||
return { status, output: data == null ? "" : as_string(data) };
|
||||
}
|
||||
|
||||
function settings() {
|
||||
return common.object_or_empty(uci_core.get_all(CONFIG_NAME, "telegram"));
|
||||
}
|
||||
|
||||
function get_proxy_args() {
|
||||
if (command_success_from_args(["pidof", "sing-box"])) {
|
||||
return [ "--proxy", "http://127.0.0.1:4534" ];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function tg_request(token, method, payload) {
|
||||
if (!token) return null;
|
||||
let url = "https://api.telegram.org/bot" + token + "/" + method;
|
||||
let payload_path = "/tmp/tg_payload_" + method + ".json";
|
||||
|
||||
fs.writefile(payload_path, json(payload));
|
||||
|
||||
let args = [ "curl", "-s", "-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);
|
||||
|
||||
let res = command_capture(command_from_args(args));
|
||||
fs.unlink(payload_path);
|
||||
|
||||
if (res.status != 0 || res.output == "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return json(res.output);
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function send_message(token, chat_id, text, parse_mode) {
|
||||
let payload = {
|
||||
chat_id: int(chat_id),
|
||||
text: text
|
||||
};
|
||||
if (parse_mode) {
|
||||
payload.parse_mode = parse_mode;
|
||||
}
|
||||
return tg_request(token, "sendMessage", payload);
|
||||
}
|
||||
|
||||
function is_admin(chat_id, admin_ids_str) {
|
||||
if (!admin_ids_str) return false;
|
||||
let admins = split(admin_ids_str, /,/);
|
||||
for (let admin in admins) {
|
||||
if (trim(admin) == as_string(chat_id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_system_status() {
|
||||
let status_obj = {};
|
||||
|
||||
// CPU
|
||||
let stat = fs.readfile("/proc/loadavg") || "";
|
||||
let load = split(stat, / /);
|
||||
status_obj.cpu = (length(load) > 0) ? load[0] : "unknown";
|
||||
|
||||
// RAM
|
||||
let mem = fs.readfile("/proc/meminfo") || "";
|
||||
let total_kb = 0, avail_kb = 0;
|
||||
for (let line in split(mem, "\n")) {
|
||||
if (index(line, "MemTotal:") == 0) {
|
||||
total_kb = int(split(trim(line), /[ \t]+/)[1]);
|
||||
}
|
||||
else if (index(line, "MemAvailable:") == 0) {
|
||||
avail_kb = int(split(trim(line), /[ \t]+/)[1]);
|
||||
}
|
||||
}
|
||||
status_obj.ram_total = total_kb ? int(total_kb / 1024) : 0;
|
||||
status_obj.ram_avail = avail_kb ? int(avail_kb / 1024) : 0;
|
||||
|
||||
// Uptime
|
||||
let upt = fs.readfile("/proc/uptime") || "";
|
||||
let up_sec = double(split(upt, / /)[0] || 0);
|
||||
let d = int(up_sec / 86400);
|
||||
let h = int((up_sec - (d * 86400)) / 3600);
|
||||
let m = int((up_sec - (d * 86400) - (h * 3600)) / 60);
|
||||
status_obj.uptime = sprintf("%d дн. %02d:%02d", d, h, m);
|
||||
|
||||
// sing-box status
|
||||
let sb_running = command_success_from_args(["pidof", "sing-box"]);
|
||||
status_obj.singbox = sb_running ? "🟢 running" : "🔴 stopped";
|
||||
|
||||
return status_obj;
|
||||
}
|
||||
|
||||
function process_command(token, chat_id, text) {
|
||||
let cmd = trim(as_string(text));
|
||||
if (cmd == "/start" || cmd == "/help") {
|
||||
let help_text = "🤖 *Tachyon Bot* — Панель управления роутером\n\n" +
|
||||
"Доступные команды:\n" +
|
||||
"⚡ /status — Статус системы и RAM/CPU\n" +
|
||||
"🩺 /doctor — Диагностика и ремонт ошибок\n" +
|
||||
"🔄 /restart — Перезапустить Tachyon\n" +
|
||||
"📋 /logs — Посмотреть последние логи\n";
|
||||
send_message(token, chat_id, help_text, "Markdown");
|
||||
}
|
||||
else if (cmd == "/status") {
|
||||
let sys = get_system_status();
|
||||
let status_text = "📊 *Статус Tachyon роутера*\n\n" +
|
||||
"Uptime: `" + sys.uptime + "`\n" +
|
||||
"CPU Load: `" + sys.cpu + "`\n" +
|
||||
"RAM: `" + sys.ram_avail + "MB / " + sys.ram_total + "MB` свободного\n" +
|
||||
"sing-box: `" + sys.singbox + "`";
|
||||
send_message(token, chat_id, status_text, "Markdown");
|
||||
}
|
||||
else if (cmd == "/doctor") {
|
||||
send_message(token, chat_id, "⏳ *Запуск диагностики и авто-исправления...*");
|
||||
let doctor_res = command_capture("/usr/bin/tachyon doctor");
|
||||
let report = doctor_res.output || "Нет вывода диагностики.";
|
||||
send_message(token, chat_id, "🩺 *Результаты Tachyon Doctor:*\n\n```\n" + report + "\n```", "Markdown");
|
||||
}
|
||||
else if (cmd == "/restart") {
|
||||
send_message(token, chat_id, "🔄 *Перезапускаю службы Tachyon...*");
|
||||
let restart_status = command_status("/usr/bin/tachyon restart");
|
||||
if (restart_status == 0) {
|
||||
send_message(token, chat_id, "✅ *Перезапуск выполнен успешно!*");
|
||||
} else {
|
||||
send_message(token, chat_id, "❌ *Ошибка при перезапуске служб.*");
|
||||
}
|
||||
}
|
||||
else if (cmd == "/logs") {
|
||||
let logs_res = command_capture("logread -e tachyon | tail -n 25");
|
||||
let logs = logs_res.output || "Логи отсутствуют.";
|
||||
send_message(token, chat_id, "📋 *Последние логи Tachyon:*\n\n```\n" + logs + "\n```", "Markdown");
|
||||
}
|
||||
else {
|
||||
send_message(token, chat_id, "⚠️ Неизвестная команда. Введите /help для списка команд.");
|
||||
}
|
||||
}
|
||||
|
||||
function process_updates(token, admin_ids) {
|
||||
let offset = int(trim(fs.readfile(OFFSET_FILE) || "0"));
|
||||
let res = tg_request(token, "getUpdates", { offset: offset, timeout: 20 });
|
||||
|
||||
if (!res || !res.ok || !res.result || length(res.result) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let upd in res.result) {
|
||||
let update_id = upd.update_id;
|
||||
if (update_id >= offset) {
|
||||
offset = update_id + 1;
|
||||
fs.writefile(OFFSET_FILE, as_string(offset));
|
||||
}
|
||||
|
||||
let msg = upd.message;
|
||||
if (!msg || !msg.text) continue;
|
||||
|
||||
let chat_id = msg.chat.id;
|
||||
if (!is_admin(chat_id, admin_ids)) {
|
||||
send_message(token, chat_id, "❌ Доступ запрещен. Ваш Chat ID: `" + chat_id + "`", "Markdown");
|
||||
continue;
|
||||
}
|
||||
|
||||
process_command(token, chat_id, msg.text);
|
||||
}
|
||||
}
|
||||
|
||||
function worker() {
|
||||
let cfg = settings();
|
||||
if (cfg.enabled != "1" || !cfg.bot_token || !cfg.admin_ids) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let poll_interval = int(cfg.poll_interval || "5");
|
||||
if (poll_interval < 1) poll_interval = 1;
|
||||
|
||||
while (true) {
|
||||
cfg = settings();
|
||||
if (cfg.enabled != "1") {
|
||||
break;
|
||||
}
|
||||
|
||||
process_updates(cfg.bot_token, cfg.admin_ids);
|
||||
sleep(poll_interval * 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function start_runtime() {
|
||||
let cfg = settings();
|
||||
stop_runtime();
|
||||
|
||||
if (cfg.enabled != "1" || !cfg.bot_token || !cfg.admin_ids) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let command = command_from_args([ "ucode", "-L", LIB_DIR, LIB_DIR + "/service/telegram.uc", "worker" ]) +
|
||||
" >/dev/null 2>&1 & echo $! >" + shell_quote(PID_FILE);
|
||||
return command_status(command);
|
||||
}
|
||||
|
||||
function stop_runtime() {
|
||||
let pid = trim(fs.readfile(PID_FILE) || "");
|
||||
if (pid != "" && match(pid, /^[0-9]+$/) != null && command_success_from_args([ "kill", "-0", pid ])) {
|
||||
command_success_from_args([ "kill", pid ]);
|
||||
}
|
||||
fs.unlink(PID_FILE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function get_status() {
|
||||
let pid = trim(fs.readfile(PID_FILE) || "");
|
||||
if (pid != "" && match(pid, /^[0-9]+$/) != null && command_success_from_args([ "kill", "-0", pid ])) {
|
||||
print("running (pid " + pid + ")\n");
|
||||
return 0;
|
||||
}
|
||||
print("stopped\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
function send_api(message) {
|
||||
let cfg = settings();
|
||||
if (cfg.enabled != "1" || !cfg.bot_token || !cfg.admin_ids) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let admins = split(cfg.admin_ids, /,/);
|
||||
for (let admin in admins) {
|
||||
let chat_id = trim(admin);
|
||||
if (chat_id != "") {
|
||||
send_message(cfg.bot_token, chat_id, message, "Markdown");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mode = (ARGV[0] == "") ? ARGV[1] : ARGV[0];
|
||||
if (!mode) mode = "";
|
||||
|
||||
if (mode == "start-runtime") {
|
||||
exit(start_runtime());
|
||||
}
|
||||
else if (mode == "stop-runtime") {
|
||||
exit(stop_runtime());
|
||||
}
|
||||
else if (mode == "worker") {
|
||||
exit(worker());
|
||||
}
|
||||
else if (mode == "status") {
|
||||
exit(get_status());
|
||||
}
|
||||
else if (mode == "send") {
|
||||
let msg = (ARGV[1] == "") ? ARGV[2] : ARGV[1];
|
||||
exit(send_api(msg));
|
||||
}
|
||||
else {
|
||||
warn("Usage: service/telegram.uc <start-runtime|stop-runtime|worker|status|send> ...\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -53,6 +53,13 @@ function log_message(message, level) {
|
||||
command_success_from_args([ "logger", "-t", "tachyon", "[" + as_string(level || "info") + "] Watchdog: " + as_string(message) ]);
|
||||
}
|
||||
|
||||
function send_telegram_notification(message) {
|
||||
let tcfg = common.object_or_empty(uci_core.get_all(CONFIG_NAME, "telegram"));
|
||||
if (tcfg.enabled == "1" && tcfg.bot_token && tcfg.admin_ids) {
|
||||
system("/usr/bin/tachyon telegram send " + shell_quote(message) + " >/dev/null 2>&1 &");
|
||||
}
|
||||
}
|
||||
|
||||
function parse_json_or_null(str) {
|
||||
try {
|
||||
return json(str);
|
||||
@@ -293,6 +300,10 @@ function worker() {
|
||||
let pid = (length(pids) > 0 && pids[0] != "") ? pids[0] : "";
|
||||
if (pid == "" && has_sections) {
|
||||
log_message("sing-box is stopped. Restarting Tachyon...", "warn");
|
||||
let tcfg = common.object_or_empty(uci_core.get_all(CONFIG_NAME, "telegram"));
|
||||
if (tcfg.notify_crash != "0") {
|
||||
send_telegram_notification("⚠️ *Watchdog:* sing-box остановлен. Перезапускаю службы Tachyon...");
|
||||
}
|
||||
command_status("/usr/bin/tachyon restart >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
@@ -302,6 +313,10 @@ function worker() {
|
||||
let out_nft = command_output_from_args(["nft", "list", "table", "inet", nft_table]);
|
||||
if (index(out_nft, "tproxy") < 0) {
|
||||
log_message("nftables rules are missing or corrupted. Rebuilding...", "warn");
|
||||
let tcfg = common.object_or_empty(uci_core.get_all(CONFIG_NAME, "telegram"));
|
||||
if (tcfg.notify_crash != "0") {
|
||||
send_telegram_notification("⚠️ *Watchdog:* правила nftables повреждены или отсутствуют. Выполняю пересборку...");
|
||||
}
|
||||
command_status("/usr/bin/tachyon restart >/dev/null 2>&1");
|
||||
}
|
||||
}
|
||||
@@ -327,6 +342,7 @@ function worker() {
|
||||
let logread_lower = lc(logread_out);
|
||||
if (index(logread_lower, "out of memory") >= 0 || index(logread_lower, "oom-killer") >= 0) {
|
||||
log_message("OOM event detected! Reducing GOMEMLIMIT scaling...", "err");
|
||||
send_telegram_notification("🚨 *Watchdog:* Обнаружено событие OOM (Out Of Memory)! Уменьшаю GOMEMLIMIT и перезапускаю службы...");
|
||||
let scale = 1.0;
|
||||
let scale_path = "/etc/tachyon/mem_scale";
|
||||
let scale_data = fs.readfile(scale_path);
|
||||
|
||||
Reference in New Issue
Block a user