Update ZapretManager_LuCI.sh

This commit is contained in:
StressOzz authored and GitHub committed 2026-10-10 16:02:49 +05:00
1 parent 1bea7c5072
commit 6366651bc0
1 file changed
+109 -29
+109 -29
View File
@@ -1,10 +1,10 @@
#!/bin/sh #!/bin/sh
# Version: 2.67 # Version: 2.68
set -e set -e
GREEN="\033[1;32m"; CYAN="\033[1;36m"; YELLOW="\033[1;33m"; MAGENTA="\033[1;35m"; BLUE="\033[0;34m"; NC="\033[0m"; DGRAY="\033[38;5;244m" GREEN="\033[1;32m"; CYAN="\033[1;36m"; YELLOW="\033[1;33m"; MAGENTA="\033[1;35m"; BLUE="\033[0;34m"; NC="\033[0m"; DGRAY="\033[38;5;244m"
ZM_NEW_VER="2.67" ZM_NEW_VER="2.68"
_zmi_say() { echo -e "${CYAN}==>${NC} $*"; } _zmi_say() { echo -e "${CYAN}==>${NC} $*"; }
_zmi_ok() { echo -e " ${GREEN}✓${NC} $*"; } _zmi_ok() { echo -e " ${GREEN}✓${NC} $*"; }
_zmi_step() { echo -e " → $*"; } _zmi_step() { echo -e " → $*"; }
@@ -129,7 +129,7 @@ if [ "$1" = zm_watch ]; then
fi fi
CONF="/etc/config/zapret" CONF="/etc/config/zapret"
ZM_VERSION="2.67" ZM_VERSION="2.68"
ZM_SCRIPT_URL="https://raw.githubusercontent.com/StressOzz/Zapret-Manager/refs/heads/main/ZapretManager_LuCI.sh" ZM_SCRIPT_URL="https://raw.githubusercontent.com/StressOzz/Zapret-Manager/refs/heads/main/ZapretManager_LuCI.sh"
GH_RAW="https://raw.githubusercontent.com" GH_RAW="https://raw.githubusercontent.com"
GH_MAIN="https://github.com" GH_MAIN="https://github.com"
@@ -8322,6 +8322,8 @@ doh_set() {
RB_SHARE="/usr/share/zm-redbtn" RB_SHARE="/usr/share/zm-redbtn"
ZM_LISTS_DIR="/opt/zapret-manager-luci/lists" ZM_LISTS_DIR="/opt/zapret-manager-luci/lists"
ZM_LISTS_ITD="${GH_RAW}/itdoginfo/allow-domains/main" ZM_LISTS_ITD="${GH_RAW}/itdoginfo/allow-domains/main"
# Meta, Instagram, Facebook и WhatsApp — списки v2fly (и itdoginfo); те же ссылки берёт Forkozz (PKG_LISTS в netshift.uc)
ZM_LISTS_V2F="${GH_RAW}/v2fly/domain-list-community/master/data"
_rb_list_src() { _rb_list_src() {
case "$1" in case "$1" in
@@ -8329,6 +8331,14 @@ _rb_list_src() {
svc_telegram.lst) echo "$ZM_LISTS_ITD/Services/telegram.lst" ;; svc_telegram.lst) echo "$ZM_LISTS_ITD/Services/telegram.lst" ;;
svc_discord.lst) echo "$ZM_LISTS_ITD/Services/discord.lst" ;; svc_discord.lst) echo "$ZM_LISTS_ITD/Services/discord.lst" ;;
svc_meta.lst) echo "$ZM_LISTS_ITD/Services/meta.lst" ;; svc_meta.lst) echo "$ZM_LISTS_ITD/Services/meta.lst" ;;
svc_instagram.lst) echo "$ZM_LISTS_V2F/instagram" ;;
svc_facebook.lst) echo "$ZM_LISTS_V2F/facebook" ;;
svc_messenger.lst) echo "$ZM_LISTS_V2F/messenger" ;;
svc_whatsapp.lst) echo "$ZM_LISTS_V2F/whatsapp" ;;
svc_v2meta.lst) echo "$ZM_LISTS_V2F/meta" ;;
svc_threads.lst) echo "$ZM_LISTS_V2F/threads" ;;
svc_oculus.lst) echo "$ZM_LISTS_V2F/oculus" ;;
svc_facebook_dev.lst) echo "$ZM_LISTS_V2F/facebook-dev" ;;
svc_twitter.lst) echo "$ZM_LISTS_ITD/Services/twitter.lst" ;; svc_twitter.lst) echo "$ZM_LISTS_ITD/Services/twitter.lst" ;;
telegram.lst) echo "$ZM_LISTS_ITD/Subnets/IPv4/telegram.lst" ;; telegram.lst) echo "$ZM_LISTS_ITD/Subnets/IPv4/telegram.lst" ;;
discord.lst) echo "$ZM_LISTS_ITD/Subnets/IPv4/discord.lst" ;; discord.lst) echo "$ZM_LISTS_ITD/Subnets/IPv4/discord.lst" ;;
@@ -9247,16 +9257,20 @@ _st_sel_remap() {
rm -f "$f.$$.tmp" rm -f "$f.$$.tmp"
done done
} }
# Instagram и WhatsApp объединены в один пункт Meta (у них и так был общий список meta) — переносим старый выбор. # Meta включает Instagram, Facebook и WhatsApp: выбранная Meta (и Meta из каталога) — это все четыре пункта.
_st_sel_meta() { _st_sel_meta() {
local f moved="" local f id
for f in "$ST_SEL" "$ST_SKIP"; do for f in "$ST_SEL" "$ST_SKIP"; do
grep -qxE 'instagram|whatsapp' "$f" 2>/dev/null || continue grep -qxE 'meta|c_itdoginfo_meta' "$f" 2>/dev/null || continue
awk '{ k = ($0 == "instagram" || $0 == "whatsapp") ? "meta" : $0; if (k != "" && !(k in s)) { s[k] = 1; print k } }' "$f" > "$f.$$.tmp" && mv -f "$f.$$.tmp" "$f" awk -v kids="$([ "$f" = "$ST_SEL" ] && echo 1)" 'function put(k) { if (k != "" && !(k in s)) { s[k] = 1; print k } }
{ if ($0 == "meta" || $0 == "c_itdoginfo_meta") { put("meta"); if (kids) { put("instagram"); put("facebook"); put("whatsapp") } } else put($0) }' "$f" > "$f.$$.tmp" &&
{ if cmp -s "$f.$$.tmp" "$f"; then rm -f "$f.$$.tmp"; else mv -f "$f.$$.tmp" "$f"; fi; }
rm -f "$f.$$.tmp" rm -f "$f.$$.tmp"
moved=1
done done
[ -n "$moved" ] && _rb_in meta "$ST_SEL" && [ -f "$ST_SKIP" ] && { sed -i '/^meta$/d' "$ST_SKIP"; [ -s "$ST_SKIP" ] || rm -f "$ST_SKIP"; } if [ -s "$ST_SKIP" ] && [ -s "$ST_SEL" ]; then
for id in meta instagram facebook whatsapp; do _rb_in "$id" "$ST_SEL" && _rb_in "$id" "$ST_SKIP" && sed -i "/^$id\$/d" "$ST_SKIP"; done
[ -s "$ST_SKIP" ] || rm -f "$ST_SKIP"
fi
return 0 return 0
} }
_st_migrate _st_migrate
@@ -10481,7 +10495,7 @@ _st_tgws_warp() {
} }
ST_LISTS_MANIFEST="https://github.com/xyzmean/splify2-lists/releases/latest/download/lists.json" ST_LISTS_MANIFEST="https://github.com/xyzmean/splify2-lists/releases/latest/download/lists.json"
ST_CAT_SKIP=" mydyson " ST_CAT_SKIP=" mydyson itdoginfo_meta "
ST_SRS_FALLBACK="https://github.com/itdoginfo/allow-domains/releases/latest/download" ST_SRS_FALLBACK="https://github.com/itdoginfo/allow-domains/releases/latest/download"
_st_fetch() { _st_fetch() {
@@ -10792,7 +10806,11 @@ _st_model() {
echo echo
fi fi
_st_dev_model _st_dev_model
for id; do _st_svc_lists "$id"; done for id; do
# Meta уже содержит списки Instagram, Facebook и WhatsApp — отдельно их не добавляем
case "$id" in instagram|facebook|whatsapp) case " $* " in *" meta "*) continue ;; esac ;; esac
_st_svc_lists "$id"
done
} }
_st_spec_apply() { _st_spec_apply() {
@@ -14795,7 +14813,7 @@ _fk_ip_clean() {
_fk_fast_patch() { _fk_fast_patch() {
local f=/usr/lib/netshift/rulesets.sh n local f=/usr/lib/netshift/rulesets.sh n
[ -f "$f" ] && [ -f /usr/lib/netshift/nft.sh ] && [ -f /usr/lib/netshift/helpers.sh ] || return 0 [ -f "$f" ] && [ -f /usr/lib/netshift/nft.sh ] && [ -f /usr/lib/netshift/helpers.sh ] || return 0
grep -q '^# zm-fast-begin 1$' "$f" && return 0 grep -q '^# zm-fast-begin 2$' "$f" && return 0
for n in patch_source_ruleset_rules import_plain_domain_list_to_local_source_ruleset_chunked import_plain_subnet_list_to_local_source_ruleset_chunked extract_ip_cidr_from_json_ruleset_to_file; do for n in patch_source_ruleset_rules import_plain_domain_list_to_local_source_ruleset_chunked import_plain_subnet_list_to_local_source_ruleset_chunked extract_ip_cidr_from_json_ruleset_to_file; do
grep -q "^$n() {" "$f" || return 0 grep -q "^$n() {" "$f" || return 0
done done
@@ -14803,7 +14821,7 @@ _fk_fast_patch() {
grep -q '^comma_string_to_json_array() {' /usr/lib/netshift/helpers.sh || return 0 grep -q '^comma_string_to_json_array() {' /usr/lib/netshift/helpers.sh || return 0
grep -q '^# zm-fast-begin' "$f" && sed -i '/^# zm-fast-begin/,/^# zm-fast-end$/d' "$f" grep -q '^# zm-fast-begin' "$f" && sed -i '/^# zm-fast-begin/,/^# zm-fast-end$/d' "$f"
cat >> "$f" << 'ZM_FK_FAST_EOF' cat >> "$f" << 'ZM_FK_FAST_EOF'
# zm-fast-begin 1 # zm-fast-begin 2
_zm_fast_subnets() { _zm_fast_subnets() {
awk -v n="$2" '{ gsub(/\r/, ""); sub(/^[ \t]+/, ""); sub(/[ \t]+$/, ""); if ($0 == "") next awk -v n="$2" '{ gsub(/\r/, ""); sub(/^[ \t]+/, ""); sub(/[ \t]+$/, ""); if ($0 == "") next
m = split($0, p, "/"); if (m > 2) next m = split($0, p, "/"); if (m > 2) next
@@ -14818,7 +14836,7 @@ _zm_fast_subnets() {
_zm_fast_domains() { _zm_fast_domains() {
awk -v n="$2" '{ gsub(/\r/, ""); sub(/^[ \t]+/, ""); sub(/[ \t]+$/, ""); if ($0 == "") next awk -v n="$2" '{ gsub(/\r/, ""); sub(/^[ \t]+/, ""); sub(/[ \t]+$/, ""); if ($0 == "") next
d = tolower($0); t = d; sub(/^\./, "", t) d = tolower($0); sub(/^(full|domain|suffix):/, "", d); t = d; sub(/^\./, "", t)
if (t !~ /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/) next if (t !~ /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/) next
if (t ~ /^[0-9.]+$/ || (d in seen)) next if (t ~ /^[0-9.]+$/ || (d in seen)) next
seen[d] = 1; buf = buf (c ? "," : "") d seen[d] = 1; buf = buf (c ? "," : "") d
@@ -15847,6 +15865,7 @@ _fk_dns_probe() {
doh) doh)
case "$v" in case "$v" in
https://*) url="$v"; case "${url#https://}" in */*) ;; *) url="$url/dns-query" ;; esac ;; https://*) url="$v"; case "${url#https://}" in */*) ;; *) url="$url/dns-query" ;; esac ;;
*/*) url="https://$v" ;;
*) url="https://$v/dns-query" ;; *) url="https://$v/dns-query" ;;
esac esac
case "$url" in *\?*) url="$url&dns=$FK_DNS_Q" ;; *) url="$url?dns=$FK_DNS_Q" ;; esac case "$url" in *\?*) url="$url&dns=$FK_DNS_Q" ;; *) url="$url?dns=$FK_DNS_Q" ;; esac
@@ -16405,7 +16424,21 @@ const ITD_RAW = "https://raw.githubusercontent.com/itdoginfo/allow-domains/main"
const SRS_MAIN = "https://github.com/itdoginfo/allow-domains/releases/latest/download"; const SRS_MAIN = "https://github.com/itdoginfo/allow-domains/releases/latest/download";
const ITD_SUBNETS = { cloudflare: true, cloudfront: true, digitalocean: true, discord: true, google_meet: true, hetzner: true, const ITD_SUBNETS = { cloudflare: true, cloudfront: true, digitalocean: true, discord: true, google_meet: true, hetzner: true,
meta: true, ovh: true, roblox: true, telegram: true, twitter: true }; meta: true, ovh: true, roblox: true, telegram: true, twitter: true };
const CAT_SKIP = { mydyson: true }; const CAT_SKIP = { mydyson: true, itdoginfo_meta: true };
/* Списки пакета без набора в каталоге — те же ссылки, что качает Steer (_rb_list_src): Meta, Instagram, Facebook, WhatsApp. */
const V2F_RAW = "https://raw.githubusercontent.com/v2fly/domain-list-community/master/data";
const PKG_LISTS = {
"svc_meta.lst": ITD_RAW + "/Services/meta.lst",
"svc_v2meta.lst": V2F_RAW + "/meta",
"svc_threads.lst": V2F_RAW + "/threads",
"svc_oculus.lst": V2F_RAW + "/oculus",
"svc_facebook_dev.lst": V2F_RAW + "/facebook-dev",
"svc_instagram.lst": V2F_RAW + "/instagram",
"svc_facebook.lst": V2F_RAW + "/facebook",
"svc_messenger.lst": V2F_RAW + "/messenger",
"svc_whatsapp.lst": V2F_RAW + "/whatsapp",
"meta.lst": ITD_RAW + "/Subnets/IPv4/meta.lst"
};
const SCHEMES = /^(vless|vmess|trojan|ss|socks4|socks4a|socks5|hysteria2|hy2):\/\/[^ \t\r\n]+$/i; const SCHEMES = /^(vless|vmess|trojan|ss|socks4|socks4a|socks5|hysteria2|hy2):\/\/[^ \t\r\n]+$/i;
const BYPASS = "zm_bypass"; const BYPASS = "zm_bypass";
const SUB_IVS = [ "30m", "1h", "3h", "6h", "12h", "1d" ]; const SUB_IVS = [ "30m", "1h", "3h", "6h", "12h", "1d" ];
@@ -16555,6 +16588,10 @@ function catalog_items(known_list) {
if (!id || id == "custom" || seen[id]) continue; if (!id || id == "custom" || seen[id]) continue;
if (s(f[2]) + s(f[3]) + s(f[7]) == "") continue; if (s(f[2]) + s(f[3]) + s(f[7]) == "") continue;
if (id == "github") t = set_tokens("github", known); if (id == "github") t = set_tokens("github", known);
else if (s(f[7]) == "") {
for (let fl in [ ...split(s(f[2]), ","), ...split(s(f[3]), ",") ])
if (PKG_LISTS[fl]) push(t, "l:" + PKG_LISTS[fl]);
}
else for (let set in split(s(f[7]), ",")) { else for (let set in split(s(f[7]), ",")) {
if (set == "") continue; if (set == "") continue;
t = [ ...t, ...set_tokens(set, known) ]; t = [ ...t, ...set_tokens(set, known) ];
@@ -16913,11 +16950,13 @@ function dns_value_ok(t, v) {
if (t == "udp") return valid_ip(replace(v, /:[0-9]+$/, "")) && index(v, "/") < 0; if (t == "udp") return valid_ip(replace(v, /:[0-9]+$/, "")) && index(v, "/") < 0;
if (t == "dot") return !match(v, /^[a-z]+:\/\//i) && index(v, "/") < 0 && !!match(v, /^[A-Za-z0-9.-]+(:[0-9]+)?$/) && !!match(v, /\./); if (t == "dot") return !match(v, /^[a-z]+:\/\//i) && index(v, "/") < 0 && !!match(v, /^[A-Za-z0-9.-]+(:[0-9]+)?$/) && !!match(v, /\./);
if (match(v, /^https:\/\//i)) return !!match(v, /^https:\/\/[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:[0-9]+)?(\/[^ ]*)?$/i); if (match(v, /^https:\/\//i)) return !!match(v, /^https:\/\/[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:[0-9]+)?(\/[^ ]*)?$/i);
return !!match(v, /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:[0-9]+)?$/); return !!match(v, /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:[0-9]+)?(\/[^ ]*)?$/);
} }
/* DoH: голый адрес сервера (dns.example.com) — дописываем стандартный /dns-query; адрес со своим путём
* (dns.example.com/abc или dns.example.com/) и ссылку https://… берём как есть, путь не трогаем. */
function dns_norm(t, v) { function dns_norm(t, v) {
if (t == "doh" && !match(v, /^https:\/\//i)) return "https://" + v + "/dns-query"; if (t == "doh" && !match(v, /^https:\/\//i)) return index(v, "/") >= 0 ? "https://" + v : "https://" + v + "/dns-query";
return v; return v;
} }
@@ -17054,13 +17093,26 @@ function cmd_device_setup() {
} }
migrated = true; migrated = true;
} }
if (!v2) { /* Пункт Meta теперь из списков пакета (с Instagram, Facebook и WhatsApp): секции со старым списком meta получают их все. */
let split_meta = false;
for (let x in sections(c, "section")) {
let cl = uniq(words(x.community_lists));
if (index(cl, "meta") < 0) continue;
/* только дописываем: остальные списки секции и их порядок не трогаем */
let n = x[".name"], urls = map(keys(PKG_LISTS), (k) => PKG_LISTS[k]), zr = rawlist(x.zm_refs);
set_list(c, n, "community_lists", filter(cl, (v) => v != "meta"));
if (length(zr)) set_list(c, n, "zm_refs", uniq([ ...zr, ...map(urls, (u) => "l:" + u) ]));
set_list(c, n, "remote_domain_lists", uniq([ ...words(x.remote_domain_lists), ...urls ]));
set_list(c, n, "remote_subnet_lists", uniq([ ...words(x.remote_subnet_lists), ...urls ]));
split_meta = true;
}
if (!v2 || split_meta) {
if (!c.get(CFG, "settings")) c.set(CFG, "settings", "settings"); if (!c.get(CFG, "settings")) c.set(CFG, "settings", "settings");
c.set(CFG, "settings", "zm_dev_v2", "1"); c.set(CFG, "settings", "zm_dev_v2", "1");
c.save(CFG); c.save(CFG);
c.commit(CFG); c.commit(CFG);
} }
out({ ok: true, changed: patched || migrated, migrated }); out({ ok: true, changed: patched || migrated || split_meta, migrated });
} }
function set_text(c, sec, key, list) { function set_text(c, sec, key, list) {
@@ -17311,7 +17363,7 @@ function write_sec_ext(c, sec, mode, x) {
let t = s(x.dres_type), v = trim(s(x.dres_server)); let t = s(x.dres_type), v = trim(s(x.dres_server));
if (index([ "udp", "dot", "doh" ], t) < 0) fail("неизвестный тип DNS для туннеля"); if (index([ "udp", "dot", "doh" ], t) < 0) fail("неизвестный тип DNS для туннеля");
if (!dns_value_ok(t, v)) if (!dns_value_ok(t, v))
fail(t == "udp" ? "DNS туннеля: нужен IP-адрес, например 1.1.1.1" : t == "dot" ? "DNS туннеля: нужен адрес без https://, например one.one.one.one" : "DNS туннеля: нужна ссылка https://…/dns-query или адрес сервера"); fail(t == "udp" ? "DNS туннеля: нужен IP-адрес, например 1.1.1.1" : t == "dot" ? "DNS туннеля: нужен адрес без https://, например one.one.one.one" : "DNS туннеля: нужен адрес сервера или ссылка https://…");
c.set(CFG, sec, "domain_resolver_enabled", "1"); c.set(CFG, sec, "domain_resolver_enabled", "1");
c.set(CFG, sec, "domain_resolver_dns_type", t); c.set(CFG, sec, "domain_resolver_dns_type", t);
c.set(CFG, sec, "domain_resolver_dns_server", dns_norm(t, v)); c.set(CFG, sec, "domain_resolver_dns_server", dns_norm(t, v));
@@ -17635,7 +17687,7 @@ function cmd_set() {
if (!dns_value_ok(t, x)) if (!dns_value_ok(t, x))
fail(t == "udp" ? "для UDP нужен IP-адрес DNS, например 9.9.9.9 (ошибка в «" + x + "»)" fail(t == "udp" ? "для UDP нужен IP-адрес DNS, например 9.9.9.9 (ошибка в «" + x + "»)"
: t == "dot" ? "для DoT нужен адрес сервера без https://, например dns.quad9.net (ошибка в «" + x + "»)" : t == "dot" ? "для DoT нужен адрес сервера без https://, например dns.quad9.net (ошибка в «" + x + "»)"
: "для DoH нужен адрес или ссылка https://…/dns-query (ошибка в «" + x + "»)"); : "для DoH нужен адрес сервера или ссылка https://… (ошибка в «" + x + "»)");
push(svs, dns_norm(t, x)); push(svs, dns_norm(t, x));
} }
svs = uniq(svs); svs = uniq(svs);
@@ -20099,7 +20151,7 @@ function notifyStrategyResult(res, okLabel) {
var SVC_BRANDS = [ var SVC_BRANDS = [
[ /youtube/i, '#ff0033', 'YT' ], [ /discord/i, '#5865f2', 'DC' ], [ /telegram/i, '#229ed9', 'TG' ], [ /youtube/i, '#ff0033', 'YT' ], [ /discord/i, '#5865f2', 'DC' ], [ /telegram/i, '#229ed9', 'TG' ],
[ /^meta$/i, '#0866ff', 'M' ], [ /whatsapp/i, '#25d366', 'WA' ], [ /instagram|facebook|\bmeta\b/i, '#e1306c', 'IG' ], [ /twitter|^x\b/i, '#16181c', 'X' ], [ /^meta$/i, '#0866ff', 'M' ], [ /whatsapp/i, '#25d366', 'WA' ], [ /instagram|\bmeta\b/i, '#e1306c', 'IG' ], [ /facebook/i, '#1877f2', 'FB' ], [ /twitter|^x\b/i, '#16181c', 'X' ],
[ /tiktok/i, '#fe2c55', 'TT' ], [ /google play/i, '#01875f', 'GP' ], [ /gemini|google ai/i, '#4285f4', 'AI' ], [ /tiktok/i, '#fe2c55', 'TT' ], [ /google play/i, '#01875f', 'GP' ], [ /gemini|google ai/i, '#4285f4', 'AI' ],
[ /chatgpt|openai|claude|\bai\b|(^|[^а-яё])ии([^а-яё]|$)/i, '#10a37f', 'AI' ], [ /githubusercontent|github raw/i, '#57606a', 'GR' ], [ /github/i, '#24292f', 'GH' ], [ /chatgpt|openai|claude|\bai\b|(^|[^а-яё])ии([^а-яё]|$)/i, '#10a37f', 'AI' ], [ /githubusercontent|github raw/i, '#57606a', 'GR' ], [ /github/i, '#24292f', 'GH' ],
[ /google/i, '#4285f4', 'G' ], [ /roblox/i, '#e2231a', 'RB' ], [ /supercell|brawl|clash/i, '#f59e0b', 'SC' ], [ /google/i, '#4285f4', 'G' ], [ /roblox/i, '#e2231a', 'RB' ], [ /supercell|brawl|clash/i, '#f59e0b', 'SC' ],
@@ -20131,7 +20183,22 @@ function svcTx(v) {
return (typeof v === 'string' || typeof v === 'number') ? [ String(v) ] : v; return (typeof v === 'string' || typeof v === 'number') ? [ String(v) ] : v;
} }
var RI_IDS = { geoblock: 1, block: 1, news: 1, anime: 1, porn: 1, youtube: 1, discord: 1, meta: 1, instagram: 1, whatsapp: 1, twitter: 1, x: 1, tiktok: 1, hdrezka: 1 }; /* Meta включает Instagram, Facebook и WhatsApp: Meta включает и выключает их вместе с собой,
* а снятый с Meta отдельный пункт снимает и саму Meta. */
var META_KIDS = [ 'instagram', 'facebook', 'whatsapp' ];
function svcPickToggle(p, id) {
if (p[id]) {
delete p[id];
if (id === 'meta') META_KIDS.forEach(function(k) { delete p[k]; });
else if (META_KIDS.indexOf(id) >= 0) delete p.meta;
} else {
p[id] = true;
if (id === 'meta') META_KIDS.forEach(function(k) { p[k] = true; });
}
return p;
}
var RI_IDS = { geoblock: 1, block: 1, news: 1, anime: 1, porn: 1, youtube: 1, discord: 1, meta: 1, instagram: 1, facebook: 1, whatsapp: 1, twitter: 1, x: 1, tiktok: 1, hdrezka: 1 };
function riCovers(id) { function riCovers(id) {
return !!RI_IDS[String(id || '').replace(/^c_itdoginfo_/, '')]; return !!RI_IDS[String(id || '').replace(/^c_itdoginfo_/, '')];
@@ -20942,6 +21009,7 @@ return baseclass.extend({
jobsCancel: callJobsCancel, jobsCancel: callJobsCancel,
dock: dock, dock: dock,
riCovers: riCovers, riCovers: riCovers,
svcPickToggle: svcPickToggle,
riNote: riNote, riNote: riNote,
swRow: swRow, swRow: swRow,
dialog: dialog, dialog: dialog,
@@ -22905,7 +22973,7 @@ return view.extend({
if (busy) { zm.toast('Дождитесь окончания текущей операции', 'warning'); return; } if (busy) { zm.toast('Дождитесь окончания текущей операции', 'warning'); return; }
pick = {}; pick = {};
for (var k in sel) if (sel[k] && k !== 'custom') pick[k] = true; for (var k in sel) if (sel[k] && k !== 'custom') pick[k] = true;
if (pick[s.id]) delete pick[s.id]; else pick[s.id] = true; zm.svcPickToggle(pick, s.id);
renderLists(); renderLists();
} }); } });
} }
@@ -23917,7 +23985,10 @@ mkdir -p /usr/share/zm-redbtn
cat > '/usr/share/zm-redbtn/services.conf' << 'ZM_INSTALLER_EOF' cat > '/usr/share/zm-redbtn/services.conf' << 'ZM_INSTALLER_EOF'
youtube|YouTube|svc_youtube.lst||www.youtube.com,youtubei.googleapis.com,manifest.googlevideo.com|youtu.be,i.ytimg.com,i9.ytimg.com,yt3.ggpht.com,yt4.ggpht.com,jnn-pa.googleapis.com,signaler-pa.youtube.com,yt3.googleusercontent.com,rr4---sn-4g5e6nze.googlevideo.com,rr4---sn-5go7yner.googlevideo.com,rr5---sn-n8v7knez.googlevideo.com,rr2---sn-q4fl6ndl.googlevideo.com,rr1---sn-q4fl6n6y.googlevideo.com,rr14---sn-n8v7kn7r.googlevideo.com,rr4---sn-jvhnu5g-c35d.googlevideo.com,rr1---sn-gvnuxaxjvh-jx3z.googlevideo.com,rr12---sn-gvnuxaxjvh-bvwz.googlevideo.com,rr1---sn-ug5onuxaxjvh-n8v6.googlevideo.com||youtube youtube|YouTube|svc_youtube.lst||www.youtube.com,youtubei.googleapis.com,manifest.googlevideo.com|youtu.be,i.ytimg.com,i9.ytimg.com,yt3.ggpht.com,yt4.ggpht.com,jnn-pa.googleapis.com,signaler-pa.youtube.com,yt3.googleusercontent.com,rr4---sn-4g5e6nze.googlevideo.com,rr4---sn-5go7yner.googlevideo.com,rr5---sn-n8v7knez.googlevideo.com,rr2---sn-q4fl6ndl.googlevideo.com,rr1---sn-q4fl6n6y.googlevideo.com,rr14---sn-n8v7kn7r.googlevideo.com,rr4---sn-jvhnu5g-c35d.googlevideo.com,rr1---sn-gvnuxaxjvh-jx3z.googlevideo.com,rr12---sn-gvnuxaxjvh-bvwz.googlevideo.com,rr1---sn-ug5onuxaxjvh-n8v6.googlevideo.com||youtube
discord|Discord|svc_discord.lst|discord.lst|discord.com,gateway.discord.gg,updates.discord.com|cdn.discordapp.com,media.discordapp.net||discord discord|Discord|svc_discord.lst|discord.lst|discord.com,gateway.discord.gg,updates.discord.com|cdn.discordapp.com,media.discordapp.net||discord
meta|Meta|svc_meta.lst|meta.lst|www.instagram.com,web.whatsapp.com|i.instagram.com,graph.instagram.com,scontent.cdninstagram.com,static.whatsapp.net,mmg.whatsapp.net||meta meta|Meta|svc_meta.lst,svc_v2meta.lst,svc_instagram.lst,svc_facebook.lst,svc_messenger.lst,svc_whatsapp.lst,svc_threads.lst,svc_oculus.lst,svc_facebook_dev.lst|meta.lst|www.instagram.com,www.facebook.com,web.whatsapp.com|i.instagram.com,graph.instagram.com,scontent.cdninstagram.com,static.xx.fbcdn.net,static.whatsapp.net,mmg.whatsapp.net||
instagram|Instagram|svc_instagram.lst,svc_facebook.lst|meta.lst|www.instagram.com|i.instagram.com,graph.instagram.com,scontent.cdninstagram.com||
facebook|Facebook|svc_facebook.lst,svc_messenger.lst|meta.lst|www.facebook.com|static.xx.fbcdn.net||
whatsapp|WhatsApp|svc_whatsapp.lst|meta.lst|web.whatsapp.com|static.whatsapp.net,mmg.whatsapp.net||
x|X (Twitter)|svc_twitter.lst|twitter_x.lst|x.com|twitter.com,abs.twimg.com,video.twimg.com||twitter x|X (Twitter)|svc_twitter.lst|twitter_x.lst|x.com|twitter.com,abs.twimg.com,video.twimg.com||twitter
github|GitHub|own_github.lst||github.com,raw.githubusercontent.com,objects.githubusercontent.com|codeload.github.com,api.github.com,ghcr.io|| github|GitHub|own_github.lst||github.com,raw.githubusercontent.com,objects.githubusercontent.com|codeload.github.com,api.github.com,ghcr.io||
telegram|Telegram|svc_telegram.lst|telegram.lst|web.telegram.org|t.me,core.telegram.org,telegra.ph||telegram telegram|Telegram|svc_telegram.lst|telegram.lst|web.telegram.org|t.me,core.telegram.org,telegra.ph||telegram
@@ -28422,15 +28493,24 @@ var DNS_TYPES = { udp: 'UDP', dot: 'DoT', doh: 'DoH' };
var DNS_HELP = { var DNS_HELP = {
udp: { ph: '9.9.9.9', hint: 'Обычный DNS без шифрования — только IP-адрес. Провайдер видит и может подменять ответы.' }, udp: { ph: '9.9.9.9', hint: 'Обычный DNS без шифрования — только IP-адрес. Провайдер видит и может подменять ответы.' },
dot: { ph: 'dns.quad9.net', hint: 'DNS over TLS: адрес сервера без https://, например dns.quad9.net или 9.9.9.9.' }, dot: { ph: 'dns.quad9.net', hint: 'DNS over TLS: адрес сервера без https://, например dns.quad9.net или 9.9.9.9.' },
doh: { ph: 'https://dns.quad9.net/dns-query', hint: 'DNS over HTTPS: ссылка https://…/dns-query или просто адрес сервера.' } doh: { ph: 'https://dns.quad9.net/dns-query', hint: 'DNS over HTTPS: ссылка https://… или адрес сервера. Голый адрес (dns.example.com) — допишем /dns-query; ссылка или адрес с путём берутся как есть, без пути — https://dns.example.com/.' }
}; };
var DNS_RE = { var DNS_RE = {
udp: /^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(:\d{1,5})?$/, udp: /^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(:\d{1,5})?$/,
dot: /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?$/, dot: /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?$/,
doh: /^(https:\/\/[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?(\/[^\s@|,;'"<>]*)?|[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?)$/i doh: /^(https:\/\/)?[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?(\/[^\s@|,;'"<>]*)?$/i
}; };
/* Свой DoH-сервер: голый адрес (dns.example.com) — стандартный путь /dns-query; ссылка https://сервер без пути —
* корень «/» (иначе sing-box сам подставит /dns-query); адрес со своим путём остаётся как есть. */
function dohOwn(v) {
if (/^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?$/.test(v)) return 'https://' + v + '/dns-query';
if (/^https:\/\/[^\/?#]+$/i.test(v)) return v + '/';
if (!/^https:\/\//i.test(v) && /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?\//.test(v)) return 'https://' + v;
return v;
}
function dnsFind(t, v) { function dnsFind(t, v) {
var list = DNS_CAT[t] || []; var list = DNS_CAT[t] || [];
for (var i = 0; i < list.length; i++) for (var i = 0; i < list.length; i++)
@@ -29949,7 +30029,7 @@ return view.extend({
return zm.svcCard({ name: s.name, key: s.id, on: !!draft.sel[s.id], sub: by ? 'и в секции «' + by + '» — сработает верхняя' : shadowed ? 'перекрыт «Всё сразу» секции «' + ra + '»' : '', inc: !!draft.sel.russia_inside && s.id !== 'russia_inside' && !s.group && zm.riCovers(s.id), click: function() { return zm.svcCard({ name: s.name, key: s.id, on: !!draft.sel[s.id], sub: by ? 'и в секции «' + by + '» — сработает верхняя' : shadowed ? 'перекрыт «Всё сразу» секции «' + ra + '»' : '', inc: !!draft.sel.russia_inside && s.id !== 'russia_inside' && !s.group && zm.riCovers(s.id), click: function() {
var nsel = {}; var nsel = {};
for (var k in draft.sel) if (draft.sel[k]) nsel[k] = true; for (var k in draft.sel) if (draft.sel[k]) nsel[k] = true;
if (nsel[s.id]) delete nsel[s.id]; else nsel[s.id] = true; zm.svcPickToggle(nsel, s.id);
set('sel', nsel); set('sel', nsel);
} }); } });
} }
@@ -30210,7 +30290,7 @@ return view.extend({
function addOwn() { function addOwn() {
var v = dnsInput.value.trim(); var v = dnsInput.value.trim();
if (!v) return; if (!v) return;
if (t === 'doh' && /^[A-Za-z0-9.-]+\.[A-Za-z0-9-]+$/.test(v)) v = 'https://' + v + '/dns-query'; if (t === 'doh') v = dohOwn(v);
if (!DNS_RE[t].test(v)) { zm.toast(DNS_HELP[t].hint, 'warning'); return; } if (!DNS_RE[t].test(v)) { zm.toast(DNS_HELP[t].hint, 'warning'); return; }
if (sel.indexOf(v) >= 0) { zm.toast('Этот сервер уже в списке', 'warning'); return; } if (sel.indexOf(v) >= 0) { zm.toast('Этот сервер уже в списке', 'warning'); return; }
if (sel.length >= DNS_MAX) { zm.toast('Не больше ' + DNS_MAX + ' серверов', 'warning'); return; } if (sel.length >= DNS_MAX) { zm.toast('Не больше ' + DNS_MAX + ' серверов', 'warning'); return; }