Compare commits

..

48 Commits

Author SHA1 Message Date
8e5691f7bb Add additional SRC-IP-CIDR entry for smart TV clients 2026-02-07 20:07:29 +03:00
c30ad90e34 Add SRC-IP-CIDR entry for 192.168.10.103 in clients-smart-tv.yaml 2026-02-07 20:05:20 +03:00
100044e79c Fix typo in DOMAIN-SUFFIX entry and remove unused entries in ru-intra.yaml 2026-01-30 22:04:49 +03:00
bd4af66d63 upd 2026-01-26 19:48:40 +03:00
f62b3ff776 Add new DOMAIN-SUFFIX entries for growcdnssedge.com and flixcdn.com in porn.yaml 2026-01-26 19:25:15 +03:00
373685ca59 Add additional DOMAIN-SUFFIX entries to libraries.yaml for expanded coverage 2026-01-23 12:09:26 +03:00
3933195746 Remove trailing whitespace in external-proxies-sanitizer.js for cleaner code 2026-01-13 17:29:59 +03:00
50bce03cf8 Refactor SUB STORE YAML ASSEMBLER to fix duplicate headers and streamline options parsing 2026-01-09 20:28:32 +03:00
49caf5dc65 Add SUB STORE YAML ASSEMBLER script with comprehensive processing capabilities 2026-01-09 19:56:50 +03:00
8826675dd8 Add SUB STORE YAML ASSEMBLER script with cleaning options for enhanced processing 2026-01-09 19:56:04 +03:00
9dc7c8dda9 Add SVG icons for numbers 01, 02, and 03 to enhance visual representation 2026-01-09 19:41:47 +03:00
7836eceda2 Add SUB STORE YAML ASSEMBLER script for content-tag aware processing 2026-01-06 15:40:56 +03:00
ef4cf861ff Remove unnecessary line from cadian.current.yaml for cleaner configuration 2026-01-05 22:37:12 +03:00
34c3e242e5 Update icon for IPv6 in external-proxies-sanitizer.js for improved clarity 2026-01-05 21:35:48 +03:00
41127d10d1 Upd 2026-01-05 21:33:50 +03:00
ae1b71f741 Reset default values in AMZ_DEFAULTS for Jc, Jmin, Jmax, S1, and S2 to ensure consistent behavior 2026-01-05 20:55:09 +03:00
eeb60ab5ad Reset default values in AMZ_DEFAULTS to zero for Jc, Jmin, and Jmax to ensure consistent behavior 2026-01-05 20:53:38 +03:00
67704659e6 Update icon tags in external-proxies-sanitizer.js for improved clarity and consistency 2026-01-05 20:51:07 +03:00
4f73cd0071 Upd 2026-01-05 20:26:07 +03:00
df7d2d555f Fix final output format in AWG to Clash conversion script; revert to YAML serialization for compatibility with existing processing 2026-01-05 20:04:11 +03:00
3dafd087cd Refactor normalizeOptions function; simplify argument handling and improve boolean conversion logic for better clarity and performance 2026-01-05 19:58:15 +03:00
2fce3a192a Refactor test-options.js; rename globalKeysSample to pickEnvSample and enhance environment variable sampling logic for improved clarity and safety 2026-01-05 19:55:39 +03:00
c140b08e12 Add test-options.js script for reporting global variables and content types 2026-01-05 19:49:38 +03:00
0847e23918 Update final output format in AWG to Clash conversion script; switch from YAML to JSON for better compatibility 2026-01-05 19:45:26 +03:00
4b607f8d6f Refactor normalizeOptions function; enhance URL extraction and query parameter handling for improved flexibility and clarity 2026-01-05 19:44:14 +03:00
e0b79a217f Refactor normalizeOptions function; streamline query parameter extraction and improve boolean conversion logic for better clarity and performance 2026-01-05 19:42:11 +03:00
ba21e17168 Refactor AWG to Clash conversion script; enhance option normalization, parsing logic, and proxy construction for improved functionality and maintainability 2026-01-05 19:37:34 +03:00
26867d4175 Update config-sub-converter/scripts/convert-awg-to-clash.js 2026-01-05 18:37:46 +03:00
e8477c3322 Refactor AWG to Clash conversion script for improved readability and maintainability; streamline parsing and output generation 2026-01-05 18:34:33 +03:00
5cef2d7bca Remove outdated configuration files for Kazakhstan, Netherlands, Poland, and the US; add new script for AWG to Clash conversion 2026-01-05 18:28:07 +03:00
6b76e578b5 Update country detection logic with prioritized flags for improved accuracy 2026-01-05 17:07:45 +03:00
e2f855bb39 Upd 2026-01-05 17:01:56 +03:00
6525bbab11 Update icon for IPv6 and add country detection rules for Belarus and China 2026-01-05 16:57:55 +03:00
0ae3f1100c Enable debug mode by setting DEBUG_APPEND_ORIGINAL_NAME to true 2026-01-05 16:41:58 +03:00
faaf393325 Add lab.svg icon file for improved visual representation 2026-01-05 16:27:20 +03:00
76d11a6621 Fix name formatting by removing space before meta tag in final output 2026-01-05 15:56:30 +03:00
38ba2fd576 Disable debug mode by setting DEBUG_APPEND_ORIGINAL_NAME to false 2026-01-05 15:50:42 +03:00
b81b395282 Add standard ports mapping and update port formatting for improved consistency 2026-01-05 15:49:43 +03:00
143f41dcde Update METATAG_RULES and PORT_FORMAT for improved icon representation and formatting 2026-01-05 15:32:10 +03:00
8f535becc2 Refactor icon and country detection rules in external-proxies-sanitizer.js for improved clarity and maintainability 2026-01-05 13:14:27 +03:00
82e97b2f57 Refactor icon and country detection rules in external-proxies-sanitizer.js for improved accuracy and maintainability 2026-01-04 20:31:45 +03:00
75439cf6df Update external-proxies-sanitizer.js to refine noise patterns and enhance country detection rules 2026-01-04 19:47:47 +03:00
b7f2adba40 Add Sub-Store Advanced Batch Processing Script for proxy normalization and tagging 2026-01-04 19:00:21 +03:00
5c8851a292 Add SOCKS5 listeners for inquisitor and servitor with authentication details 2025-12-27 22:45:07 +03:00
46e8ef128f Add SRC-IP-CIDR entry for DTS-TAB-S7-NET in testzone-a.yaml 2025-12-27 20:06:01 +03:00
3933f84a75 Update commit message for your changes. Lines starting 2025-12-27 19:59:09 +03:00
a67b0879e7 Update host IP addresses for gitea and webway services 2025-12-27 19:30:20 +03:00
20b063a10e Add authentication details for local SOCKS5/HTTP(S) server 2025-12-27 19:11:26 +03:00
27 changed files with 1837 additions and 166 deletions

View File

@@ -11,8 +11,9 @@ lan-allowed-ips:
bind-address: "*"
# authentication of local SOCKS5/HTTP(S) server
# authentication:
# - "user1:pass1"
authentication:
- "inquisitor:271828ir314159"
- "servitor:271828sr314159"
# ————————————————————————————————————————————————— EXTERNAL CONTROLLER —————————————————————————————————————————————————
external-controller: 0.0.0.0:9090
@@ -39,11 +40,11 @@ lgbm-auto-update: true
lgbm-update-interval: 72 # model auto update interval, the default is 72 (hours)
lgbm-url: "https://github.com/vernesong/mihomo/releases/download/LightGBM-Model/Model.bin" # model update url
# ———————————————————————————————————————————————————————— HOSTS ————————————————————————————————————————————————————————
hosts:
'gitea.shamanlanding.org': '192.168.10.4'
'niyya.shamanlanding.org': '192.168.10.4'
'webway.shamanlanding.org': '192.168.10.4'
'gitea.shamanlanding.org': '79.120.77.123'
'webway.shamanlanding.org': '79.120.77.123'
# ——————————————————————————————————————————————————————— PROFILE ———————————————————————————————————————————————————————
profile:

View File

@@ -0,0 +1,233 @@
/**
* SUB STORE YAML ASSEMBLER (v4: Fix Duplicate Headers)
* * Arguments:
* - clear-comments=true
* - clear-manifest=true
* - clear-replacements=true
* * Requires: Header "# @file: filename.yaml" in input files.
*/
// --- OPTIONS PARSING ---
function normalizeOptions() {
const args = (typeof $arguments !== "undefined" && $arguments) ? $arguments : {};
const asBool = (v, def = false) => {
if (v === undefined || v === null || v === "") return def;
if (typeof v === "boolean") return v;
const s = String(v).toLowerCase().trim();
if (["1", "true", "yes", "y", "on"].includes(s)) return true;
if (["0", "false", "no", "n", "off"].includes(s)) return false;
return def;
};
return {
clearComments: asBool(args['clear-comments'], false),
clearManifest: asBool(args['clear-manifest'], false),
clearReplacements: asBool(args['clear-replacements'], false),
};
}
// --- UTILS ---
function normalizeFiles(rawFiles) {
const map = new Map();
if (!rawFiles || !Array.isArray(rawFiles)) return map;
rawFiles.forEach((content) => {
if (typeof content !== 'string') return;
const match = content.match(/^#\s*@file:\s*(.+?)(\s|$)/m);
if (match) map.set(match[1].trim(), content);
});
return map;
}
function cleanText(text) {
return String(text || "").replace(/\r\n/g, "\n");
}
function extractName(block) {
const match = block.match(/^ {4}name:\s*(?:["']?)(.*?)(?:["']?)\s*(?:#.*)?$/m);
return match ? match[1].trim() : null;
}
function extractKey(block, indentLevel) {
const spaceStr = " ".repeat(indentLevel);
const re = new RegExp(`^${spaceStr}([^#\\s][^:]+):`);
const match = block.match(re);
return match ? match[1].trim().replace(/['"]/g, "") : null;
}
function stripFullLineComments(text) {
return text.split('\n')
.filter(line => !line.trim().startsWith('#'))
.join('\n');
}
function splitBlocks(text, type) {
const lines = cleanText(text).split("\n");
const blocks = [];
let currentBuf = [];
const isListStart = (l) => l.match(/^ {2}-\s/);
const isMapStart2 = (l) => l.match(/^ {2}[^ \-#][^:]*:/);
const isMapStart0 = (l) => l.match(/^[^ \-#][^:]*:/);
const isStart = (l) => {
if (type === 'list') return isListStart(l);
if (type === 'map2') return isMapStart2(l);
if (type === 'map0') return isMapStart0(l);
return false;
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.match(/^#\s*@file:/)) continue;
if (isStart(line)) {
if (currentBuf.length > 0) {
blocks.push(currentBuf.join("\n"));
currentBuf = [];
}
}
if (currentBuf.length === 0 && !line.trim()) continue;
currentBuf.push(line);
}
if (currentBuf.length > 0) blocks.push(currentBuf.join("\n"));
return blocks;
}
// --- MERGE LOGIC ---
function processSection(sectionName, manifestEntries, fileMap, opts) {
let sectionOutput = [];
const seenKeys = new Set();
// Добавляем заголовок секции
if (!['root', 'x-substore', 'rules'].includes(sectionName)) {
sectionOutput.push(`${sectionName}:`);
}
if (sectionName === 'x-substore') {
sectionOutput.push(`x-substore:`);
}
for (const entry of manifestEntries) {
let content = fileMap.get(entry.file);
if (!content) throw new Error(`CRITICAL: File "${entry.file}" not found.`);
if (!opts.clearComments) {
sectionOutput.push(`\n# --- source: ${entry.file} | mode: ${entry.mode || "concat"} ---`);
}
if (opts.clearComments) {
content = stripFullLineComments(content);
}
// [FIX] Удаляем дублирующий заголовок x-substore из контента файла,
// но оставляем содержимое (оно уже имеет правильный отступ 2 пробела)
if (sectionName === 'x-substore') {
content = content.replace(/^x-substore:\s*(?:#.*)?$/m, '');
}
const mode = entry.mode || "concat";
if (mode === 'concat') {
const lines = cleanText(content).split('\n').filter(l => !l.match(/^#\s*@file:/));
sectionOutput.push(lines.join('\n'));
continue;
}
if (mode === 'first_wins') {
let blockType = 'map2';
if (sectionName === 'root') blockType = 'map0';
if (['proxies', 'proxy-groups'].includes(sectionName)) blockType = 'list';
const blocks = splitBlocks(content, blockType);
if (blocks.length === 0 && !opts.clearComments) {
sectionOutput.push(`# WARNING: No valid blocks found in ${entry.file}`);
}
for (const block of blocks) {
let id = null;
if (blockType === 'list') {
id = extractName(block);
} else if (blockType === 'map0') {
id = extractKey(block, 0);
} else {
id = extractKey(block, 2);
}
// Apply cleaning options for x-substore content
if (sectionName === 'x-substore' && id) {
if (opts.clearManifest && id === 'manifest') continue;
if (opts.clearReplacements && id === 'replacements') continue;
}
if (id) {
if (seenKeys.has(id)) {
if (!opts.clearComments) sectionOutput.push(`# [SKIP] Duplicate "${id}" ignored`);
continue;
}
seenKeys.add(id);
}
sectionOutput.push(block);
}
}
}
return sectionOutput.join("\n");
}
// --- MAIN EXECUTION ---
try {
const opts = normalizeOptions();
const fileMap = normalizeFiles($files);
let manifestKey = null;
for (const k of fileMap.keys()) {
if (k.startsWith("00-manifest")) {
manifestKey = k;
break;
}
}
if (!manifestKey) throw new Error("Manifest file (00-manifest-...) not found.");
const manifestRaw = fileMap.get(manifestKey);
const manifestObj = ProxyUtils.yaml.safeLoad(manifestRaw);
if (!manifestObj?.['x-substore']?.manifest) {
throw new Error("Invalid Manifest structure.");
}
const manifestList = manifestObj['x-substore'].manifest;
const replacements = manifestObj['x-substore'].replacements || [];
const sectionOrder = [
"x-substore", "root", "hosts", "sniffer", "tun", "dns",
"proxies", "proxy-providers", "proxy-groups", "rule-providers", "rules"
];
const plan = {};
sectionOrder.forEach(s => plan[s] = []);
manifestList.forEach(entry => {
if (!plan[entry.section]) plan[entry.section] = [];
plan[entry.section].push(entry);
});
const finalChunks = [];
for (const sec of sectionOrder) {
if (!plan[sec] || plan[sec].length === 0) continue;
const secStr = processSection(sec, plan[sec], fileMap, opts);
finalChunks.push(secStr);
}
let result = finalChunks.join("\n\n");
if (Array.isArray(replacements)) {
replacements.forEach(rep => {
if (rep.from && rep.to) {
result = result.split(rep.from).join(rep.to);
}
});
}
$content = result;
} catch (err) {
$content = `# CRITICAL ERROR:\n# ${err.message}\n# Stack: ${err.stack}`;
}

View File

@@ -0,0 +1,239 @@
/**
* SUB STORE YAML ASSEMBLER (Content-Tag Aware)
* * Требование: Каждый файл должен начинаться с комментария:
* # @file: filename.yaml
*/
// --- UTILS ---
// Функция нормализации теперь парсит содержимое, чтобы найти имя файла
function normalizeFiles(rawFiles) {
const map = new Map();
if (!rawFiles || !Array.isArray(rawFiles)) return map;
rawFiles.forEach((content, index) => {
if (typeof content !== 'string') return;
// Ищем магический тег: # @file: filename.yaml
const match = content.match(/^#\s*@file:\s*(.+?)(\s|$)/m);
if (match) {
const filename = match[1].trim();
map.set(filename, content);
} else {
// Если тега нет, файл остается "анонимным" и недоступным через манифест,
// но мы можем логировать это.
// console.log(`File at index ${index} has no @file tag`);
}
});
return map;
}
function cleanText(text) {
return String(text || "").replace(/\r\n/g, "\n");
}
// Извлечение name из элемента списка
function extractName(block) {
// Ищем name: value с учетом отступов (4 пробела)
const match = block.match(/^ {4}name:\s*(?:["']?)(.*?)(?:["']?)\s*(?:#.*)?$/m);
return match ? match[1].trim() : null;
}
// Извлечение ключа из map (0 или 2 пробела)
function extractKey(block, indentLevel) {
const spaceStr = " ".repeat(indentLevel);
const re = new RegExp(`^${spaceStr}([^#\\s][^:]+):`);
const match = block.match(re);
return match ? match[1].trim().replace(/['"]/g, "") : null;
}
// Разбивка текста на логические блоки
function splitBlocks(text, type) {
const lines = cleanText(text).split("\n");
const blocks = [];
let currentBuf = [];
// Детекторы начала блока
const isListStart = (l) => l.match(/^ {2}-\s/); // " - "
const isMapStart2 = (l) => l.match(/^ {2}[^ \-#][^:]*:/); // " key:"
const isMapStart0 = (l) => l.match(/^[^ \-#][^:]*:/); // "key:" (root)
const isStart = (l) => {
if (type === 'list') return isListStart(l);
if (type === 'map2') return isMapStart2(l);
if (type === 'map0') return isMapStart0(l);
return false;
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Если строка начинается с # @file:, пропускаем ее, чтобы не мусорить в конфиге
if (line.match(/^#\s*@file:/)) continue;
if (isStart(line)) {
if (currentBuf.length > 0) {
blocks.push(currentBuf.join("\n"));
currentBuf = [];
}
}
// Пропуск пустых строк в начале, если буфер пуст
if (currentBuf.length === 0 && !line.trim()) continue;
currentBuf.push(line);
}
if (currentBuf.length > 0) {
blocks.push(currentBuf.join("\n"));
}
return blocks;
}
// --- MERGE LOGIC ---
function processSection(sectionName, manifestEntries, fileMap) {
let sectionOutput = [];
const seenKeys = new Set();
// Добавляем заголовок секции (кроме root, x-substore и rules)
if (!['root', 'x-substore', 'rules'].includes(sectionName)) {
sectionOutput.push(`${sectionName}:`);
}
if (sectionName === 'x-substore') {
sectionOutput.push(`x-substore:`);
}
for (const entry of manifestEntries) {
const content = fileMap.get(entry.file);
if (!content) {
// FAIL-FAST: Если файл из манифеста не найден (нет тега или не загружен)
throw new Error(`CRITICAL: File "${entry.file}" not found inside input bundle. Did you add '# @file: ${entry.file}' header?`);
}
const mode = entry.mode || "concat";
sectionOutput.push(`\n# --- source: ${entry.file} | mode: ${mode} ---`);
if (mode === 'concat') {
// Просто чистим от тега @file при вставке
const lines = cleanText(content).split('\n').filter(l => !l.match(/^#\s*@file:/));
sectionOutput.push(lines.join('\n'));
continue;
}
if (mode === 'first_wins') {
let blockType = 'map2';
if (sectionName === 'root') blockType = 'map0';
if (['proxies', 'proxy-groups'].includes(sectionName)) blockType = 'list';
const blocks = splitBlocks(content, blockType);
if (blocks.length === 0) {
sectionOutput.push(`# WARNING: No valid blocks found in ${entry.file}`);
}
for (const block of blocks) {
let id = null;
if (blockType === 'list') {
id = extractName(block);
} else if (blockType === 'map0') {
id = extractKey(block, 0);
} else {
id = extractKey(block, 2);
}
if (id) {
if (seenKeys.has(id)) {
sectionOutput.push(`# [SKIP] Duplicate "${id}" ignored`);
continue;
}
seenKeys.add(id);
}
sectionOutput.push(block);
}
}
}
return sectionOutput.join("\n");
}
// --- MAIN EXECUTION ---
try {
// 1. Создаем карту файлов на основе тегов # @file:
const fileMap = normalizeFiles($files);
// 2. Ищем Манифест
let manifestKey = null;
// Ищем файл, чье имя (из тега) начинается с 00-manifest
for (const k of fileMap.keys()) {
if (k.startsWith("00-manifest")) {
manifestKey = k;
break;
}
}
if (!manifestKey) {
const foundFiles = Array.from(fileMap.keys()).join(", ");
throw new Error(`Manifest file (00-manifest-...) not found in headers. Found files: [${foundFiles}]`);
}
// 3. Парсим Манифест
const manifestRaw = fileMap.get(manifestKey);
const manifestObj = ProxyUtils.yaml.safeLoad(manifestRaw);
if (!manifestObj?.['x-substore']?.manifest) {
throw new Error("Invalid Manifest: missing x-substore.manifest structure.");
}
const manifestList = manifestObj['x-substore'].manifest;
const replacements = manifestObj['x-substore'].replacements || [];
// 4. Планирование порядка секций
const sectionOrder = [
"x-substore", "root", "hosts", "sniffer", "tun", "dns",
"proxies", "proxy-providers", "proxy-groups", "rule-providers", "rules"
];
const plan = {};
sectionOrder.forEach(s => plan[s] = []);
manifestList.forEach(entry => {
// Если секция в манифесте есть, а в нашем плане нет - добавляем динамически (на всякий случай)
if (!plan[entry.section]) plan[entry.section] = [];
plan[entry.section].push(entry);
});
// 5. Сборка
const finalChunks = [];
for (const sec of sectionOrder) {
if (!plan[sec] || plan[sec].length === 0) continue;
// Обработка секции
const secStr = processSection(sec, plan[sec], fileMap);
finalChunks.push(secStr);
}
let result = finalChunks.join("\n\n");
// 6. Replacements (Literal)
if (Array.isArray(replacements)) {
replacements.forEach(rep => {
if (rep.from && rep.to) {
result = result.split(rep.from).join(rep.to);
}
});
}
$content = result;
} catch (err) {
$content = `# CRITICAL ERROR in Assembler:\n# ${err.message}\n\n# Stack:\n${err.stack}`;
}

View File

@@ -0,0 +1,235 @@
/**********************
* Defaults (AmneziaWG)
* Если в исходнике нет параметра, берём отсюда.
* Если итоговое значение == 0, параметр пропускаем в amnezia-wg-option.
**********************/
const AMZ_DEFAULTS = {
Jc: 4,
Jmin: 10,
Jmax: 50,
S1: 110,
S2: 120,
H1: 0,
H2: 0,
H3: 0,
H4: 0,
};
/**********************
* Options from Sub Store
* Example URL:
* .../convert-awg-to-clash.js#dns=false&ipv6=false#noCache
*
* Требования:
* - dns=false => remote-dns-resolve: false (вне зависимости от входа)
* - ipv6=false => удалить IPv6 из allowed-ips (и вообще не добавлять ipv6-части)
**********************/
function normalizeOptions() {
const args = (typeof $arguments !== "undefined" && $arguments) ? $arguments : {};
const asBool = (v, def = true) => {
if (v === undefined || v === null || v === "") return def;
if (typeof v === "boolean") return v;
const s = String(v).toLowerCase().trim();
if (["1", "true", "yes", "y", "on"].includes(s)) return true;
if (["0", "false", "no", "n", "off"].includes(s)) return false;
return def;
};
return {
dns: asBool(args.dns, true),
ipv6: asBool(args.ipv6, true),
};
}
/**********************
* Parsing WG INI blocks
**********************/
function cleanLines(text) {
return String(text ?? "")
.replace(/\r\n/g, "\n")
.split("\n");
}
// Парсим один INI-фрагмент с [Interface] и [Peer] (один peer)
function parseIniOne(text) {
const lines = cleanLines(text)
.map((l) => l.trim())
.filter((l) => l.length > 0 && !l.startsWith("#") && !l.startsWith(";"));
let section = null;
const data = { Interface: {}, Peer: {} };
for (const line of lines) {
const mSec = line.match(/^\[(.+?)\]$/);
if (mSec) {
section = mSec[1];
continue;
}
const mKV = line.match(/^([^=]+?)\s*=\s*(.+)$/);
if (!mKV || !section) continue;
const key = mKV[1].trim();
const value = mKV[2].trim();
if (section === "Interface") data.Interface[key] = value;
else if (section === "Peer") data.Peer[key] = value;
}
return data;
}
// Разбиваем весь файл на блоки по заголовкам "##### ..."
function splitByHeaders(fullText) {
const lines = cleanLines(fullText);
const blocks = [];
let current = { name: "amz-wg", buf: [] };
const headerRe = /^#{5}\s*(.+)\s*$/;
for (const line of lines) {
const mh = line.match(headerRe);
if (mh) {
// закрываем предыдущий блок, если там что-то есть
if (current.buf.join("\n").trim().length > 0) blocks.push(current);
current = { name: mh[1].trim(), buf: [] };
continue;
}
current.buf.push(line);
}
if (current.buf.join("\n").trim().length > 0) blocks.push(current);
return blocks;
}
function splitList(val) {
return String(val || "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
}
function parseEndpoint(endpoint) {
// Поддержка:
// - host:port
// - [ipv6]:port
const s = String(endpoint || "").trim();
const v6 = s.match(/^\[(.+?)\]:(\d+)$/);
if (v6) return { host: v6[1], port: Number(v6[2]) };
const v4 = s.match(/^(.+?):(\d+)$/);
if (v4) return { host: v4[1], port: Number(v4[2]) };
return { host: "", port: 0 };
}
function toNumberOrNull(v) {
const s = String(v ?? "").trim();
if (s === "") return null;
if (/^-?\d+$/.test(s)) return Number(s);
return null;
}
function buildAmzOptions(interfaceObj) {
// Правило:
// - если в файле есть параметр => используем его
// - иначе берём из AMZ_DEFAULTS
// - если итог == 0 => пропускаем
const out = {};
const keys = Object.keys(AMZ_DEFAULTS);
for (const K of keys) {
const fromFile = interfaceObj[K];
const fileNum = toNumberOrNull(fromFile);
const fallback = AMZ_DEFAULTS[K];
const finalVal =
fileNum !== null ? fileNum : (fallback ?? 0);
if (Number(finalVal) !== 0) {
out[K.toLowerCase()] = Number(finalVal);
}
}
return out;
}
function filterAllowedIPs(allowed, enableIPv6) {
if (enableIPv6) return allowed;
// выкидываем всё, что похоже на IPv6
return allowed.filter((cidr) => !cidr.includes(":"));
}
function buildProxy(blockName, wg, options) {
const i = wg.Interface || {};
const p = wg.Peer || {};
const address = i.Address || "";
const dnsList = splitList(i.DNS);
const ep = parseEndpoint(p.Endpoint);
let allowed = splitList(p.AllowedIPs);
allowed = filterAllowedIPs(allowed, options.ipv6);
const proxy = {
name: blockName || "amz-wg",
type: "wireguard",
ip: address,
// ipv6 поле в твоём примере закомментировано, так что не добавляем вообще
"private-key": i.PrivateKey || "",
peers: [
{
server: ep.host,
port: ep.port,
"public-key": p.PublicKey || "",
...(p.PresharedKey ? { "pre-shared-key": p.PresharedKey } : {}),
"allowed-ips": allowed,
},
],
udp: true,
// dns=false => принудительно false
"remote-dns-resolve": options.dns ? true : false,
...(dnsList.length ? { dns: dnsList } : {}),
};
const amz = buildAmzOptions(i);
if (Object.keys(amz).length) {
proxy["amnezia-wg-option"] = amz;
}
return proxy;
}
/**********************
* ENTRYPOINT
**********************/
const opts = normalizeOptions();
// Вход: чаще всего $content, но на всякий пожарный берём $files[0]
const input = String($content ?? ($files && $files[0]) ?? "");
// Разбиваем по заголовкам ##### ...
const blocks = splitByHeaders(input);
// Для каждого блока парсим INI и строим proxy
const proxies = [];
for (const b of blocks) {
const iniText = b.buf.join("\n").trim();
if (!iniText) continue;
const wg = parseIniOne(iniText);
// минимальная валидация: нужны ключи
if (!wg.Interface?.PrivateKey || !wg.Peer?.PublicKey || !wg.Peer?.Endpoint) {
// пропускаем мусорные блоки, чтобы не ронять весь конвертер
continue;
}
proxies.push(buildProxy(b.name, wg, opts));
}
// Финальный YAML
$content = ProxyUtils.yaml.safeDump({ proxies });

View File

@@ -0,0 +1,328 @@
/**
* Sub-Store operator: Normalize + tag + country detect + per-country numbering
*
* Output format (default):
* 🇺🇸 USA-01 🔒 📺 ❻ ▫ws/vless/443
*
* Notes:
* - Numbering is computed per-country AFTER grouping the full list.
* - Tags (icons) do NOT affect numbering order.
* - GeoIP is optional and only used when server is an IP and utils.geoip.lookup exists.
*/
///////////////////////
// CONFIG (EDIT ME)
///////////////////////
// 1) Remove these patterns (marketing noise, brackets, separators, etc.)
const NOISE_PATTERNS = [
/\[[^\]]*]/g, // [ ... ]
/\([^)]*\)/g, // ( ... )
/\{[^}]*}/g, // { ... }
/\btraffic\b/gi,
/\bfree\b/gi,
/\bwebsite\b/gi,
/\bexpire\b/gi,
/\blow\s*ping\b/gi,
/\bai\s*studio\b/gi,
/\bno\s*p2p\b/gi,
/\b10\s*gbit\b/gi,
/\bvless\b/gi, // you said you don't want it in the visible name
/\bvmess\b/gi,
/\bssr?\b/gi,
/\btrojan\b/gi,
/\bhysteria2?\b/gi,
/\btuic\b/gi,
/[|]/g,
/[_]+/g,
/[-]{2,}/g
];
// 2) Keyword -> icon tags (if found in original name, icon is added; the keyword is removed from base name)
const ICON_RULES = [
{ regex: /\bYT\b/gi, icon: "📺" },
{ regex: /\bIPv6\b/gi, icon: "❻" },
{ regex: /\bNetflix\b|\bNF\b/gi, icon: "🎬" },
{ regex: /\bDisney\+?\b|\bDSNY\b/gi, icon: "🏰" },
{ regex: /\bHBO\b/gi, icon: "📼" },
{ regex: /\bPrime\b|\bAmazon\b/gi, icon: "📦" },
{ regex: /\bChatGPT\b|\bOpenAI\b/gi, icon: "🤖" },
{ regex: /\bSteam\b/gi, icon: "🎮" },
];
// 3) Optional “network” tag rules based on NAME text (not $server.network)
// (Useful if providers shove "BGP/IPLC" into the node name)
const NAME_NETWORK_TAGS = [
{ regex: /\bIPLC\b/gi, tag: "🛰️" },
{ regex: /\bBGP\b/gi, tag: "🧭" },
{ regex: /\bAnycast\b/gi, tag: "🌍" }
];
// 4) Country detection rules by NAME (regex). First match wins (priority = lower is earlier)
const COUNTRY_RULES = [
// USA
{ regex: /\b(USA|US|UNITED\s*STATES|AMERICA|NEW\s*YORK|NYC|LOS\s*ANGELES|LA|DALLAS|CHI(CAGO)?)\b/i, iso3: "USA", flag: "🇺🇸", priority: 10 },
// Germany
{ regex: /\b(DE|DEU|GER(MANY)?|FRANKFURT|BERLIN|MUNICH|MÜNCHEN)\b/i, iso3: "DEU", flag: "🇩🇪", priority: 20 },
// Netherlands
{ regex: /\b(NL|NLD|NETHERLANDS|HOLLAND|AMSTERDAM)\b/i, iso3: "NLD", flag: "🇳🇱", priority: 30 },
// UK
{ regex: /\b(UK|GB|GBR|UNITED\s*KINGDOM|LONDON|MANCHESTER)\b/i, iso3: "GBR", flag: "🇬🇧", priority: 40 },
// France
{ regex: /\b(FR|FRA|FRANCE|PARIS|MARSEILLE)\b/i, iso3: "FRA", flag: "🇫🇷", priority: 50 },
// Poland
{ regex: /\b(PL|POL|POLAND|WARSAW|WARSZAWA)\b/i, iso3: "POL", flag: "🇵🇱", priority: 60 },
// Finland
{ regex: /\b(FI|FIN|FINLAND|HELSINKI)\b/i, iso3: "FIN", flag: "🇫🇮", priority: 70 },
// Sweden
{ regex: /\b(SE|SWE|SWEDEN|STOCKHOLM)\b/i, iso3: "SWE", flag: "🇸🇪", priority: 80 },
// Norway
{ regex: /\b(NO|NOR|NORWAY|OSLO)\b/i, iso3: "NOR", flag: "🇳🇴", priority: 90 },
// Switzerland
{ regex: /\b(CH|CHE|SWITZERLAND|ZURICH|GENEVA)\b/i, iso3: "CHE", flag: "🇨🇭", priority: 100 },
// Estonia / Latvia / Lithuania
{ regex: /\b(EE|EST|ESTONIA|TALLINN)\b/i, iso3: "EST", flag: "🇪🇪", priority: 110 },
{ regex: /\b(LV|LVA|LATVIA|RIGA)\b/i, iso3: "LVA", flag: "🇱🇻", priority: 120 },
{ regex: /\b(LT|LTU|LITHUANIA|VILNIUS)\b/i, iso3: "LTU", flag: "🇱🇹", priority: 130 },
// Turkey
{ regex: /\b(TR|TUR|TURKEY|ISTANBUL)\b/i, iso3: "TUR", flag: "🇹🇷", priority: 140 },
// Singapore / Japan / Korea / Hong Kong
{ regex: /\b(SG|SGP|SINGAPORE)\b/i, iso3: "SGP", flag: "🇸🇬", priority: 200 },
{ regex: /\b(JP|JPN|JAPAN|TOKYO|OSAKA)\b/i, iso3: "JPN", flag: "🇯🇵", priority: 210 },
{ regex: /\b(KR|KOR|KOREA|SEOUL)\b/i, iso3: "KOR", flag: "🇰🇷", priority: 220 },
{ regex: /\b(HK|HKG|HONG\s*KONG)\b/i, iso3: "HKG", flag: "🇭🇰", priority: 230 },
];
// 5) GeoIP mapping (ISO2 -> ISO3 + flag) used only if utils.geoip.lookup(ip) returns ISO2
const ISO2_TO_ISO3 = {
US: { iso3: "USA", flag: "🇺🇸" },
DE: { iso3: "DEU", flag: "🇩🇪" },
NL: { iso3: "NLD", flag: "🇳🇱" },
GB: { iso3: "GBR", flag: "🇬🇧" },
FR: { iso3: "FRA", flag: "🇫🇷" },
PL: { iso3: "POL", flag: "🇵🇱" },
FI: { iso3: "FIN", flag: "🇫🇮" },
SE: { iso3: "SWE", flag: "🇸🇪" },
NO: { iso3: "NOR", flag: "🇳🇴" },
CH: { iso3: "CHE", flag: "🇨🇭" },
EE: { iso3: "EST", flag: "🇪🇪" },
LV: { iso3: "LVA", flag: "🇱🇻" },
LT: { iso3: "LTU", flag: "🇱🇹" },
TR: { iso3: "TUR", flag: "🇹🇷" },
SG: { iso3: "SGP", flag: "🇸🇬" },
JP: { iso3: "JPN", flag: "🇯🇵" },
KR: { iso3: "KOR", flag: "🇰🇷" },
HK: { iso3: "HKG", flag: "🇭🇰" },
};
// 6) Protocol icons (based on proxy.type)
const PROTOCOL_ICONS = {
ss: "🔒",
ssr: "☂️",
vmess: "🪁",
vless: "🌌",
trojan: "🐎",
http: "🌐",
socks5: "🧦",
snell: "🐌",
wireguard: "🐲",
hysteria: "🤪",
hysteria2: "⚡",
tuic: "🚅"
};
///////////////////////
// HELPERS
///////////////////////
function isIPv4(str) {
if (typeof str !== "string") return false;
const m = str.match(/^(\d{1,3})(\.\d{1,3}){3}$/);
if (!m) return false;
return str.split(".").every(oct => {
const n = Number(oct);
return n >= 0 && n <= 255 && String(n) === oct.replace(/^0+(\d)/, "$1"); // avoids "001" weirdness
});
}
function uniq(arr) {
return [...new Set(arr.filter(Boolean))];
}
function sanitizeBaseName(name) {
let s = String(name || "");
// Remove noise patterns
for (const re of NOISE_PATTERNS) s = s.replace(re, " ");
// Collapse spaces
s = s.replace(/\s+/g, " ").trim();
return s;
}
function extractIconTagsAndStrip(name) {
let s = String(name || "");
const tags = [];
for (const r of ICON_RULES) {
if (r.regex.test(s)) {
tags.push(r.icon);
s = s.replace(r.regex, " ");
}
}
for (const t of NAME_NETWORK_TAGS) {
if (t.regex.test(s)) {
tags.push(t.tag);
s = s.replace(t.regex, " ");
}
}
return { stripped: s.replace(/\s+/g, " ").trim(), tags: uniq(tags) };
}
function detectCountryByName(name) {
const n = String(name || "");
// Order by priority, then first match wins
const sorted = COUNTRY_RULES.slice().sort((a, b) => a.priority - b.priority);
for (const c of sorted) {
if (c.regex.test(n)) return { iso3: c.iso3, flag: c.flag, priority: c.priority, source: "name" };
}
return null;
}
function detectCountryByGeoIP(server, utils) {
if (!isIPv4(server)) return null;
if (!utils || !utils.geoip || typeof utils.geoip.lookup !== "function") return null;
try {
const geo = utils.geoip.lookup(server);
const iso2 = geo && (geo.country || geo.country_code || geo.iso_code);
if (!iso2 || typeof iso2 !== "string") return null;
const key = iso2.toUpperCase();
const mapped = ISO2_TO_ISO3[key];
if (mapped) return { iso3: mapped.iso3, flag: mapped.flag, priority: 900, source: "geoip" };
// Unknown ISO2: keep something sane
return { iso3: key, flag: "🏳️", priority: 950, source: "geoip" };
} catch (e) {
return null;
}
}
function pad2(n) {
const x = Number(n);
return x < 10 ? `0${x}` : String(x);
}
function safeStr(v) {
return (v === undefined || v === null) ? "" : String(v);
}
///////////////////////
// OPERATOR
///////////////////////
function operator(proxies, targetPlatform, utils) {
// Sub-Store sometimes passes utils as global $utils; sometimes as 3rd arg; sometimes not at all.
// We'll accept any of them without whining.
const U = utils || (typeof $utils !== "undefined" ? $utils : null);
const buckets = Object.create(null);
for (const proxy of proxies) {
const originalName = safeStr(proxy && proxy.name);
// 1) Extract tags (icons) from ORIGINAL name, then strip those keywords out
const iconStage = extractIconTagsAndStrip(originalName);
// 2) Sanitize remaining base name (remove marketing trash, brackets, etc.)
const cleanBase = sanitizeBaseName(iconStage.stripped);
// 3) Detect country (name first, then GeoIP)
const byName = detectCountryByName(originalName);
const byGeo = detectCountryByGeoIP(proxy && proxy.server, U);
const country = byName || byGeo || { iso3: "UNK", flag: "🏴‍☠️", priority: 9999, source: "fallback" };
// 4) Protocol icon (based on type)
const proto = PROTOCOL_ICONS[(proxy && proxy.type) || ""] || "🔌";
// 5) Network/type/port tag (from proxy fields)
const net = safeStr(proxy && proxy.network) || "net?";
const typ = safeStr(proxy && proxy.type) || "type?";
const port = safeStr(proxy && proxy.port) || "port?";
const metaTag = `▫️${net}/${typ}/${port}`;
// 6) Prepare bucket key
const key = country.iso3;
if (!buckets[key]) {
buckets[key] = {
country,
list: []
};
}
// Keep meta used for sorting and final formatting
buckets[key].list.push({
proxy,
_meta: {
cleanBase,
iconTags: iconStage.tags,
proto,
metaTag
}
});
}
// 7) Sort buckets by priority
const bucketKeys = Object.keys(buckets).sort((a, b) => {
return (buckets[a].country.priority || 9999) - (buckets[b].country.priority || 9999);
});
// 8) Sort inside each country bucket and rename with per-country numbering
const result = [];
for (const key of bucketKeys) {
const group = buckets[key];
group.list.sort((A, B) => {
// Tags do not affect numbering: sort only by sanitized base + server:port as tie-breaker
const an = A._meta.cleanBase.toLowerCase();
const bn = B._meta.cleanBase.toLowerCase();
if (an !== bn) return an.localeCompare(bn);
const as = `${safeStr(A.proxy.server)}:${safeStr(A.proxy.port)}`;
const bs = `${safeStr(B.proxy.server)}:${safeStr(B.proxy.port)}`;
return as.localeCompare(bs);
});
for (let i = 0; i < group.list.length; i++) {
const item = group.list[i];
const p = item.proxy;
const num = pad2(i + 1);
const tagStr = item._meta.iconTags.length ? ` ${item._meta.iconTags.join(" ")}` : "";
// Final name format:
// 🇩🇪 DEU-03 🌌 📺 ❻ ▫ws/vless/443
p.name = `${group.country.flag} ${group.country.iso3}-${num} ${item._meta.proto}${tagStr} ${item._meta.metaTag}`.replace(/\s+/g, " ").trim();
result.push(p);
}
}
return result;
}

View File

@@ -0,0 +1,499 @@
/**
* Sub-Store operator: Normalize + tag + country detect + per-country numbering
*
* Output format (default):
* 🇺🇸 USA-01 🔒 📺 ❻ ▫ws/vless/443
*
* Notes:
* - Numbering is computed per-country AFTER grouping the full list.
* - Tags (icons) do NOT affect numbering order.
* - GeoIP is optional and only used when server is an IP and utils.geoip.lookup exists.
*/
///////////////////////
// CONFIG (EDIT ME)
///////////////////////
const DEBUG_APPEND_ORIGINAL_NAME = false; // set true to enable debug mode (appends original name as comment)
// 1) Remove these patterns (marketing noise, brackets, separators, etc.)
const NOISE_PATTERNS = [
/\[[^\]]*]/g, // [ ... ]
/\([^)]*\)/g, // ( ... )
/\{[^}]*}/g, // { ... }
/\btraffic\b/gi,
/\bfree\b/gi,
/\bwebsite\b/gi,
/\bexpire\b/gi,
/\blow\s*ping\b/gi,
/\bai\s*studio\b/gi,
/\bno\s*p2p\b/gi,
/\b10\s*gbit\b/gi,
/\bvless\b/gi,
/\bvmess\b/gi,
/\bssr?\b/gi,
/\btrojan\b/gi,
/\bhysteria2?\b/gi,
/\btuic\b/gi,
/[|]/g,
/[_]+/g,
/[-]{2,}/g
];
// 2) Keyword -> icon tags (if found in original name, icon is added; the keyword is removed from base name)
// 🇫‌🇿‌ 🇺‌🇳‌ 🇩‌🇻‌ 🇻‌🇿‌ 🇵‌🇷‌ 🇦‌🇿‌ 🇬‌🇺‌🇦‌🇷‌🇩‌
const ICON_RULES = [
{ regex: /TEST/gi, icon: "🧪" },
{ regex: uWordBoundaryGroup("Low Ping"), icon: "⚡️" },
{ regex: uWordBoundaryGroup("10 Gbit"), icon: "🛤️" },
{ regex: uWordBoundaryGroup("YT|Russia|Россия"), icon: "📺" },
{ regex: uWordBoundaryGroup("IPv6"), icon: "🎱" },
{ regex: uWordBoundaryGroup("Gemini|AI Studio"), icon: "🤖" },
{ regex: uWordBoundaryGroup("Torrent|P2P|P2P-Torrents"), icon: "🧲" },
{ regex: uWordBoundaryGroup("local"), icon: "🚪" },
{ regex: uWordBoundaryGroup("neighbourhood"), icon: "🫂" },
{ regex: uWordBoundaryGroup("xfizz|x-fizz"), icon: "🇫‌" },
{ regex: uWordBoundaryGroup("unicade|uncd"), icon: "🇺‌" },
{ regex: uWordBoundaryGroup("vzdh|vezdehod"), icon: "🇻‌" },
{ regex: uWordBoundaryGroup("dvpn|d-vpn"), icon: "🇩‌" },
{ regex: uWordBoundaryGroup("proton"), icon: "🇵‌" },
{ regex: uWordBoundaryGroup("amnezia"), icon: "🇦‌" },
{ regex: uWordBoundaryGroup("adguard"), icon: "🇬‌‌" },
];
// 3) Optional “network” tag rules based on NAME text (not $server.network)
// (Useful if providers shove "BGP/IPLC" into the node name)
const NAME_NETWORK_TAGS = [
{ regex: uWordBoundaryGroup("IPLC"), tag: "🛰️" },
{ regex: uWordBoundaryGroup("BGP"), tag: "🧭" },
{ regex: uWordBoundaryGroup("Anycast"), tag: "🌍" }
];
// 4) Country detection rules by NAME (regex). First match wins (priority = lower is earlier)
const COUNTRY_RULES = [
{ regex: uWordBoundaryGroup("(Аргентина|Argentina|AR|ARG|ARGENTINA|BUENOS\s*AIRES)"), iso3: "ARG", flag: "🇦🇷", priority: 100 }, // Argentina
{ regex: uWordBoundaryGroup("(Australia|AU|AUS|AUSTRALIA|SYDNEY)"), iso3: "AUS", flag: "🇦🇺", priority: 110 }, // Australia
{ regex: uWordBoundaryGroup("(Austria|AT|AUT|AUSTRIA|VIENNA)"), iso3: "AUT", flag: "🇦🇹", priority: 120 }, // Austria
{ regex: uWordBoundaryGroup("(Беларусь|Белоруссия|BELARUS)"), iso3: "BLR", flag: "🇧🇾", priority: 130 }, // Belarus
{ regex: uWordBoundaryGroup("(Brazil|BR|BRA|BRAZIL|SAO\s*PAULO)"), iso3: "BRA", flag: "🇧🇷", priority: 140 }, // Brazil
{ regex: uWordBoundaryGroup("(Bulgaria|BG|BGR|BULGARIA|SOFIA)"), iso3: "BGR", flag: "🇧🇬", priority: 150 }, // Bulgaria
{ regex: uWordBoundaryGroup("(Canada|CA|CAN|CANADA|TORONTO)"), iso3: "CAN", flag: "🇨🇦", priority: 160 }, // Canada
{ regex: uWordBoundaryGroup("(КИТАЙ|China)"), iso3: "CHN", flag: "🇨🇳", priority: 170 }, // China
{ regex: uWordBoundaryGroup("(Czech\s*Republic|CZ|CZE|CZECH|PRAGUE)"), iso3: "CZE", flag: "🇨🇿", priority: 180 }, // Czech Republic
{ regex: uWordBoundaryGroup("(Denmark|DK|DNK|DENMARK|COPENHAGEN)"), iso3: "DNK", flag: "🇩🇰", priority: 190 }, // Denmark
{ regex: uWordBoundaryGroup("(Egypt|EG|EGY|EGYPT|CAIRO)"), iso3: "EGY", flag: "🇪🇬", priority: 200 }, // Egypt
{ regex: uWordBoundaryGroup("(Эстония|EE|EST|ESTONIA|TALLINN)"), iso3: "EST", flag: "🇪🇪", priority: 210 }, // Estonia
{ regex: uWordBoundaryGroup("(Финляндия|FI|FIN|FINLAND|HELSINKI)"), iso3: "FIN", flag: "🇫🇮", priority: 220 }, // Finland
{ regex: uWordBoundaryGroup("(Франция|FR|FRA|FRANCE|PARIS|MARSEILLE)"), iso3: "FRA", flag: "🇫🇷", priority: 230 }, // France
{ regex: uWordBoundaryGroup("(Georgia|GE|GEO|GEORGIA|TBILISI)"), iso3: "GEO", flag: "🇬🇪", priority: 240 }, // Georgia
{ regex: uWordBoundaryGroup("(Германия|DE|DEU|GER(MANY)?|FRANKFURT|BERLIN|MUNICH)"), iso3: "DEU", flag: "🇩🇪", priority: 250 }, // Germany
{ regex: uWordBoundaryGroup("(Гонконг|HK|HKG|HONG\s*KONG)"), iso3: "HKG", flag: "🇭🇰", priority: 260 }, // Hong Kong
{ regex: uWordBoundaryGroup("(India|IN|IND|INDIA|MUMBAI)"), iso3: "IND", flag: "🇮🇳", priority: 270 }, // India
{ regex: uWordBoundaryGroup("(Ireland|IE|IRL|IRELAND|DUBLIN)"), iso3: "IRL", flag: "🇮🇪", priority: 280 }, // Ireland
{ regex: uWordBoundaryGroup("(Israel|IL|ISR|ISRAEL|TEL\s*AVIV)"), iso3: "ISR", flag: "🇮🇱", priority: 290 }, // Israel
{ regex: uWordBoundaryGroup("(Italy|IT|ITA|ITALY|ROME)"), iso3: "ITA", flag: "🇮🇹", priority: 300 }, // Italy
{ regex: uWordBoundaryGroup("(Япония|JP|JPN|JAPAN|TOKYO|OSAKA)"), iso3: "JPN", flag: "🇯🇵", priority: 310 }, // Japan
{ regex: uWordBoundaryGroup("(Kazakhstan|KZ|KAZ|KAZAKHSTAN|ALMATY)"), iso3: "KAZ", flag: "🇰🇿", priority: 320 }, // Kazakhstan
{ regex: uWordBoundaryGroup("(Латвия|LV|LVA|LATVIA|RIGA)"), iso3: "LVA", flag: "🇱🇻", priority: 330 }, // Latvia
{ regex: uWordBoundaryGroup("(Литва|LT|LTU|LITHUANIA|VILNIUS)"), iso3: "LTU", flag: "🇱🇹", priority: 340 }, // Lithuania
{ regex: uWordBoundaryGroup("(Malaysia|MY|MYS|MALAYSIA|KUALA\s*LUMPUR)"), iso3: "MYS", flag: "🇲🇾", priority: 350 }, // Malaysia
{ regex: uWordBoundaryGroup("(Moldova|MD|MDA|MOLDOVA|CHISINAU)"), iso3: "MDA", flag: "🇲🇩", priority: 360 }, // Moldova
{ regex: uWordBoundaryGroup("(Нидерланды|NL|NLD|NETHERLANDS|HOLLAND|AMSTERDAM)"), iso3: "NLD", flag: "🇳🇱", priority: 370 }, // Netherlands
{ regex: uWordBoundaryGroup("(Nigeria|NG|NGA|NIGERIA|LAGOS)"), iso3: "NGA", flag: "🇳🇬", priority: 380 }, // Nigeria
{ regex: uWordBoundaryGroup("(Норвегия|NO|NOR|NORWAY|OSLO)"), iso3: "NOR", flag: "🇳🇴", priority: 390 }, // Norway
{ regex: uWordBoundaryGroup("(Philippines|PH|PHL|PHILIPPINES|MANILA)"), iso3: "PHL", flag: "🇵🇭", priority: 400 }, // Philippines
{ regex: uWordBoundaryGroup("(Польша|PL|POL|POLAND|WARSAW|WARSZAWA)"), iso3: "POL", flag: "🇵🇱", priority: 410 }, // Poland
{ regex: uWordBoundaryGroup("(Portugal|PT|PRT|PORTUGAL|LISBON)"), iso3: "PRT", flag: "🇵🇹", priority: 420 }, // Portugal
{ regex: uWordBoundaryGroup("(Romania|RO|ROU|ROMANIA|BUCHAREST)"), iso3: "ROU", flag: "🇷🇴", priority: 430 }, // Romania
{ regex: uWordBoundaryGroup("(Russia|RU|RUS|RUSSIA|MOSCOW)"), iso3: "RUS", flag: "🇷🇺", priority: 440 }, // Russia
{ regex: uWordBoundaryGroup("(Сингапур|SG|SGP|SINGAPORE)"), iso3: "SGP", flag: "🇸🇬", priority: 200 }, // Singapore
{ regex: uWordBoundaryGroup("(South Korea|Корея|KR|KOR|KOREA|SEOUL)"), iso3: "KOR", flag: "🇰🇷", priority: 450 }, // South Korea
{ regex: uWordBoundaryGroup("(Spain|ES|ESP|SPAIN|MADRID)"), iso3: "ESP", flag: "🇪🇸", priority: 460 }, // Spain
{ regex: uWordBoundaryGroup("(Швеция|SE|SWE|SWEDEN|STOCKHOLM)"), iso3: "SWE", flag: "🇸🇪", priority: 470 }, // Sweden
{ regex: uWordBoundaryGroup("(Швейцария|CH|CHE|SWITZERLAND|Switzerl)"), iso3: "CHE", flag: "🇨🇭", priority: 480 }, // Switzerland
{ regex: uWordBoundaryGroup("(Taiwan|TW|TWN|TAIWAN|TAIPEI)"), iso3: "TWN", flag: "🇹🇼", priority: 490 }, // Taiwan
{ regex: uWordBoundaryGroup("(Thailand|TH|THA|THAILAND|BANGKOK)"), iso3: "THA", flag: "🇹🇭", priority: 500 }, // Thailand
{ regex: uWordBoundaryGroup("(Турция|TR|TUR|TURKEY|ISTANBUL)"), iso3: "TUR", flag: "🇹🇷", priority: 510 }, // Turkey
{ regex: uWordBoundaryGroup("(UAE|United\s*Arab\s*Emirates|AE|ARE|DUBAI)"), iso3: "ARE", flag: "🇦🇪", priority: 520 }, // UAE
{ regex: uWordBoundaryGroup("(Великобритания|Англия|England|UK|GB|GBR|UNITED\s*KINGDOM)"), iso3: "GBR", flag: "🇬🇧", priority: 530 }, // UK
{ regex: uWordBoundaryGroup("(США|USA|US|UNITED\s*STATES|AMERICA|NEW\s*YORK|NYC)"), iso3: "USA", flag: "🇺🇸", priority: 540 }, // USA
{ regex: uWordBoundaryGroup("(Vietnam|VN|VNM|VIETNAM|HANOI)"), iso3: "VNM", flag: "🇻🇳", priority: 500 } // Vietnam
];
// 5) GeoIP mapping (ISO2 -> ISO3 + flag) used only if utils.geoip.lookup(ip) returns ISO2
const ISO2_TO_ISO3 = {
US: { iso3: "USA", flag: "🇺🇸" },
DE: { iso3: "DEU", flag: "🇩🇪" },
NL: { iso3: "NLD", flag: "🇳🇱" },
GB: { iso3: "GBR", flag: "🇬🇧" },
FR: { iso3: "FRA", flag: "🇫🇷" },
PL: { iso3: "POL", flag: "🇵🇱" },
FI: { iso3: "FIN", flag: "🇫🇮" },
SE: { iso3: "SWE", flag: "🇸🇪" },
NO: { iso3: "NOR", flag: "🇳🇴" },
CH: { iso3: "CHE", flag: "🇨🇭" },
EE: { iso3: "EST", flag: "🇪🇪" },
LV: { iso3: "LVA", flag: "🇱🇻" },
LT: { iso3: "LTU", flag: "🇱🇹" },
TR: { iso3: "TUR", flag: "🇹🇷" },
SG: { iso3: "SGP", flag: "🇸🇬" },
JP: { iso3: "JPN", flag: "🇯🇵" },
KR: { iso3: "KOR", flag: "🇰🇷" },
HK: { iso3: "HKG", flag: "🇭🇰" },
};
// 6) Protocol icons (based on proxy.type)
const PROTOCOL_ICONS = {
ss: "",
ssr: "",
vmess: "",
vless: "",
trojan: "",
http: "",
socks5: "",
snell: "",
wireguard: "",
hysteria: "",
hysteria2: "",
tuic: ""
};
const STANDARD_PORTS_BY_TYPE = {
wireguard: new Set(["51820"]),
vless: new Set(["443"]),
trojan: new Set(["443"]),
ss: new Set(["443"]),
};
const PROTOCOL_ICON_DEFAULT = ""; // fallback icon if type is unknown
const METATAG_RULES = {
// Keys are "network/type" OR "/type" (network-agnostic) OR "network/" (type-agnostic)
// Matching priority: exact "network/type" -> "/type" -> "network/" -> default
// 🅶🆃 🆃🆂 🆃🆅 🆆🆅 🆇🆅 🆆🅶 🅽🅸
pairMap: {
"grpc/trojan": "🅶🆃",
"tcp/ss": "🆃🆂‌",
"tcp/vless": "🆃🆅",
"ws/vless": "🆆🆅",
"xhttp/vless": "🆇🆅",
"/wireguard": "🆆🅶‌",
"/naive": "🅽🅸",
},
defaultPair: "▫️", // fallback if nothing matches
includeFallbackText: false, // if true, append "(net/type)" when defaultPair is used
};
// Port formatting: superscript digits with left padding to 4 chars
// 𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗
const PORT_FORMAT = {
padLeftTo: 3,
padChar: "0",
fancy: {
"0": "𝟎", "1": "𝟏", "2": "𝟐", "3": "𝟑", "4": "𝟒", "5": "𝟓", "6": "𝟔", "7": "𝟕", "8": "𝟖", "9": "𝟗",
},
};
///////////////////////
// HELPERS
///////////////////////
function normalizeToken(s) {
return String(s || "").trim().toLowerCase();
}
function uWordBoundaryGroup(inner) {
// Match if surrounded by non-letter/non-digit (Unicode-aware)
// We don't use lookbehind for max compatibility.
return new RegExp(`(?:^|[^\\p{L}\\p{N}])(?:${inner})(?=$|[^\\p{L}\\p{N}])`, "iu");
}
function portToFancy(port, type) {
let p = String(port ?? "").trim();
p = p.replace(/[^\d]/g, "");
if (!p) return "";
if (STANDARD_PORTS_BY_TYPE[type]?.has(p)) {
return "";
}
// left pad to fixed width
if (PORT_FORMAT.padLeftTo && p.length < PORT_FORMAT.padLeftTo) {
p = p.padStart(PORT_FORMAT.padLeftTo, PORT_FORMAT.padChar);
}
// map digits
let out = "";
for (const ch of p) out += PORT_FORMAT.fancy[ch] ?? ch;
return out;
}
function buildMetaTag(proxy) {
const net = safeStr(proxy && proxy.network) || "";
const typ = safeStr(proxy && proxy.type) || "";
const port = safeStr(proxy && proxy.port);
const { icon, matched } = metaPairIcon(net, typ);
const portSup = portToFancy(port, typ);
if (icon === METATAG_RULES.defaultPair && METATAG_RULES.includeFallbackText) {
return `${icon}${portSup}(${normalizeToken(net)}/${normalizeToken(typ)})`;
}
return `${icon}${portSup}`;
}
function metaPairIcon(network, type) {
const net = normalizeToken(network);
const typ = normalizeToken(type);
const exact = `${net}/${typ}`;
const typeOnly = `/${typ}`;
const netOnly = `${net}/`;
const m = METATAG_RULES.pairMap;
if (m[exact]) return { icon: m[exact], matched: exact };
if (m[typeOnly]) return { icon: m[typeOnly], matched: typeOnly };
if (m[netOnly]) return { icon: m[netOnly], matched: netOnly };
return { icon: METATAG_RULES.defaultPair, matched: null };
}
function isIPv4(str) {
if (typeof str !== "string") return false;
const m = str.match(/^(\d{1,3})(\.\d{1,3}){3}$/);
if (!m) return false;
return str.split(".").every(oct => {
const n = Number(oct);
return n >= 0 && n <= 255 && String(n) === oct.replace(/^0+(\d)/, "$1"); // avoids "001" weirdness
});
}
function uniq(arr) {
return [...new Set(arr.filter(Boolean))];
}
function sanitizeBaseName(name) {
let s = String(name || "");
// Remove noise patterns
for (const re of NOISE_PATTERNS) s = s.replace(re, " ");
// Collapse spaces
s = s.replace(/\s+/g, " ").trim();
return s;
}
function extractIconTagsAndStrip(name) {
let s = String(name || "");
const tags = [];
for (const r of ICON_RULES) {
if (r.regex.test(s)) {
tags.push(r.icon);
s = s.replace(r.regex, " ");
}
}
for (const t of NAME_NETWORK_TAGS) {
if (t.regex.test(s)) {
tags.push(t.tag);
s = s.replace(t.regex, " ");
}
}
return { stripped: s.replace(/\s+/g, " ").trim(), tags: uniq(tags) };
}
function detectCountryByName(name) {
const n = String(name || "");
// Order by priority, then first match wins
// Fast path: flag emoji
if (n.includes("🇦🇪")) return { iso3: "ARE", flag: "🇦🇪", priority: 1, source: "flag" };
if (n.includes("🇦🇷")) return { iso3: "ARG", flag: "🇦🇷", priority: 2, source: "flag" };
if (n.includes("🇦🇹")) return { iso3: "AUT", flag: "🇦🇹", priority: 3, source: "flag" };
if (n.includes("🇦🇺")) return { iso3: "AUS", flag: "🇦🇺", priority: 4, source: "flag" };
if (n.includes("🇧🇬")) return { iso3: "BGR", flag: "🇧🇬", priority: 5, source: "flag" };
if (n.includes("🇧🇾")) return { iso3: "BLR", flag: "🇧🇾", priority: 6, source: "flag" };
if (n.includes("🇧🇷")) return { iso3: "BRA", flag: "🇧🇷", priority: 7, source: "flag" };
if (n.includes("🇨🇦")) return { iso3: "CAN", flag: "🇨🇦", priority: 8, source: "flag" };
if (n.includes("🇨🇭")) return { iso3: "CHE", flag: "🇨🇭", priority: 9, source: "flag" };
if (n.includes("🇨🇳")) return { iso3: "CHN", flag: "🇨🇳", priority: 10, source: "flag" };
if (n.includes("🇨🇿")) return { iso3: "CZE", flag: "🇨🇿", priority: 11, source: "flag" };
if (n.includes("🇩🇪")) return { iso3: "DEU", flag: "🇩🇪", priority: 12, source: "flag" };
if (n.includes("🇩🇰")) return { iso3: "DNK", flag: "🇩🇰", priority: 13, source: "flag" };
if (n.includes("🇪🇪")) return { iso3: "EST", flag: "🇪🇪", priority: 14, source: "flag" };
if (n.includes("🇪🇬")) return { iso3: "EGY", flag: "🇪🇬", priority: 15, source: "flag" };
if (n.includes("🇪🇸")) return { iso3: "ESP", flag: "🇪🇸", priority: 16, source: "flag" };
if (n.includes("🇫🇮")) return { iso3: "FIN", flag: "🇫🇮", priority: 17, source: "flag" };
if (n.includes("🇫🇷")) return { iso3: "FRA", flag: "🇫🇷", priority: 18, source: "flag" };
if (n.includes("🇬🇧")) return { iso3: "GBR", flag: "🇬🇧", priority: 19, source: "flag" };
if (n.includes("🇬🇪")) return { iso3: "GEO", flag: "🇬🇪", priority: 20, source: "flag" };
if (n.includes("🇭🇰")) return { iso3: "HKG", flag: "🇭🇰", priority: 21, source: "flag" };
if (n.includes("🇮🇪")) return { iso3: "IRL", flag: "🇮🇪", priority: 22, source: "flag" };
if (n.includes("🇮🇱")) return { iso3: "ISR", flag: "🇮🇱", priority: 23, source: "flag" };
if (n.includes("🇮🇳")) return { iso3: "IND", flag: "🇮🇳", priority: 24, source: "flag" };
if (n.includes("🇮🇹")) return { iso3: "ITA", flag: "🇮🇹", priority: 25, source: "flag" };
if (n.includes("🇯🇵")) return { iso3: "JPN", flag: "🇯🇵", priority: 26, source: "flag" };
if (n.includes("🇰🇷")) return { iso3: "KOR", flag: "🇰🇷", priority: 27, source: "flag" };
if (n.includes("🇰🇿")) return { iso3: "KAZ", flag: "🇰🇿", priority: 28, source: "flag" };
if (n.includes("🇱🇹")) return { iso3: "LTU", flag: "🇱🇹", priority: 29, source: "flag" };
if (n.includes("🇱🇻")) return { iso3: "LVA", flag: "🇱🇻", priority: 30, source: "flag" };
if (n.includes("🇲🇩")) return { iso3: "MDA", flag: "🇲🇩", priority: 31, source: "flag" };
if (n.includes("🇲🇾")) return { iso3: "MYS", flag: "🇲🇾", priority: 32, source: "flag" };
if (n.includes("🇳🇬")) return { iso3: "NGA", flag: "🇳🇬", priority: 33, source: "flag" };
if (n.includes("🇳🇱")) return { iso3: "NLD", flag: "🇳🇱", priority: 34, source: "flag" };
if (n.includes("🇳🇴")) return { iso3: "NOR", flag: "🇳🇴", priority: 35, source: "flag" };
if (n.includes("🇵🇭")) return { iso3: "PHL", flag: "🇵🇭", priority: 36, source: "flag" };
if (n.includes("🇵🇱")) return { iso3: "POL", flag: "🇵🇱", priority: 37, source: "flag" };
if (n.includes("🇵🇹")) return { iso3: "PRT", flag: "🇵🇹", priority: 38, source: "flag" };
if (n.includes("🇷🇴")) return { iso3: "ROU", flag: "🇷🇴", priority: 39, source: "flag" };
if (n.includes("🇷🇺")) return { iso3: "RUS", flag: "🇷🇺", priority: 40, source: "flag" };
if (n.includes("🇸🇪")) return { iso3: "SWE", flag: "🇸🇪", priority: 41, source: "flag" };
if (n.includes("🇸🇬")) return { iso3: "SGP", flag: "🇸🇬", priority: 42, source: "flag" };
if (n.includes("🇹🇭")) return { iso3: "THA", flag: "🇹🇭", priority: 43, source: "flag" };
if (n.includes("🇹🇷")) return { iso3: "TUR", flag: "🇹🇷", priority: 44, source: "flag" };
if (n.includes("🇹🇼")) return { iso3: "TWN", flag: "🇹🇼", priority: 45, source: "flag" };
if (n.includes("🇺🇸")) return { iso3: "USA", flag: "🇺🇸", priority: 46, source: "flag" };
if (n.includes("🇻🇳")) return { iso3: "VNM", flag: "🇻🇳", priority: 47, source: "flag" };
const sorted = COUNTRY_RULES.slice().sort((a, b) => a.priority - b.priority);
for (const c of sorted) {
if (c.regex.test(n)) return { iso3: c.iso3, flag: c.flag, priority: c.priority, source: "name" };
}
return null;
}
function detectCountryByGeoIP(server, utils) {
if (!isIPv4(server)) return null;
if (!utils || !utils.geoip || typeof utils.geoip.lookup !== "function") return null;
try {
const geo = utils.geoip.lookup(server);
const iso2 = geo && (geo.country || geo.country_code || geo.iso_code);
if (!iso2 || typeof iso2 !== "string") return null;
const key = iso2.toUpperCase();
const mapped = ISO2_TO_ISO3[key];
if (mapped) return { iso3: mapped.iso3, flag: mapped.flag, priority: 900, source: "geoip" };
// Unknown ISO2: keep something sane
return { iso3: key, flag: "🏳️", priority: 950, source: "geoip" };
} catch (e) {
return null;
}
}
function pad2(n) {
const x = Number(n);
return x < 10 ? `0${x}` : String(x);
}
function safeStr(v) {
return (v === undefined || v === null) ? "" : String(v);
}
///////////////////////
// OPERATOR
///////////////////////
function operator(proxies, targetPlatform, utils) {
// Sub-Store sometimes passes utils as global $utils; sometimes as 3rd arg; sometimes not at all.
// We'll accept any of them without whining.
const U = utils || (typeof $utils !== "undefined" ? $utils : null);
const buckets = Object.create(null);
for (const proxy of proxies) {
const originalName = safeStr(proxy && proxy.name);
// 1) Extract tags (icons) from ORIGINAL name, then strip those keywords out
const iconStage = extractIconTagsAndStrip(originalName);
// 2) Sanitize remaining base name (remove marketing trash, brackets, etc.)
const cleanBase = sanitizeBaseName(iconStage.stripped);
// 3) Detect country (name first, then GeoIP)
const byName = detectCountryByName(originalName);
const byGeo = detectCountryByGeoIP(proxy && proxy.server, U);
const country = byName || byGeo || { iso3: "UNK", flag: "🏴‍☠️", priority: 9999, source: "fallback" };
// 4) Protocol icon (based on type)
const proto = PROTOCOL_ICONS[(proxy && proxy.type) || ""] || PROTOCOL_ICON_DEFAULT;
// 5) Network/type/port tag (from proxy fields)
const metaTag = buildMetaTag(proxy);
// 6) Prepare bucket key
const key = country.iso3;
if (!buckets[key]) {
buckets[key] = {
country,
list: []
};
}
// Keep meta used for sorting and final formatting
buckets[key].list.push({
proxy,
_meta: {
originalName,
cleanBase,
iconTags: iconStage.tags,
proto,
metaTag
}
});
}
// 7) Sort buckets by priority
const bucketKeys = Object.keys(buckets).sort((a, b) => {
return (buckets[a].country.priority || 9999) - (buckets[b].country.priority || 9999);
});
// 8) Sort inside each country bucket and rename with per-country numbering
const result = [];
for (const key of bucketKeys) {
const group = buckets[key];
group.list.sort((A, B) => {
// Tags do not affect numbering: sort only by sanitized base + server:port as tie-breaker
const an = A._meta.cleanBase.toLowerCase();
const bn = B._meta.cleanBase.toLowerCase();
if (an !== bn) return an.localeCompare(bn);
const as = `${safeStr(A.proxy.server)}:${safeStr(A.proxy.port)}`;
const bs = `${safeStr(B.proxy.server)}:${safeStr(B.proxy.port)}`;
return as.localeCompare(bs);
});
for (let i = 0; i < group.list.length; i++) {
const item = group.list[i];
const p = item.proxy;
const num = pad2(i + 1);
const debugSuffix = DEBUG_APPEND_ORIGINAL_NAME
? `${item._meta.originalName}`
: "";
const tagStr = item._meta.iconTags.length ? ` ${item._meta.iconTags.join(" ")}` : "";
p.name = `${group.country.flag}${item._meta.metaTag} ${group.country.iso3}-${num} ${item._meta.proto}${tagStr} ${debugSuffix}`
.replace(/\s+/g, " ")
.trim();
result.push(p);
}
}
return result;
}

View File

@@ -0,0 +1,110 @@
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(
obj,
(k, v) => {
if (typeof v === "object" && v !== null) {
if (seen.has(v)) return "[Circular]";
seen.add(v);
}
if (typeof v === "function") return `[Function: ${v.name || "anonymous"}]`;
if (typeof v === "bigint") return v.toString();
return v;
},
2
);
}
function pickEnvSample() {
try {
const env = (typeof process !== "undefined" && process && process.env) ? process.env : null;
if (!env) return null;
// only show safe-ish keys, no full dump
const keys = Object.keys(env).sort();
const filtered = keys.filter(k =>
k.toLowerCase().includes("sub") ||
k.toLowerCase().includes("store") ||
k.toLowerCase().includes("script") ||
k.toLowerCase().includes("url") ||
k.toLowerCase().includes("option") ||
k.toLowerCase().includes("param")
);
const sample = {};
for (const k of filtered.slice(0, 50)) sample[k] = env[k];
return { keysCount: keys.length, filteredKeys: filtered.slice(0, 100), sample };
} catch (e) {
return { error: String(e) };
}
}
function getGlobalDollarKeys() {
try {
return Object.getOwnPropertyNames(globalThis).filter(k => k.startsWith("$")).sort();
} catch {
return [];
}
}
// Safe "typeof" probes: never throws even if variable doesn't exist
const probes = {
$content: typeof $content,
$files: typeof $files,
$options: typeof $options,
$params: typeof $params,
$args: typeof $args,
$arguments: typeof $arguments,
$argument: typeof $argument,
$argv: typeof $argv,
$ctx: typeof $ctx,
$context: typeof $context,
$request: typeof $request,
$req: typeof $req,
$url: typeof $url,
$scriptUrl: typeof $scriptUrl,
$script_url: typeof $script_url,
ProxyUtils: typeof ProxyUtils,
produceArtifact: typeof produceArtifact,
process: typeof process,
};
const values = {};
function maybeSet(name, getter) {
try {
const v = getter();
// Avoid huge outputs
if (typeof v === "string") values[name] = v.length > 800 ? v.slice(0, 800) + "…(truncated)" : v;
else values[name] = v;
} catch (e) {
values[name] = { error: String(e) };
}
}
maybeSet("$options", () => (typeof $options !== "undefined" ? $options : null));
maybeSet("$params", () => (typeof $params !== "undefined" ? $params : null));
maybeSet("$args", () => (typeof $args !== "undefined" ? $args : null));
maybeSet("$arguments", () => (typeof $arguments !== "undefined" ? $arguments : null));
maybeSet("$argument", () => (typeof $argument !== "undefined" ? $argument : null));
maybeSet("$ctx", () => (typeof $ctx !== "undefined" ? $ctx : null));
maybeSet("$request", () => (typeof $request !== "undefined" ? $request : null));
maybeSet("$url", () => (typeof $url !== "undefined" ? $url : null));
maybeSet("$scriptUrl", () => (typeof $scriptUrl !== "undefined" ? $scriptUrl : null));
maybeSet("$script_url", () => (typeof $script_url !== "undefined" ? $script_url : null));
maybeSet("$contentPreview", () => (typeof $content === "string" ? $content.slice(0, 300) : $content));
maybeSet("$contentLength", () => (typeof $content === "string" ? $content.length : null));
maybeSet("$files", () => (typeof $files !== "undefined" ? $files : null));
const report = {
probes,
values,
globalDollarKeys: getGlobalDollarKeys(),
envSample: pickEnvSample(),
};
$content = safeStringify(report);

File diff suppressed because one or more lines are too long

13
icons/svg/numbers/n01.svg Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 496.158 496.158" xml:space="preserve">
<path style="fill:#56B48C;" d="M248.082,0.003C111.07,0.003,0,111.061,0,248.085c0,137,111.07,248.07,248.082,248.07
c137.006,0,248.076-111.07,248.076-248.07C496.158,111.061,385.088,0.003,248.082,0.003z"/>
<path style="fill:#FFFFFF;" d="M278.767,145.419c-3.126-4.003-7.276-6.006-12.451-6.006c-4.591,0-7.716,0.879-9.375,2.637
c-1.662,1.758-5.226,6.445-10.693,14.063c-5.47,7.617-11.744,14.502-18.823,20.654c-7.082,6.152-16.53,12.012-28.345,17.578
c-7.91,3.712-13.429,6.738-16.553,9.082c-3.126,2.344-4.688,6.006-4.688,10.986c0,4.298,1.586,8.082,4.761,11.353
c3.172,3.273,6.812,4.907,10.913,4.907c8.592,0,25.292-9.521,50.098-28.564V335.41c0,7.814,1.806,13.722,5.42,17.725
c3.612,4.003,8.397,6.006,14.355,6.006c13.378,0,20.068-9.814,20.068-29.443V161.972
C283.455,154.941,281.892,149.425,278.767,145.419z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

19
icons/svg/numbers/n02.svg Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 496.158 496.158" xml:space="preserve">
<path style="fill:#56B48C;" d="M248.082,0.003C111.07,0.003,0,111.061,0,248.085c0,137,111.07,248.07,248.082,248.07
c137.006,0,248.076-111.07,248.076-248.07C496.158,111.061,385.088,0.003,248.082,0.003z"/>
<path style="fill:#FFFFFF;" d="M319.783,325.595c-4.005-3.124-9.814-4.688-17.432-4.688h-76.465c2.44-3.71,4.834-6.885,7.178-9.521
c5.468-6.64,15.55-15.967,30.249-27.979c14.696-12.012,25.17-20.824,31.421-26.44c6.249-5.614,12.378-13.378,18.384-23.291
c6.006-9.911,9.009-20.922,9.009-33.032c0-7.713-1.442-15.161-4.321-22.339c-2.882-7.178-6.91-13.5-12.085-18.97
c-5.177-5.468-11.183-9.764-18.018-12.891c-10.547-4.688-23.291-7.031-38.232-7.031c-12.403,0-23.218,1.831-32.446,5.493
s-16.846,8.473-22.852,14.429c-6.006,5.958-10.524,12.598-13.55,19.922c-3.028,7.324-4.541,14.355-4.541,21.094
c0,5.566,1.611,9.961,4.834,13.184s7.274,4.834,12.158,4.834c5.566,0,9.789-1.758,12.671-5.273
c2.879-3.516,5.468-8.544,7.764-15.088c2.293-6.542,3.93-10.547,4.907-12.012c7.324-11.229,17.381-16.846,30.176-16.846
c6.054,0,11.646,1.369,16.772,4.102c5.127,2.735,9.178,6.569,12.158,11.499c2.978,4.933,4.468,10.524,4.468,16.772
c0,5.763-1.392,11.646-4.175,17.651s-6.837,11.865-12.158,17.578c-5.324,5.713-11.989,11.403-19.995,17.065
c-4.493,3.028-11.964,9.352-22.412,18.97c-10.451,9.62-22.169,21.167-35.156,34.644c-3.126,3.321-6.006,7.887-8.643,13.696
c-2.637,5.812-3.955,10.474-3.955,13.989c0,5.47,2.051,10.231,6.152,14.282c4.102,4.054,9.814,6.079,17.139,6.079H306.6
c6.445,0,11.254-1.659,14.429-4.98c3.172-3.319,4.761-7.372,4.761-12.158C325.789,332.97,323.786,328.722,319.783,325.595z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

24
icons/svg/numbers/n03.svg Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 496.158 496.158" xml:space="preserve">
<path style="fill:#56B48C;" d="M248.082,0.003C111.07,0.003,0,111.061,0,248.085c0,137,111.07,248.07,248.082,248.07
c137.006,0,248.076-111.07,248.076-248.07C496.158,111.061,385.088,0.003,248.082,0.003z"/>
<path style="fill:#FFFFFF;" d="M319.637,269.711c-2.637-6.395-6.569-12.231-11.792-17.505c-5.226-5.273-11.646-9.961-19.263-14.063
c7.91-6.64,13.989-13.451,18.237-20.435c4.248-6.981,6.372-15.355,6.372-25.122c0-7.42-1.465-14.355-4.395-20.801
s-7.276-12.108-13.037-16.992c-5.763-4.882-12.55-8.617-20.361-11.206c-7.814-2.586-16.457-3.882-25.928-3.882
c-10.84,0-20.654,1.538-29.443,4.614s-16.139,7.155-22.046,12.231c-5.91,5.079-10.4,10.426-13.477,16.04
c-3.076,5.617-4.614,10.963-4.614,16.04c0,5.273,1.634,9.499,4.907,12.671c3.271,3.175,6.859,4.761,10.767,4.761
c3.319,0,6.249-0.586,8.789-1.758c2.538-1.172,4.296-2.783,5.273-4.834c1.659-3.809,3.49-7.86,5.493-12.158
c2-4.296,4.125-7.812,6.372-10.547c2.245-2.733,5.296-4.93,9.155-6.592c3.856-1.659,8.764-2.49,14.722-2.49
c8.789,0,15.77,2.71,20.947,8.13c5.175,5.42,7.764,11.891,7.764,19.409c0,9.865-3.248,17.432-9.741,22.705
c-6.496,5.273-14.234,7.91-23.218,7.91h-6.006c-6.935,0-12.158,1.442-15.674,4.321c-3.516,2.882-5.273,6.665-5.273,11.353
c0,4.786,1.465,8.521,4.395,11.206c2.93,2.687,7.079,4.028,12.451,4.028c1.172,0,3.809-0.194,7.91-0.586
c4.102-0.389,7.127-0.586,9.082-0.586c11.133,0,19.823,3.248,26.074,9.741c6.249,6.496,9.375,15.454,9.375,26.88
c0,7.716-1.831,14.502-5.493,20.361s-8.302,10.279-13.916,13.257c-5.617,2.98-11.451,4.468-17.505,4.468
c-10.547,0-18.727-3.296-24.536-9.888c-5.812-6.592-11.256-16.674-16.333-30.249c-0.783-2.245-2.442-4.175-4.98-5.786
c-2.541-1.611-5.177-2.417-7.91-2.417c-5.47,0-10.034,1.735-13.696,5.2c-3.662,3.468-5.493,8.034-5.493,13.696
c0,4.395,1.538,9.961,4.614,16.699s7.617,13.257,13.623,19.556s13.646,11.549,22.925,15.747c9.276,4.198,19.775,6.299,31.494,6.299
c11.522,0,22.046-1.831,31.567-5.493s17.748-8.739,24.683-15.234c6.933-6.493,12.181-13.891,15.747-22.192
c3.563-8.299,5.347-16.894,5.347-25.781C323.592,283.018,322.273,276.109,319.637,269.711z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="si-glyph si-glyph-hat">
<title>629</title>
<defs>
</defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(0.000000, 3.000000)" fill="#434343">
<path d="M12.708,5.211 C12.278,6.128 10.548,7.239 8,7.239 C5.468,7.239 3.667,6.142 3.288,5.226 C1.375,5.312 0,6.456 0,7.136 C0,8.264 3.581,9.864 8,9.864 C12.418,9.864 16,8.264 16,7.136 C16,6.447 14.625,5.569 12.708,5.211 L12.708,5.211 Z" class="si-glyph-fill">
</path>
<path d="M10.077,0.197 C9.561,0.197 8.495,0.551 8.012,0.551 C7.529,0.551 6.463,0.197 5.948,0.197 C4.834,0.197 4.138,1.569 4.041,2.094 L4.021,4.521 C4.583,5.027 5.632,5.663 6.782,5.759 C7.182,5.792 7.604,5.812 8.043,5.812 C8.48,5.812 8.901,5.792 9.302,5.759 C10.452,5.662 11.376,5.105 11.97,4.538 L11.965,2.125 C11.879,1.579 11.181,0.197 10.077,0.197 L10.077,0.197 Z" class="si-glyph-fill">
</path>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 365.789 365.789" xml:space="preserve">
<g id="XMLID_7_">
<path id="XMLID_8_" d="M137.743,18.96v139.676c0,62.873,51.15,114.023,114.022,114.023c62.873,0,114.023-51.15,114.023-114.023
V18.96H137.743z M225.575,98.13c0,14.77-12.017,26.787-26.787,26.787S172.001,112.9,172.001,98.13
c0-14.771,12.017-26.787,26.787-26.787S225.575,83.359,225.575,98.13z M277.957,98.13c0-14.771,12.017-26.787,26.787-26.787
s26.787,12.017,26.787,26.787c0,14.77-12.017,26.787-26.787,26.787S277.957,112.9,277.957,98.13z M219.562,172.001
c3.773,8.898,17.209,15.894,32.204,15.894s28.432-6.996,32.205-15.894h33.146c-1.503,13.065-8.85,25.31-20.672,34.176
c-12.122,9.092-27.989,14.1-44.679,14.1c-16.688,0-32.556-5.007-44.678-14.1c-11.823-8.867-19.17-21.111-20.673-34.176H219.562z"/>
<path id="XMLID_13_" d="M177.154,284.553l-1.181-0.703h-29.747c-3.772-8.898-17.209-15.894-32.203-15.894
c-14.995,0-28.432,6.996-32.205,15.894H48.673c1.503-13.065,8.85-25.31,20.672-34.176c12.124-9.093,27.991-14.1,44.679-14.1
c1.293,0,2.678,0.041,4.356,0.129l8.923,0.469l-4.268-7.85c-11.563-21.271-17.675-45.369-17.675-69.687V93.13H0v139.676
c0,62.873,51.15,114.023,114.023,114.023c34.758,0,67.232-15.701,89.099-43.077l4.313-5.402l-6.48-2.406
C192.743,292.895,184.736,289.063,177.154,284.553z M61.045,145.512c14.771,0,26.787,12.017,26.787,26.787
c0,14.771-12.017,26.787-26.787,26.787c-14.77,0-26.786-12.017-26.786-26.787C34.259,157.529,46.275,145.512,61.045,145.512z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="-0.5 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m22.171 19.68-7.352-11.311v-5.407h1.708v-2.962h-10.429v2.965h1.722v5.407l-7.366 11.308c-.285.431-.455.96-.455 1.528 0 1.542 1.25 2.792 2.792 2.792h.003 17.031c1.544-.003 2.795-1.255 2.795-2.8 0-.565-.167-1.091-.455-1.531l.007.011zm-.905 2.302c-.282.513-.818.854-1.434.854-.002 0-.004 0-.006 0h-17.032c-.001 0-.002 0-.003 0-.904 0-1.636-.732-1.636-1.636 0-.33.098-.638.266-.895l-.004.006 7.549-11.6v-5.751h4.686v5.754l7.541 11.6c.17.251.272.561.272.895 0 .285-.074.553-.204.785l.004-.008z"/><path d="m14.412 12.351h-6.191l-5.655 8.698c-.03.045-.048.1-.048.159 0 .051.013.098.036.14l-.001-.001c.05.087.142.145.248.146h17.031.001c.106 0 .198-.058.247-.145l.001-.001c.022-.04.036-.088.036-.138 0-.059-.018-.115-.049-.16l.001.001z"/></svg>

After

Width:  |  Height:  |  Size: 969 B

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.544 1.49857C16.8278 1.08878 16.0621 1.24503 15.3437 1.54072C14.6378 1.83126 13.769 2.3458 12.7076 2.97441L12.0804 3.34588C11.394 3.75237 11.2339 3.83012 11.0785 3.84959C10.9285 3.86837 10.7686 3.83515 10.0272 3.60702L9.34735 3.3978C8.20554 3.0464 7.26483 2.75689 6.52104 2.64685C5.74973 2.53274 4.98782 2.57927 4.38078 3.13487C3.77784 3.68672 3.65464 4.4423 3.68494 5.22709C3.71433 5.98815 3.89821 6.96778 4.12234 8.16182L4.25502 8.86874C4.3996 9.63911 4.41777 9.81347 4.38149 9.97264C4.34436 10.1356 4.24727 10.294 3.77952 10.9431L3.3514 11.5371C2.62441 12.5457 2.03065 13.3695 1.67313 14.0516C1.30926 14.7457 1.08259 15.4902 1.40213 16.2436C1.72676 17.009 2.42603 17.3422 3.17507 17.5274C3.90291 17.7074 4.88842 17.7975 6.08514 17.907L6.79813 17.9723C7.57839 18.0437 7.73636 18.0742 7.85944 18.1446C7.98151 18.2144 8.08585 18.3329 8.53855 18.9671L8.95248 19.5469C9.64616 20.5187 10.2182 21.32 10.74 21.8553C11.2791 22.4083 11.9187 22.8384 12.743 22.7351C13.5531 22.6337 14.0878 22.0723 14.5134 21.4115C14.9307 20.7636 15.3538 19.842 15.8718 18.7137L16.1769 18.0494C16.2869 17.8098 16.3703 17.6316 16.4373 17.4962C16.4477 17.5079 16.4585 17.5194 16.4697 17.5306L20.4697 21.5306C20.7626 21.8235 21.2374 21.8235 21.5303 21.5306C21.8232 21.2377 21.8232 20.7628 21.5303 20.4699L17.6859 16.6255C17.7093 16.6173 17.7333 16.6088 17.758 16.6002L18.441 16.361C19.5975 15.956 20.5446 15.6243 21.2233 15.2696C21.9207 14.9051 22.5191 14.4235 22.7004 13.628C22.8839 12.8228 22.5399 12.1402 22.0536 11.5343C21.5851 10.9503 20.8613 10.2874 19.9839 9.48369L19.4604 9.00421C18.8892 8.48099 18.7829 8.36232 18.7259 8.22792C18.6669 8.08891 18.6544 7.91704 18.6665 7.12305L18.6775 6.39777C18.6962 5.17362 18.7114 4.1698 18.6122 3.41751C18.511 2.65005 18.2629 1.90987 17.544 1.49857ZM13.4178 4.29716C14.5467 3.62858 15.3217 3.17189 15.9146 2.92782C16.5071 2.68397 16.7019 2.74496 16.7991 2.80054C16.8936 2.8546 17.0424 2.98631 17.1251 3.61361C17.2081 4.24305 17.1965 5.13504 17.1767 6.43877L17.1647 7.21994C17.1547 7.82701 17.1461 8.34464 17.345 8.81361C17.5457 9.2869 17.9243 9.63271 18.3612 10.0316L18.4472 10.1103L18.9225 10.5456C19.8602 11.4046 20.4962 11.9901 20.8837 12.473C21.2622 12.9446 21.2697 13.1551 21.2379 13.2947C21.2039 13.444 21.0914 13.646 20.5285 13.9402C19.9583 14.2382 19.1162 14.5352 17.8865 14.9659L17.1539 15.2223C16.5752 15.4242 16.0914 15.593 15.7122 15.94C15.3345 16.2857 15.1202 16.7539 14.8621 17.3179L14.5343 18.0319C13.9843 19.2298 13.6047 20.0522 13.2524 20.5992C12.9015 21.1439 12.6931 21.2297 12.5566 21.2468C12.4345 21.2621 12.2378 21.2428 11.8141 20.8082C11.3826 20.3655 10.8767 19.6606 10.1351 18.6219L9.75937 18.0955L9.69049 17.9988C9.34758 17.5169 9.04872 17.0968 8.60434 16.8426C8.16092 16.5889 7.64642 16.5426 7.05381 16.4893L6.93485 16.4785L6.28697 16.4192C5.00882 16.3022 4.1387 16.2205 3.53516 16.0712C2.93966 15.924 2.82806 15.764 2.78306 15.6579C2.73296 15.5398 2.7003 15.3229 3.00168 14.748C3.30352 14.1721 3.83249 13.435 4.60438 12.364L5.06497 11.7251C5.42825 11.2224 5.73046 10.8041 5.844 10.3059C5.95834 9.80418 5.86331 9.30141 5.75053 8.70473L5.60821 7.94698C5.36962 6.67572 5.20841 5.80603 5.18383 5.16922C5.15965 4.54303 5.27846 4.34669 5.39353 4.24137C5.50449 4.13981 5.69883 4.04154 6.30151 4.1307C6.91831 4.22195 7.7488 4.4753 8.96802 4.85048L9.6966 5.0748C10.2652 5.25066 10.7542 5.40192 11.2649 5.33796C11.7704 5.27465 12.2154 5.01032 12.7422 4.69734L13.4178 4.29716Z" fill="#1C274C"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.04582 10.8715C8.52718 10.6007 8.6979 9.99103 8.42713 9.50967C8.15637 9.02831 7.54665 8.85759 7.0653 9.12836L3.50974 11.1284C3.18725 11.3098 2.99128 11.6544 3.0003 12.0243C3.00931 12.3942 3.22184 12.7289 3.55279 12.8944L5.63258 13.9343L3.50974 15.1284C3.18725 15.3098 2.99128 15.6544 3.0003 16.0243C3.00931 16.3942 3.22184 16.7289 3.55279 16.8944L11.5528 20.8944C11.8343 21.0351 12.1657 21.0351 12.4472 20.8944L20.4472 16.8944C20.7782 16.7289 20.9907 16.3942 20.9997 16.0243C21.0087 15.6544 20.8128 15.3098 20.4903 15.1284L18.3674 13.9343L20.4472 12.8944C20.7782 12.7289 20.9907 12.3942 20.9997 12.0243C21.0087 11.6544 20.8128 11.3098 20.4903 11.1284L16.9347 9.12836C16.4533 8.85759 15.8436 9.02831 15.5729 9.50967C15.3021 9.99103 15.4728 10.6007 15.9542 10.8715L17.8679 11.948L12 14.8819L6.13213 11.948L8.04582 10.8715ZM16.2077 15.0141L12.4472 16.8944C12.1657 17.0351 11.8343 17.0351 11.5528 16.8944L7.7923 15.0141L6.13213 15.948L12 18.8819L17.8679 15.948L16.2077 15.0141Z" fill="#152C70"/>
<path d="M12.4472 3.10557C12.1657 2.96481 11.8343 2.96481 11.5528 3.10557L3.55279 7.10557C3.214 7.27496 3 7.62123 3 8C3 8.37877 3.214 8.72504 3.55279 8.89443L11.5528 12.8944C11.8343 13.0352 12.1657 13.0352 12.4472 12.8944L20.4472 8.89443C20.786 8.72504 21 8.37877 21 8C21 7.62123 20.786 7.27496 20.4472 7.10557L12.4472 3.10557Z" fill="#4296FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -51,3 +51,15 @@ proxies:
h2: 430880481
h3: 1214405368
h4: 1739253821
- name: "cadian-adguard"
type: socks5
server: 192.168.0.3
port: 1080
username: adguard
password: adguard
# tls: true
# fingerprint: xxxx
# skip-cert-verify: true
udp: true
# ip-version: ipv6

View File

@@ -1,24 +0,0 @@
# Generated on: 2025-12-02 09:51:50
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
# Keenetic: interface Wireguard0 wireguard asc 4 10 50 110 20 211644422 533572853 237534087 1314455475
[Interface]
Address = 100.70.184.183/32
DNS = 8.8.8.8, 8.8.4.4
PrivateKey = ZiA7zQ9osP4nEoB5jryU99gpHFg6cTprfiaA1nV/pks=
Jc = 4
Jmin = 10
Jmax = 50
S1 = 110
S2 = 20
H1 = 211644422
H2 = 533572853
H3 = 237534087
H4 = 1314455475
[Peer]
PublicKey = 5uUhd8S8MjnULEEAiSQysBi+JO6zfrXC0QIIsqsjUkc=
PresharedKey = aJ4XS6+QgVasEcGP+5DInldM5i/YSR6PPmmix0wMtFA=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 5.189.202.78:46358
PersistentKeepalive = 25

View File

@@ -1,20 +0,0 @@
[Interface]
PrivateKey = mJd8aibZI0iuQqPxpTdbs1Enb9+Ury7ido20lu2NrWo=
Address = 10.136.82.167/32
DNS = 1.1.1.1, 8.8.4.4
MTU = 1380
Jc = 43
Jmin = 50
Jmax = 70
S1 = 110
S2 = 120
H1 = 1593635057
H2 = 430880481
H3 = 1214405368
H4 = 1739253821
[Peer]
PublicKey = gbUPMNfaxgRSGD3xcnnbAJSclxfnOyh4U1qqmYMWmCI=
PresharedKey = SCz82d6cfj9bfdlUyHwEBYC3u4T3znL6wFpJ7dMQYbM=
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Endpoint = nl02awg.kcufwfgnkr.net:60136

View File

@@ -1,24 +0,0 @@
# Generated on: 2025-12-02 09:35:19
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
# Keenetic: interface Wireguard0 wireguard asc 2 10 50 18 122 1512494805 1147470590 1658720028 1826833034
[Interface]
Address = 100.71.64.86/32
DNS = 8.8.8.8, 8.8.4.4
PrivateKey = /K20yRTTw44Ged/fXjdf4hSLnr33igHMl0SAXy0aMCE=
Jc = 2
Jmin = 10
Jmax = 50
S1 = 18
S2 = 122
H1 = 1512494805
H2 = 1147470590
H3 = 1658720028
H4 = 1826833034
[Peer]
PublicKey = Z01PDi29KeHvdtThNoSmXdxG4zyT1yFbulcI7AA5nQg=
PresharedKey = uExSfwmdNaUTiJmyNNMuoDavJEKdrdsAqNaVcZ/U00Y=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 212.23.222.12:37089
PersistentKeepalive = 25

View File

@@ -1,24 +0,0 @@
# Generated on: 2025-12-02 09:46:50
# VPN Key: vpn://vpn://AAAA_3icdY1PC4IwGIe_iuxcHYwKu4VQIB4Uieo0xlw2dH-cm6Hid2-bRKdOL7zPw--ZAEeMgGMAToyTkaIgU4RRw8AqACXpsKJSU8H_GFjwJ61gT1S3SKF9IknhAuxjAh1RPcUE6kH6EFpm1vI381WkElpg0XjtXTlkLLNrhms12Fv6CWXA7EJGv2CJNPIdl63J4HgbX8Yy2qebq8riXCZJURcoqW67_pynbSPDcHtoHtEdgXn-AEBTVs8=
# Keenetic: interface Wireguard0 wireguard asc 3 10 50 145 34 203715079 914012290 174842657 1514769902
[Interface]
Address = 100.71.64.155/32
DNS = 8.8.8.8, 8.8.4.4
PrivateKey = Vuk0huRtjGpA7KtWuQN/kJE0CRFOFs3JysN71nDFGqg=
Jc = 3
Jmin = 10
Jmax = 50
S1 = 145
S2 = 34
H1 = 203715079
H2 = 914012290
H3 = 174842657
H4 = 1514769902
[Peer]
PublicKey = ZqTlR9tYsMacuawQQaU6UCoXdT1exYJD2tzFXTpN9zs=
PresharedKey = oOJNRLL+5aOgimi28Lwq18w6xCrry4AFLt68fhbUSlk=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 5.8.93.236:42854
PersistentKeepalive = 25

View File

@@ -1,12 +0,0 @@
ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpER1E3THlXRHBSOVFrU2ZvZi1zcnN3@151.243.101.40:2060#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
vmess://eyJhZGQiOiAiMTUxLjI0My4xMDEuNDAiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogImEzZTc1OTM2LTBlNDQtNGJjOS1iOTdlLWExZmRiMjc2Y2NhNCIsICJuZXQiOiAidGNwIiwgInBhdGgiOiAiLyIsICJwb3J0IjogODA4MSwgInBzIjogIlx1MjZhMVx1MDQxMlx1MDQzNVx1MDQzYlx1MDQzOFx1MDQzYVx1MDQzZVx1MDQzMVx1MDQ0MFx1MDQzOFx1MDQ0Mlx1MDQzMFx1MDQzZFx1MDQzOFx1MDQ0ZiAtIEdCX1QwNzQwNTUiLCAic2N5IjogImF1dG8iLCAidGxzIjogIm5vbmUiLCAidHlwZSI6ICJodHRwIiwgInYiOiAiMiJ9
vmess://eyJhZGQiOiAiMTUxLjI0My4xMDEuNDAiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogImEzZTc1OTM2LTBlNDQtNGJjOS1iOTdlLWExZmRiMjc2Y2NhNCIsICJuZXQiOiAid3MiLCAicGF0aCI6ICIvIiwgInBvcnQiOiA4MDgwLCAicHMiOiAiXHUyNmExXHUwNDEyXHUwNDM1XHUwNDNiXHUwNDM4XHUwNDNhXHUwNDNlXHUwNDMxXHUwNDQwXHUwNDM4XHUwNDQyXHUwNDMwXHUwNDNkXHUwNDM4XHUwNDRmIC0gR0JfVDA3NDA1NSIsICJzY3kiOiAiYXV0byIsICJ0bHMiOiAibm9uZSIsICJ0eXBlIjogIiIsICJ2IjogIjIifQ==
vless://ca1a269d-6409-4bbf-a4e2-1e9cb04490e2@151.243.101.40:8443?security=reality&type=tcp&headerType=&path=&host=&sni=discordapp.com&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=6ba85179e30d4fc2#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
vless://ca1a269d-6409-4bbf-a4e2-1e9cb04490e2@151.243.101.40:2053?security=reality&type=grpc&headerType=&serviceName=xyz&authority=&mode=gun&sni=discordapp.com&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
trojan://TEx16RQixr5cJ3rCBz1Ddw@151.243.101.40:2058?security=tls&type=ws&headerType=&path=%2F&host=&sni=uk.hydranet.space&fp=#%E2%9A%A1%D0%92%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%B1%D1%80%D0%B8%D1%82%D0%B0%D0%BD%D0%B8%D1%8F%20-%20GB_T074055
ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp2aVluTUlnYW83TjlIbVlCWHV5ZnRB@109.235.48.181:2060#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
vmess://eyJhZGQiOiAiMTA5LjIzNS40OC4xODEiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogIjM1Y2EyMDY5LWFmYmYtNDkwMi04ZWIwLWViYjUxZTUwYjNiMyIsICJuZXQiOiAidGNwIiwgInBhdGgiOiAiLyIsICJwb3J0IjogODA4MSwgInBzIjogIlx1MjZhMVx1MDQxZFx1MDQzOFx1MDQzNFx1MDQzNVx1MDQ0MFx1MDQzYlx1MDQzMFx1MDQzZFx1MDQzNFx1MDQ0YiAtIE5MX1QwNzg3NTYiLCAic2N5IjogImF1dG8iLCAidGxzIjogIm5vbmUiLCAidHlwZSI6ICJodHRwIiwgInYiOiAiMiJ9
vmess://eyJhZGQiOiAiMTA5LjIzNS40OC4xODEiLCAiYWlkIjogIjAiLCAiaG9zdCI6ICJnb29nbGUuY29tIiwgImlkIjogIjM1Y2EyMDY5LWFmYmYtNDkwMi04ZWIwLWViYjUxZTUwYjNiMyIsICJuZXQiOiAid3MiLCAicGF0aCI6ICIvIiwgInBvcnQiOiA4MDgwLCAicHMiOiAiXHUyNmExXHUwNDFkXHUwNDM4XHUwNDM0XHUwNDM1XHUwNDQwXHUwNDNiXHUwNDMwXHUwNDNkXHUwNDM0XHUwNDRiIC0gTkxfVDA3ODc1NiIsICJzY3kiOiAiYXV0byIsICJ0bHMiOiAibm9uZSIsICJ0eXBlIjogIiIsICJ2IjogIjIifQ==
vless://b9ae9a97-18da-4c5d-9ada-3b0a92d4813e@109.235.48.181:8443?security=reality&type=tcp&headerType=&path=&host=&sni=vkvideo.ru&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=6ba85179e30d4fc2#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
vless://b9ae9a97-18da-4c5d-9ada-3b0a92d4813e@109.235.48.181:2053?security=reality&type=grpc&headerType=&serviceName=xyz&authority=&mode=gun&sni=vkvideo.ru&fp=chrome&pbk=SbVKOEMjK0sIlbwg4akyBg5mL5KZwwB-ed4eEE7YnRc&sid=#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756
trojan://tbPsjmK8x6xSDW1vTO8V5g@109.235.48.181:2058?security=tls&type=ws&headerType=&path=%2F&host=&sni=nl2.hydranet.space&fp=#%E2%9A%A1%D0%9D%D0%B8%D0%B4%D0%B5%D1%80%D0%BB%D0%B0%D0%BD%D0%B4%D1%8B%20-%20NL_T078756

View File

@@ -0,0 +1,3 @@
payload:
- SRC-IP-CIDR,192.168.10.103/32
- SRC-IP-CIDR,192.168.10.86/32

View File

@@ -1,4 +1,5 @@
payload:
- SRC-IP-CIDR,192.168.10.203/32
- SRC-IP-CIDR,192.168.10.204/32
- SRC-IP-CIDR,100.98.138.18/32 # DTS-TAB-S7-NET
- SRC-IP-CIDR,192.168.0.101/32 # DTS-TAB-S7-NET

View File

@@ -4,4 +4,8 @@
payload:
- GEOSITE,category-novel
- DOMAIN-SUFFIX,libgen.li
- DOMAIN-SUFFIX,libgen.li
- DOMAIN-SUFFIX,gaminik.net
- DOMAIN-SUFFIX,gaminik.cn
- DOMAIN-SUFFIX,langbag.com

View File

@@ -65,4 +65,9 @@ payload:
- DOMAIN-SUFFIX,redwap.me
- DOMAIN-SUFFIX,pervertium.com
- DOMAIN-SUFFIX,tubesafari.com
- DOMAIN-SUFFIX,tubesafari.com
- DOMAIN-SUFFIX,growcdnssedge.com
- DOMAIN-SUFFIX,flixcdn.com
- DOMAIN-SUFFIX,noodlemagazine.com
- DOMAIN-SUFFIX,pvvstream.pro

View File

@@ -1,7 +1,5 @@
payload:
- DOMAIN-SUFFIX,globus.ru
- DOMAIN-SUFFIX,samokat.ru
- DOMAIN-SUFFIX,smartmed.pro
- DOMAIN-SUFFIX,megamarket.ru
- DOMAIN-SUFFIX,anextel.ru
- DOMAIN-SUFFIX,2gis.com
- DOMAIN-SUFFIX,rg.ru