Files
archived-MoviePilot-Plugins/plugins.v2/clashruleprovider/models/proxygroups.py

120 lines
5.5 KiB
Python

import re
from typing import List, Optional, Union, Literal
from pydantic import BaseModel, Field, field_validator, RootModel
class ProxyGroupBase(BaseModel):
"""
包含所有代理组类型共有的通用字段。
"""
# Required field
name: str = Field(..., description="The name of the proxy group.")
# Proxy and provider references
proxies: Optional[List[str]] = Field(None, description="References to outbound proxies or other proxy groups.")
use: Optional[List[str]] = Field(None, description="References to proxy provider sets.")
# Health check fields
url: Optional[str] = Field(None, description="Health check test address.")
interval: Optional[int] = Field(None, description="Health check interval in seconds.")
lazy: Optional[bool] = Field(True, description="If not selected, no health checks are performed.")
timeout: Optional[int] = Field(None, description="Health check timeout in milliseconds.")
max_failed_times: Optional[int] = Field(5, description="Maximum number of failures before a forced health check.",
alias="max-failed-times")
expected_status: Optional[str] = Field('*',
description="Expected HTTP response status code for health checks.",
alias="expected-status")
# Network and routing fields
disable_udp: Optional[bool] = Field(False, description="Disables UDP for this proxy group.", alias="disable-udp")
interface_name: Optional[str] = Field(None, description="DEPRECATED. Specifies the outbound interface.",
alias="interface-name")
routing_mark: Optional[int] = Field(None, description="DEPRECATED. The routing mark for outbound connections.",
alias="routing-mark")
# Dynamic proxy inclusion
include_all: Optional[bool] = Field(False, description="Includes all outbound proxies and proxy sets.",
alias="include-all")
include_all_proxies: Optional[bool] = Field(False, description="Includes all outbound proxies.",
alias="include-all-proxies")
include_all_providers: Optional[bool] = Field(False, description="Includes all proxy provider sets.",
alias="include-all-providers")
# Filtering
filter: Optional[str] = Field(None, description="Regex to filter nodes from providers.")
exclude_filter: Optional[str] = Field(None, description="Regex to exclude nodes.", alias="exclude-filter")
exclude_type: Optional[str] = Field(None, description="Exclude nodes by adapter type, separated by '|'.",
alias="exclude-type")
# UI fields
hidden: Optional[bool] = Field(False, description="Hides the proxy group in the API.")
icon: Optional[str] = Field(None, description="Icon string for the proxy group, for UI use.")
@field_validator('expected_status')
@classmethod
def validate_expected_status(cls, v: Optional[str]) -> Optional[str]:
if v is None or v == '*':
return v
pattern = re.compile(r'^\d{3}([-/]\d{3})*$')
if not pattern.match(v):
raise ValueError("Invalid format for expected-status.")
parts = re.split(r'[/]', v)
for part in parts:
if '-' in part:
start, end = part.split('-')
if not (start.isdigit() and end.isdigit() and 100 <= int(start) < 600 and 100 <= int(end) < 600 and int(
start) <= int(end)):
raise ValueError(f"Invalid status code range: {part}")
elif not (part.isdigit() and 100 <= int(part) < 600):
raise ValueError(f"Invalid status code: {part}")
return v
class SelectGroup(ProxyGroupBase):
type: Literal['select']
class RelayGroup(ProxyGroupBase):
type: Literal['relay']
class FallbackGroup(ProxyGroupBase):
type: Literal['fallback']
class UrlTestGroup(ProxyGroupBase):
type: Literal['url-test']
tolerance: Optional[int] = Field(None, description="proxies switch tolerance, measured in milliseconds (ms).")
class LoadBalanceGroup(ProxyGroupBase):
type: Literal['load-balance']
strategy: Optional[Literal['round-robin', 'consistent-hashing', 'sticky-sessions']] = Field(
'round-robin',
description="Load balancing strategy."
)
class SmartGroup(ProxyGroupBase):
type: Literal['smart']
uselightgbm: bool = Field(..., description="Use LightGBM model predict weight.")
collectdata: bool = Field(..., description="Collect datas for model training.")
policy_priority: Optional[str] = Field("1",
description="<1 means lower priority, >1 means higher priority, "
"the default is 1, pattern support regex and string.",
alias="policy-priority")
strategy: Optional[Literal['round-robin', 'sticky-sessions']] = Field(
'sticky-sessions',
description="Load balancing strategy."
)
sample_rate: Optional[int] = Field(1, description="Data acquisition rate.", alias="sample-rate")
# Discriminated Union
ProxyGroupType = Union[SelectGroup, RelayGroup, FallbackGroup, UrlTestGroup, LoadBalanceGroup, SmartGroup]
class ProxyGroup(RootModel[ProxyGroupType]):
root: ProxyGroupType = Field(..., discriminator='type')