package config import ( "vpnem/internal/models" ) type SingBoxConfig struct { DNS map[string]any `json:"dns"` Inbounds []map[string]any `json:"inbounds"` Outbounds []map[string]any `json:"outbounds"` Route map[string]any `json:"route"` Experimental map[string]any `json:"experimental,omitempty"` } var blockedDomains = []string{ // Telegram "telegram.org", "t.me", "telegram.me", "telegra.ph", "telegram.dog", "web.telegram.org", // Discord "discord.com", "discord.gg", "discordapp.com", "discordapp.net", // Meta "instagram.com", "cdninstagram.com", "ig.me", "igcdn.com", "facebook.com", "fb.com", "fbcdn.net", "fbsbx.com", "fb.me", "whatsapp.com", "whatsapp.net", // Twitter/X "twitter.com", "x.com", "twimg.com", "t.co", // AI "openai.com", "chatgpt.com", "oaistatic.com", "oaiusercontent.com", "claude.ai", "anthropic.com", // YouTube/Google "youtube.com", "googlevideo.com", "youtu.be", "ggpht.com", "ytimg.com", "gstatic.com", "doubleclick.net", "googleadservices.com", // Cam sites "stripchat.com", "stripchat.global", "ststandard.com", "strpssts-ana.com", "strpst.com", "striiiipst.com", "chaturbate.com", "highwebmedia.com", "cb.dev", "camsoda.com", "cam4.com", "cam101.com", "bongamodels.com", "flirt4free.com", "privatecams.com", "streamray.com", "cams.com", "homelivesex.com", "skyprivate.com", "mywebcamroom.com", "livemediahost.com", // Cam CDNs "xcdnpro.com", "mmcdn.com", "vscdns.com", "bgicdn.com", "bgmicdn.com", "doppiocdn.com", "doppiocdn.net", "doppiostreams.com", "fanclubs.tech", "my.club", "chapturist.com", // Cam analytics/services "moengage.com", "amplitude.com", "dwin1.com", "eizzih.com", "loo3laej.com", "iesnare.com", "hytto.com", "zendesk.com", // Lovense "lovense.com", "lovense-api.com", "lovense.club", // Bitrix "bitrix24.ru", "bitrix24.com", // Cloudflare "cloudflare.com", // Other blocked "viber.com", "linkedin.com", "spotify.com", "ntc.party", "ipify.org", "rutracker.org", "rutracker.net", "rutracker.me", "4pda.to", "kinozal.tv", "nnmclub.to", "protonmail.com", "proton.me", "tutanota.com", "medium.com", "archive.org", "soundcloud.com", "twitch.tv", // IP check "ifconfig.me", "ifconfig.co", "icanhazip.com", "ipinfo.io", // Email "em-mail.ru", } func BuildConfig(server models.Server, mode Mode, ruleSets []models.RuleSet, serverIPs []string) SingBoxConfig { return BuildConfigFull(server, mode, ruleSets, serverIPs, nil) } // BuildConfigFull — exact vpn.py config. Fast, proven. func BuildConfigFull(server models.Server, mode Mode, ruleSets []models.RuleSet, serverIPs []string, customBypass []string) SingBoxConfig { bypassIPs := BuildBypassIPs(serverIPs) bypassProcs := BuildBypassProcesses(customBypass) var rules []map[string]any rules = append(rules, map[string]any{"ip_cidr": bypassIPs, "outbound": "direct"}) rules = append(rules, map[string]any{"process_name": bypassProcs, "outbound": "direct"}) rules = append(rules, map[string]any{"domain_suffix": LocalDomainSuffixes, "outbound": "direct"}) // Bypass own infrastructure — prevent double proxying when OmegaSwitcher is also active rules = append(rules, map[string]any{"domain_suffix": []string{"em-sysadmin.xyz"}, "outbound": "direct"}) rules = append(rules, map[string]any{"process_path_regex": LovenseProcessRegex, "outbound": "proxy"}) rules = append(rules, map[string]any{"ip_cidr": ForcedProxyIPs, "outbound": "proxy"}) rules = append(rules, map[string]any{"domain_suffix": TelegramDomains, "outbound": "proxy"}) rules = append(rules, map[string]any{"domain_regex": TelegramDomainRegex, "outbound": "proxy"}) rules = append(rules, map[string]any{"ip_cidr": TelegramIPs, "outbound": "proxy"}) rules = append(rules, map[string]any{"domain_suffix": blockedDomains, "outbound": "proxy"}) for _, r := range mode.Rules { rule := map[string]any{"outbound": r.Outbound} if len(r.DomainSuffix) > 0 { rule["domain_suffix"] = r.DomainSuffix } if len(r.DomainRegex) > 0 { rule["domain_regex"] = r.DomainRegex } if len(r.IPCIDR) > 0 { rule["ip_cidr"] = r.IPCIDR } if len(r.RuleSet) > 0 { rule["rule_set"] = r.RuleSet } if len(r.Network) > 0 { rule["network"] = r.Network } if len(r.PortRange) > 0 { rule["port_range"] = r.PortRange } rules = append(rules, rule) } var ruleSetDefs []map[string]any for _, rs := range ruleSets { if rs.URL == "" { continue } ruleSetDefs = append(ruleSetDefs, map[string]any{ "tag": rs.Tag, "type": "remote", "format": rs.Format, "url": rs.URL, "download_detour": "direct", "update_interval": "1d", }) } route := map[string]any{ "auto_detect_interface": true, "final": mode.Final, "rules": rules, } if len(ruleSetDefs) > 0 { route["rule_set"] = ruleSetDefs } return SingBoxConfig{ DNS: map[string]any{ "servers": []map[string]any{ {"tag": "proxy-dns", "address": "https://8.8.8.8/dns-query", "detour": "proxy"}, {"tag": "direct-dns", "address": "https://1.1.1.1/dns-query", "detour": "direct"}, }, "rules": []map[string]any{ {"outbound": "proxy", "server": "proxy-dns"}, {"outbound": "direct", "server": "direct-dns"}, }, "strategy": "ipv4_only", }, Inbounds: []map[string]any{ { "type": "tun", "interface_name": "singbox", "address": []string{"172.19.0.1/30"}, "auto_route": true, "strict_route": false, "stack": "gvisor", "sniff": true, "sniff_override_destination": true, }, }, Outbounds: []map[string]any{ buildOutbound(server), {"type": "direct", "tag": "direct"}, }, Route: route, Experimental: map[string]any{ "cache_file": map[string]any{ "enabled": true, "path": "cache.db", }, }, } } func buildOutbound(s models.Server) map[string]any { switch s.Type { case "vless": out := map[string]any{ "type": "vless", "tag": "proxy", "server": s.Server, "server_port": s.ServerPort, "uuid": s.UUID, } if s.TLS != nil { out["tls"] = map[string]any{"enabled": s.TLS.Enabled, "server_name": s.TLS.ServerName} } if s.Transport != nil { out["transport"] = map[string]any{"type": s.Transport.Type, "path": s.Transport.Path} } return out case "shadowsocks": return map[string]any{ "type": "shadowsocks", "tag": "proxy", "server": s.Server, "server_port": s.ServerPort, "method": s.Method, "password": s.Password, } default: return map[string]any{ "type": "socks", "tag": "proxy", "server": s.Server, "server_port": s.ServerPort, "udp_over_tcp": s.UDPOverTCP, } } }