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