Files
sub-provider/app/services/policy_group_builder.py
2026-04-20 11:47:10 +08:00

310 lines
11 KiB
Python

from __future__ import annotations
import re
from typing import Any
from app.models import ProxyGroupConfig, ResolvedProfile, SourceSnapshot
from app.services.proxy_pipeline import source_auto_group_name
def _expand_proxy_tokens(
proxies: list[str],
*,
resolved_profile: ResolvedProfile,
source_auto_names: list[str],
selector_names: list[str],
) -> list[str]:
client = resolved_profile.client
tokens = {
"{{ main_policy }}": [client.main_policy],
"{{main_policy}}": [client.main_policy],
"{{ source_policy }}": [client.source_policy],
"{{source_policy}}": [client.source_policy],
"{{ mixed_auto_policy }}": [client.mixed_auto_policy],
"{{mixed_auto_policy}}": [client.mixed_auto_policy],
"{{ manual_policy }}": [client.manual_policy],
"{{manual_policy}}": [client.manual_policy],
"{{ direct_policy }}": [client.direct_policy],
"{{direct_policy}}": [client.direct_policy],
"{{ source_auto_groups }}": source_auto_names,
"{{source_auto_groups}}": source_auto_names,
"{{ selector_groups }}": selector_names,
"{{selector_groups}}": selector_names,
}
expanded: list[str] = []
for item in proxies:
expanded.extend(tokens.get(item, [item]))
return expanded
def _build_filter_group_for_thin(
*,
resolved_profile: ResolvedProfile,
group: ProxyGroupConfig,
selected_source_names: list[str],
) -> dict[str, Any]:
client = resolved_profile.client
built: dict[str, Any] = {"name": group.name, "type": group.type, "filter": group.filter}
if group.type == "url-test":
built["url"] = str(group.url or client.test_url)
built["interval"] = group.interval or client.test_interval
if group.tolerance is not None:
built["tolerance"] = group.tolerance
if resolved_profile.client_type == "mihomo":
built["use"] = selected_source_names
else:
built["include-all"] = True
return built
def _build_filter_group_for_bundle(
*,
resolved_profile: ResolvedProfile,
group: ProxyGroupConfig,
all_proxy_names: list[str],
) -> dict[str, Any]:
client = resolved_profile.client
matched = [name for name in all_proxy_names if group.filter and re.search(group.filter, name)]
built: dict[str, Any] = {"name": group.name, "type": group.type, "proxies": matched or [client.direct_policy]}
if group.type == "url-test":
built["url"] = str(group.url or client.test_url)
built["interval"] = group.interval or client.test_interval
if group.tolerance is not None:
built["tolerance"] = group.tolerance
return built
def _build_custom_policy_groups(
*,
resolved_profile: ResolvedProfile,
source_auto_names: list[str],
selector_names: list[str],
) -> list[dict[str, Any]]:
groups: list[dict[str, Any]] = []
client = resolved_profile.client
for group in resolved_profile.policy_groups:
built: dict[str, Any] = {
"name": group.name,
"type": group.type,
"proxies": _expand_proxy_tokens(
group.proxies,
resolved_profile=resolved_profile,
source_auto_names=source_auto_names,
selector_names=selector_names,
),
}
if group.type == "url-test":
built["url"] = str(group.url or client.test_url)
built["interval"] = group.interval or client.test_interval
if group.tolerance is not None:
built["tolerance"] = group.tolerance
groups.append(built)
return groups
def build_thin_groups(
*,
resolved_profile: ResolvedProfile,
selected_source_names: list[str],
) -> list[dict[str, Any]]:
client = resolved_profile.client
groups: list[dict[str, Any]] = []
source_auto_names: list[str] = []
for source_name in selected_source_names:
source = resolved_profile.selected_sources[source_name]
group_name = source_auto_group_name(source.display_name or source_name)
source_auto_names.append(group_name)
groups.append(
{
"name": group_name,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"use": [source_name],
}
)
if resolved_profile.client_type == "mihomo":
mixed_auto = {
"name": client.mixed_auto_policy,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"include-all-providers": True,
}
manual = {
"name": client.manual_policy,
"type": "select",
"proxies": [client.direct_policy],
"include-all-providers": True,
}
else:
mixed_auto = {
"name": client.mixed_auto_policy,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"include-all": True,
}
manual = {
"name": client.manual_policy,
"type": "select",
"proxies": [client.direct_policy],
"include-all": True,
}
groups.append(mixed_auto)
region_names = [region.name for region in resolved_profile.regions.values()]
selector_names = [*region_names, *[selector.name for selector in resolved_profile.selector_groups]]
groups.append({"name": client.source_policy, "type": "select", "proxies": [client.mixed_auto_policy, *source_auto_names, client.direct_policy]})
groups.append(manual)
groups.append(
{
"name": client.main_policy,
"type": "select",
"proxies": [client.source_policy, client.mixed_auto_policy, *selector_names, client.manual_policy, client.direct_policy],
}
)
groups.extend(
_build_custom_policy_groups(
resolved_profile=resolved_profile,
source_auto_names=source_auto_names,
selector_names=selector_names,
)
)
for region in resolved_profile.regions.values():
group: dict[str, Any] = {
"name": region.name,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"filter": region.filter,
"tolerance": region.tolerance,
}
if resolved_profile.client_type == "mihomo":
group["include-all-providers"] = True
else:
group["include-all"] = True
groups.append(group)
for selector in resolved_profile.selector_groups:
if selector.filter:
groups.append(
_build_filter_group_for_thin(
resolved_profile=resolved_profile,
group=selector,
selected_source_names=selected_source_names,
)
)
else:
groups.append(
{
"name": selector.name,
"type": selector.type,
"proxies": _expand_proxy_tokens(
selector.proxies,
resolved_profile=resolved_profile,
source_auto_names=source_auto_names,
selector_names=selector_names,
),
}
)
return groups
def build_bundle_groups(
*,
resolved_profile: ResolvedProfile,
snapshots: list[SourceSnapshot],
source_proxy_names: dict[str, list[str]],
) -> list[dict[str, Any]]:
client = resolved_profile.client
groups: list[dict[str, Any]] = []
source_auto_names: list[str] = []
all_proxy_names = [name for names in source_proxy_names.values() for name in names]
for snapshot in snapshots:
group_name = source_auto_group_name(snapshot.display_name)
source_auto_names.append(group_name)
groups.append(
{
"name": group_name,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"proxies": source_proxy_names.get(snapshot.name) or [client.direct_policy],
}
)
groups.append(
{
"name": client.mixed_auto_policy,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"proxies": all_proxy_names or [client.direct_policy],
}
)
region_names = [region.name for region in resolved_profile.regions.values()]
selector_names = [*region_names, *[selector.name for selector in resolved_profile.selector_groups]]
groups.append({"name": client.source_policy, "type": "select", "proxies": [client.mixed_auto_policy, *source_auto_names, client.direct_policy]})
groups.append({"name": client.manual_policy, "type": "select", "proxies": [*all_proxy_names, client.direct_policy] if all_proxy_names else [client.direct_policy]})
groups.append(
{
"name": client.main_policy,
"type": "select",
"proxies": [client.source_policy, client.mixed_auto_policy, *selector_names, client.manual_policy, client.direct_policy],
}
)
groups.extend(
_build_custom_policy_groups(
resolved_profile=resolved_profile,
source_auto_names=source_auto_names,
selector_names=selector_names,
)
)
for region in resolved_profile.regions.values():
matched = [name for name in all_proxy_names if re.search(region.filter, name)]
groups.append(
{
"name": region.name,
"type": "url-test",
"url": str(client.test_url),
"interval": client.test_interval,
"tolerance": region.tolerance,
"proxies": matched or [client.direct_policy],
}
)
for selector in resolved_profile.selector_groups:
if selector.filter:
groups.append(
_build_filter_group_for_bundle(
resolved_profile=resolved_profile,
group=selector,
all_proxy_names=all_proxy_names,
)
)
else:
groups.append(
{
"name": selector.name,
"type": selector.type,
"proxies": _expand_proxy_tokens(
selector.proxies,
resolved_profile=resolved_profile,
source_auto_names=source_auto_names,
selector_names=selector_names,
),
}
)
return groups