Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49e1105763 | ||
|
|
5fde0b6658 | ||
|
|
6b769ef998 | ||
|
|
458ba6dd35 | ||
|
|
82f6e1dae8 | ||
|
|
9de327fb29 | ||
|
|
0e88bb62b5 | ||
|
|
ef1b6eab6b | ||
|
|
fb90213e33 | ||
|
|
a87e298002 | ||
|
|
0b6eea08d1 | ||
|
|
37797cba06 | ||
|
|
100f3d5431 | ||
|
|
80276f4b4d | ||
|
|
e149b1cc18 | ||
|
|
8fe4d7e7db | ||
|
|
27c936fbe0 | ||
|
|
65b0eb3798 | ||
|
|
54b72a9324 | ||
|
|
3c6304b8c2 | ||
|
|
ca12fc149b | ||
|
|
7de03fce79 | ||
|
|
03cf8c6c22 | ||
|
|
60009fedea |
1
.github/workflows/go.yml
vendored
1
.github/workflows/go.yml
vendored
@@ -31,7 +31,6 @@ jobs:
|
||||
run: |
|
||||
go get -u github.com/go-bindata/go-bindata/...
|
||||
go-bindata -o internal/bindata/geoip/geoip.go -pkg bingeoip assets/GeoLite2-City.mmdb assets/flags.json
|
||||
go-bindata -o internal/bindata/html/html.go -pkg binhtml assets/html/
|
||||
|
||||
- name: Build
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,4 +21,4 @@ vendor
|
||||
# macOS file
|
||||
.DS_Store
|
||||
|
||||
*.exe
|
||||
assets/html/
|
||||
|
||||
117
api/router.go
117
api/router.go
@@ -4,6 +4,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/heroku/x/hmetrics/onload"
|
||||
@@ -13,14 +14,14 @@ import (
|
||||
"github.com/zu1k/proxypool/pkg/provider"
|
||||
)
|
||||
|
||||
const version = "v0.3.3"
|
||||
const version = "v0.3.5"
|
||||
|
||||
var router *gin.Engine
|
||||
|
||||
func setupRouter() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router = gin.New()
|
||||
router.Use(gin.Logger(), gin.Recovery())
|
||||
router.Use(gin.Recovery())
|
||||
temp, err := loadTemplate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -69,36 +70,130 @@ func setupRouter() {
|
||||
router.GET("/clash/proxies", func(c *gin.Context) {
|
||||
proxyTypes := c.DefaultQuery("type", "")
|
||||
proxyCountry := c.DefaultQuery("c", "")
|
||||
proxyNotCountry := c.DefaultQuery("nc", "")
|
||||
text := ""
|
||||
if proxyTypes == "" && proxyCountry == "" {
|
||||
if proxyTypes == "" && proxyCountry == "" && proxyNotCountry == "" {
|
||||
text = cache.GetString("clashproxies")
|
||||
if text == "" {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
clash := provider.Clash{Proxies: proxies}
|
||||
clash := provider.Clash{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
},
|
||||
}
|
||||
text = clash.Provide()
|
||||
cache.SetString("clashproxies", text)
|
||||
}
|
||||
} else if proxyTypes == "all" {
|
||||
proxies := cache.GetProxies("allproxies")
|
||||
clash := provider.Clash{Proxies: proxies, Types: proxyTypes, Country: proxyCountry}
|
||||
clash := provider.Clash{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: proxyTypes,
|
||||
Country: proxyCountry,
|
||||
NotCountry: proxyNotCountry,
|
||||
},
|
||||
}
|
||||
text = clash.Provide()
|
||||
} else {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
clash := provider.Clash{Proxies: proxies, Types: proxyTypes, Country: proxyCountry}
|
||||
clash := provider.Clash{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: proxyTypes,
|
||||
Country: proxyCountry,
|
||||
NotCountry: proxyNotCountry,
|
||||
},
|
||||
}
|
||||
text = clash.Provide()
|
||||
}
|
||||
c.String(200, text)
|
||||
})
|
||||
router.GET("/surge/proxies", func(c *gin.Context) {
|
||||
text := cache.GetString("surgeproxies")
|
||||
if text == "" {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
surge := provider.Surge{Proxies: proxies}
|
||||
proxyTypes := c.DefaultQuery("type", "")
|
||||
proxyCountry := c.DefaultQuery("c", "")
|
||||
proxyNotCountry := c.DefaultQuery("nc", "")
|
||||
text := ""
|
||||
if proxyTypes == "" && proxyCountry == "" && proxyNotCountry == "" {
|
||||
text = cache.GetString("surgeproxies")
|
||||
if text == "" {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
surge := provider.Surge{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
},
|
||||
}
|
||||
text = surge.Provide()
|
||||
cache.SetString("surgeproxies", text)
|
||||
}
|
||||
} else if proxyTypes == "all" {
|
||||
proxies := cache.GetProxies("allproxies")
|
||||
surge := provider.Surge{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: proxyTypes,
|
||||
Country: proxyCountry,
|
||||
NotCountry: proxyNotCountry,
|
||||
},
|
||||
}
|
||||
text = surge.Provide()
|
||||
} else {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
surge := provider.Surge{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: proxyTypes,
|
||||
Country: proxyCountry,
|
||||
NotCountry: proxyNotCountry,
|
||||
},
|
||||
}
|
||||
text = surge.Provide()
|
||||
cache.SetString("surgeproxies", text)
|
||||
}
|
||||
c.String(200, text)
|
||||
})
|
||||
|
||||
router.GET("/ss/sub", func(c *gin.Context) {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
ssSub := provider.SSSub{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: "ss",
|
||||
},
|
||||
}
|
||||
c.String(200, ssSub.Provide())
|
||||
})
|
||||
router.GET("/ssr/sub", func(c *gin.Context) {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
ssrSub := provider.SSRSub{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: "ssr",
|
||||
},
|
||||
}
|
||||
c.String(200, ssrSub.Provide())
|
||||
})
|
||||
router.GET("/vmess/sub", func(c *gin.Context) {
|
||||
proxies := cache.GetProxies("proxies")
|
||||
vmessSub := provider.VmessSub{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
Types: "vmess",
|
||||
},
|
||||
}
|
||||
c.String(200, vmessSub.Provide())
|
||||
})
|
||||
router.GET("/link/:id", func(c *gin.Context) {
|
||||
idx := c.Param("id")
|
||||
proxies := cache.GetProxies("allproxies")
|
||||
id, err := strconv.Atoi(idx)
|
||||
if err != nil {
|
||||
c.String(500, err.Error())
|
||||
}
|
||||
if id >= proxies.Len() {
|
||||
c.String(500, "id too big")
|
||||
}
|
||||
c.String(200, proxies[id].Link())
|
||||
})
|
||||
}
|
||||
|
||||
func Run() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<title>免费Clash节点</title>
|
||||
<style type='text/css'>
|
||||
body {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 36px;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.section.friendly {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.section.friendly h1 {
|
||||
font-size: 26px;
|
||||
background-color: #dad8e4;
|
||||
padding: 18px 22px 15px 22px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section.friendly h1 strong {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.section.friendly h1 small {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
font-size: 18px;
|
||||
padding-top: 4px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.section.friendly .article {
|
||||
border: 4px solid #dad8e4;
|
||||
padding: 24px 18px 18px 18px;
|
||||
}
|
||||
|
||||
.section.friendly .article h3 {
|
||||
font-size: 20px;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
|
||||
.section.friendly .article a {
|
||||
color: #6b6ceb;
|
||||
}
|
||||
|
||||
.section.friendly .article a:visited {
|
||||
color: #1d1d3b;
|
||||
}
|
||||
|
||||
.section.friendly .article p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.section.friendly .article ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.section.original {
|
||||
background-color: #eeeeee;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
.section.original h2 {
|
||||
background-color: #dddddd;
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.section.original pre {
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
overflow: auto;
|
||||
font-family: monaco, monospaced;
|
||||
}
|
||||
|
||||
.section.original pre code {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container'>
|
||||
<div class='section friendly'>
|
||||
<h1><strong>免费Clash节点</strong></h1>
|
||||
<div class='article'>
|
||||
<p>自动抓取tg频道、订阅地址、公开互联网上的ss、ssr、vmess、trojan节点信息,聚合去重后提供clash配置,每15分钟更新</p>
|
||||
<br>
|
||||
<p>Clash配置文件:https://{{ .domain }}/clash/config <a href="clash://install-config?url=https://{{ .domain }}/clash/config">一键导入</a></p>
|
||||
<p>Clash proxy-provider(Shadowrocket添加订阅方式可用):<a href="https://{{ .domain }}/clash/proxies">https://{{ .domain }}/clash/proxies</a></p>
|
||||
<br>
|
||||
<p>筛选代理类型(此种方式你只能自己维护配置文件):https://{{ .domain }}/clash/proxies?type=ss,ssr,vmess</p>
|
||||
<p>筛选国家(此种方式你只能自己维护配置文件):https://{{ .domain }}/clash/proxies?type=ss,ssr,vmess&c=HK,TW,US</p>
|
||||
<p>所有节点的Provider(不是都可以用):<a href="https://{{ .domain }}/clash/proxies?type=all">https://{{ .domain }}/clash/proxies?type=all</a></p>
|
||||
<br>
|
||||
<p>抓取程序已开源:<a href="https://github.com/zu1k/proxypool">https://github.com/zu1k/proxypool</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DC6CR61FHE"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-DC6CR61FHE');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,148 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<title>免费节点</title>
|
||||
<meta property="og:site_name" content="免费节点">
|
||||
<meta property="og:description" content="免费ss,免费ssr,免费v2ray,免费vmess,免费vless,免费shadowsocks,免费shadowsocksr,免费shadowsocksrr,免费shadowrocket,免费quan,免费quanx,免费surge,免费小火箭,白嫖ss,白嫖ssr,白嫖v2ray,白嫖vmess,白嫖vless,白嫖shadowsocks,白嫖shadowsocksr,白嫖shadowsocksrr,白嫖shadowrocket,白嫖quan,白嫖quanx,白嫖surge,白嫖小火箭,高速ss,高速ssr,高速v2ray,高速vmess,高速vless,高速shadowsocks,高速shadowsocksr,高速shadowsocksrr,高速shadowrocket,高速quan,高速quanx,高速surge,高速小火箭,公益ss,公益ssr,公益v2ray,公益vmess,公益vless,公益shadowsocks,公益shadowsocksr,公益shadowsocksrr,公益shadowrocket,公益quan,公益quanx,公益surge,公益小火箭,windowsss,windowsssr,windowsv2ray,windowsvmess,windowsvless,windowsshadowsocks,windowsshadowsocksr,windowsshadowsocksrr,windowsshadowrocket,windowsquan,windowsquanx,windowssurge,windows小火箭,androidss,androidssr,androidv2ray,androidvmess,androidvless,androidshadowsocks,androidshadowsocksr,androidshadowsocksrr,androidshadowrocket,androidquan,androidquanx,androidsurge,android小火箭,iosss,iosssr,iosv2ray,iosvmess,iosvless,iosshadowsocks,iosshadowsocksr,iosshadowsocksrr,iosshadowrocket,iosquan,iosquanx,iossurge,ios小火箭,macss,macssr,macv2ray,macvmess,macvless,macshadowsocks,macshadowsocksr,macshadowsocksrr,macshadowrocket,macquan,macquanx,macsurge,mac小火箭,苹果ss,苹果ssr,苹果v2ray,苹果vmess,苹果vless,苹果shadowsocks,苹果shadowsocksr,苹果shadowsocksrr,苹果shadowrocket,苹果quan,苹果quanx,苹果surge,苹果小火箭,安卓ss,安卓ssr,安卓v2ray,安卓vmess,安卓vless,安卓shadowsocks,安卓shadowsocksr,安卓shadowsocksrr,安卓shadowrocket,安卓quan,安卓quanx,安卓surge,安卓小火箭">
|
||||
<meta name="keywords" content="免费ss,免费ssr,免费v2ray,免费vmess,免费vless,免费shadowsocks,免费shadowsocksr,免费shadowsocksrr,免费shadowrocket,免费quan,免费quanx,免费surge,免费小火箭,白嫖ss,白嫖ssr,白嫖v2ray,白嫖vmess,白嫖vless,白嫖shadowsocks,白嫖shadowsocksr,白嫖shadowsocksrr,白嫖shadowrocket,白嫖quan,白嫖quanx,白嫖surge,白嫖小火箭,高速ss,高速ssr,高速v2ray,高速vmess,高速vless,高速shadowsocks,高速shadowsocksr,高速shadowsocksrr,高速shadowrocket,高速quan,高速quanx,高速surge,高速小火箭,公益ss,公益ssr,公益v2ray,公益vmess,公益vless,公益shadowsocks,公益shadowsocksr,公益shadowsocksrr,公益shadowrocket,公益quan,公益quanx,公益surge,公益小火箭,windowsss,windowsssr,windowsv2ray,windowsvmess,windowsvless,windowsshadowsocks,windowsshadowsocksr,windowsshadowsocksrr,windowsshadowrocket,windowsquan,windowsquanx,windowssurge,windows小火箭,androidss,androidssr,androidv2ray,androidvmess,androidvless,androidshadowsocks,androidshadowsocksr,androidshadowsocksrr,androidshadowrocket,androidquan,androidquanx,androidsurge,android小火箭,iosss,iosssr,iosv2ray,iosvmess,iosvless,iosshadowsocks,iosshadowsocksr,iosshadowsocksrr,iosshadowrocket,iosquan,iosquanx,iossurge,ios小火箭,macss,macssr,macv2ray,macvmess,macvless,macshadowsocks,macshadowsocksr,macshadowsocksrr,macshadowrocket,macquan,macquanx,macsurge,mac小火箭,苹果ss,苹果ssr,苹果v2ray,苹果vmess,苹果vless,苹果shadowsocks,苹果shadowsocksr,苹果shadowsocksrr,苹果shadowrocket,苹果quan,苹果quanx,苹果surge,苹果小火箭,安卓ss,安卓ssr,安卓v2ray,安卓vmess,安卓vless,安卓shadowsocks,安卓shadowsocksr,安卓shadowsocksrr,安卓shadowrocket,安卓quan,安卓quanx,安卓surge,安卓小火箭">
|
||||
<style type='text/css'>
|
||||
body {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 36px;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.section.friendly {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.section.friendly h1 {
|
||||
font-size: 26px;
|
||||
background-color: #dad8e4;
|
||||
padding: 18px 22px 15px 22px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section.friendly h1 strong {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.section.friendly h1 small {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
font-size: 18px;
|
||||
padding-top: 4px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.section.friendly .article {
|
||||
border: 4px solid #dad8e4;
|
||||
padding: 24px 18px 18px 18px;
|
||||
}
|
||||
|
||||
.section.friendly .article h3 {
|
||||
font-size: 20px;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
|
||||
.section.friendly .article a {
|
||||
color: #6b6ceb;
|
||||
}
|
||||
|
||||
.section.friendly .article a:visited {
|
||||
color: #1d1d3b;
|
||||
}
|
||||
|
||||
.section.friendly .article p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.section.friendly .article ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.section.original {
|
||||
background-color: #eeeeee;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
.section.original h2 {
|
||||
background-color: #dddddd;
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.section.original pre {
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
overflow: auto;
|
||||
font-family: monaco, monospaced;
|
||||
}
|
||||
|
||||
.section.original pre code {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container'>
|
||||
<div class='section friendly'>
|
||||
<h1><strong>免费节点</strong></h1>
|
||||
<div class='article'>
|
||||
<p>自动抓取tg频道、订阅地址、公开互联网上的ss、ssr、vmess、trojan节点信息,聚合去重后提供节点列表,每15分钟更新,目前共有{{.getters_count}}个抓取源</p>
|
||||
<p>汇总节点数量:{{ .all_proxies_count }}</p>
|
||||
<p>ss节点数量:{{ .ss_proxies_count }}</p>
|
||||
<p>ssr节点数量:{{ .ssr_proxies_count }}</p>
|
||||
<p>vmess节点数量:{{ .vmess_proxies_count }}</p>
|
||||
<p>trojan节点数量:{{ .trojan_proxies_count }}</p>
|
||||
<p>可用节点数量:{{ .useful_proxies_count }}</p>
|
||||
<p>最后更新时间:{{ .last_crawl_time }}</p>
|
||||
<br>
|
||||
<h4><a href="/clash">Clash</a></h4>
|
||||
<p>Clash配置文件:https://{{ .domain }}/clash/config <a
|
||||
href="clash://install-config?url=https://{{ .domain }}/clash/config">一键导入</a></p>
|
||||
<p>Clash proxy-provider(Shadowrocket添加订阅方式可用):<a href="https://{{ .domain }}/clash/proxies">https://{{ .domain }}/clash/proxies</a>
|
||||
</p>
|
||||
<p>筛选代理类型:https://{{ .domain }}/clash/proxies?type=ss,ssr,vmess</p>
|
||||
<p>筛选国家:https://{{ .domain }}/clash/proxies?c=HK,TW,US</p>
|
||||
<br>
|
||||
<h4><a href="/surge">Surge</a></h4>
|
||||
<p>Surge配置文件:https://{{ .domain }}/surge/config <a
|
||||
href="surge3:///install-config?url=https://{{ .domain }}/surge/config">一键导入</a></p>
|
||||
<p>Surge proxy list:<a href="https://{{ .domain }}/surge/proxies">https://{{ .domain }}/surge/proxies</a>
|
||||
</p>
|
||||
<br>
|
||||
{{- /* 所有使用本代码提供服务的禁止删除该行*/}}
|
||||
<p>欢迎关注tg频道:<a href="https://t.me/peekfun">@peekfun</a></p>
|
||||
<p>抓取程序已开源:<a href="https://github.com/zu1k/proxypool">https://github.com/zu1k/proxypool</a> {{ .version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DC6CR61FHE"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-DC6CR61FHE');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,45 +0,0 @@
|
||||
[Proxy]
|
||||
Direct = direct
|
||||
|
||||
[Proxy Group]
|
||||
Proxy = select, 延迟最低, 失败切换, 手动选择
|
||||
延迟最低 = url-test, policy-path=https://{{ .domain }}/surge/proxies, url=http://www.qualcomm.cn/generate_204, update-interval=3600, interval = 600s, tolerance = 100ms, timeout = 5s, evaluate-before-use = true
|
||||
失败切换 = fallback, policy-path=https://{{ .domain }}/surge/proxies, url=http://www.qualcomm.cn/generate_204, update-interval=3600, interval = 600s, tolerance = 100ms, timeout = 5s, evaluate-before-use = true
|
||||
手动选择 = select, policy-path=https://{{ .domain }}/surge/proxies, url=http://www.qualcomm.cn/generate_204, update-interval=3600, interval = 600s, tolerance = 100ms, timeout = 5s, evaluate-before-use = true
|
||||
Apple = select, Direct, Proxy
|
||||
Adblock = select, Direct, REJECT, REJECT-TINYGIF
|
||||
|
||||
[Rule]
|
||||
# RULESET
|
||||
RULE-SET,SYSTEM,Direct
|
||||
#
|
||||
# Unbreak 后续规则修正
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Unbreak.list,Adblock
|
||||
# Advertising 广告
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Guard/Advertising.list,Adblock
|
||||
# Privacy 隐私
|
||||
# RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Guard/Privacy.list,Adblock
|
||||
# Hijacking 运营商劫持或恶意网站
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Guard/Hijacking.list,Adblock
|
||||
#
|
||||
# Apple
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Extra/Apple/Apple.list,Apple
|
||||
#
|
||||
# 代理
|
||||
# Streaming 国际流媒体服务
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/StreamingMedia/Streaming.list,Proxy
|
||||
# StreamingSE 中国流媒体服务(面向海外版本)
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/StreamingMedia/StreamingSE.list,Proxy
|
||||
# Global 全球加速
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Global.list,Proxy
|
||||
#
|
||||
RULE-SET,https://github.com/Hackl0us/SS-Rule-Snippet/raw/master/Rulesets/App/social/Telegram.list,Proxy
|
||||
RULE-SET,https://github.com/Hackl0us/SS-Rule-Snippet/raw/master/Rulesets/App/social/WhatsApp.list,Proxy
|
||||
RULE-SET,https://github.com/Hackl0us/SS-Rule-Snippet/raw/master/Rulesets/App/social/LINE.list,Proxy
|
||||
#
|
||||
# Direct
|
||||
RULE-SET,https://github.com/DivineEngine/Profiles/raw/master/Surge/Ruleset/Extra/ChinaIP.list,Direct
|
||||
#
|
||||
RULE-SET,LAN,Direct
|
||||
# GEOIP,CN,Direct
|
||||
FINAL,Proxy,dns-failed
|
||||
@@ -1,95 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<title>免费Surge节点</title>
|
||||
<style type='text/css'>
|
||||
body {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 36px;
|
||||
line-height: 1;
|
||||
font-size: 14px; }
|
||||
|
||||
.section {
|
||||
margin-bottom: 36px; }
|
||||
.section.friendly {
|
||||
color: #222222; }
|
||||
.section.friendly h1 {
|
||||
font-size: 26px;
|
||||
background-color: #dad8e4;
|
||||
padding: 18px 22px 15px 22px;
|
||||
margin: 0;
|
||||
overflow: hidden; }
|
||||
.section.friendly h1 strong {
|
||||
display: inline-block;
|
||||
float: left; }
|
||||
.section.friendly h1 small {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
font-size: 18px;
|
||||
padding-top: 4px;
|
||||
color: #333333; }
|
||||
.section.friendly .article {
|
||||
border: 4px solid #dad8e4;
|
||||
padding: 24px 18px 18px 18px; }
|
||||
.section.friendly .article h3 {
|
||||
font-size: 20px;
|
||||
margin: 0 0 18px 0; }
|
||||
.section.friendly .article a {
|
||||
color: #6b6ceb; }
|
||||
.section.friendly .article a:visited {
|
||||
color: #1d1d3b; }
|
||||
.section.friendly .article p {
|
||||
font-size: 14px; }
|
||||
.section.friendly .article ul {
|
||||
list-style-type: square; }
|
||||
.section.original {
|
||||
background-color: #eeeeee;
|
||||
color: #444444; }
|
||||
.section.original h2 {
|
||||
background-color: #dddddd;
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
font-size: 20px; }
|
||||
.section.original pre {
|
||||
margin: 0;
|
||||
padding: 18px 22px 18px 22px;
|
||||
overflow: auto;
|
||||
font-family: monaco, monospaced; }
|
||||
.section.original pre code {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
width: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container'>
|
||||
<div class='section friendly'>
|
||||
<h1><strong>免费Surge节点</strong></h1>
|
||||
<div class='article'>
|
||||
<p>自动抓取tg频道、订阅地址、公开互联网上的ss、vmess节点信息,聚合去重后提供Surge节点列表,每15分钟更新</p>
|
||||
<br>
|
||||
<p>Surge配置文件:https://{{ .domain }}/surge/config <a href="surge3:///install-config?url=https://{{ .domain }}/surge/config">一键导入</a></p>
|
||||
<p>Surge proxy list:<a href="https://{{ .domain }}/surge/proxies">https://{{ .domain }}/surge/proxies</a></p>
|
||||
<br>
|
||||
<p>抓取程序已开源:<a href="https://github.com/zu1k/proxypool">https://github.com/zu1k/proxypool</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-DC6CR61FHE"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-DC6CR61FHE');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,13 @@
|
||||
# your domain
|
||||
domain: example.com
|
||||
|
||||
# cloudflare api
|
||||
cf_email: ""
|
||||
cf_key: ""
|
||||
|
||||
# source definition file
|
||||
source-files:
|
||||
- ./source.yaml
|
||||
# use local file
|
||||
- source.yaml
|
||||
# use web file
|
||||
- https://example.com/source.yaml
|
||||
@@ -20,9 +20,9 @@
|
||||
num: 200
|
||||
|
||||
# 翻墙党论坛抓取
|
||||
- type: web-fanqiangdang-rss
|
||||
- type: web-fanqiangdang
|
||||
options:
|
||||
url: https://fanqiangdang.com/forum.php?mod=rss&fid=50&auth=0
|
||||
url: https://fanqiangdang.com/forum-48-1.html
|
||||
|
||||
# 某个网站抓取
|
||||
- type: web-freessrxyz
|
||||
|
||||
2
docs/genbindata.sh
Normal file
2
docs/genbindata.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
go-bindata -o internal/bindata/html/html.go -pkg binhtml assets/html/
|
||||
go-bindata -o internal/bindata/geoip/geoip.go -pkg bingeoip assets/GeoLite2-City.mmdb assets/flags.json
|
||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
||||
github.com/antchfx/htmlquery v1.2.3 // indirect
|
||||
github.com/antchfx/xmlquery v1.2.4 // indirect
|
||||
github.com/antchfx/xpath v1.1.8 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.13.1
|
||||
github.com/cloudflare/cloudflare-go v0.13.2
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -30,6 +30,8 @@ github.com/cloudflare/cloudflare-go v0.13.0 h1:Mx2gNz/r4H0eHETENq5ZPtc/kOX/uzEfT
|
||||
github.com/cloudflare/cloudflare-go v0.13.0/go.mod h1:6rZ6s/XWxP8WIWMATgDM7aUop0Z0ZOAfcm/4rEd0lOY=
|
||||
github.com/cloudflare/cloudflare-go v0.13.1 h1:8i8E5CubfAlD/2zWM7VGKoIPS2/x/lO/nRnqDl0j5Dk=
|
||||
github.com/cloudflare/cloudflare-go v0.13.1/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2 h1:bhMGoNhAg21DuqJjU9jQepRRft6vYfo6pejT3NN4V6A=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -201,6 +203,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soveran/redisurl v0.0.0-20180322091936-eb325bc7a4b8/go.mod h1:FVJ8jbHu7QrNFs3bZEsv/L5JjearIAY9N0oXh2wk+6Y=
|
||||
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
|
||||
@@ -19,7 +19,7 @@ func CrawlGo() {
|
||||
wg.Add(1)
|
||||
go g.Get2Chan(pc, wg)
|
||||
}
|
||||
proxies := cache.GetProxies("proxies")
|
||||
proxies := cache.GetProxies("allproxies")
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(pc)
|
||||
@@ -32,7 +32,11 @@ func CrawlGo() {
|
||||
// 节点去重
|
||||
proxies = proxies.Deduplication()
|
||||
log.Println("CrawlGo node count:", len(proxies))
|
||||
proxies = provider.Clash{Proxies: proxies}.CleanProxies()
|
||||
proxies = provider.Clash{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
},
|
||||
}.CleanProxies()
|
||||
log.Println("CrawlGo cleaned node count:", len(proxies))
|
||||
proxies.NameAddCounrty().Sort().NameAddIndex().NameAddTG()
|
||||
log.Println("Proxy rename DONE!")
|
||||
@@ -58,6 +62,14 @@ func CrawlGo() {
|
||||
cache.SetProxies("proxies", proxies)
|
||||
cache.UsefullProxiesCount = proxies.Len()
|
||||
|
||||
cache.SetString("clashproxies", provider.Clash{Proxies: proxies}.Provide())
|
||||
cache.SetString("surgeproxies", provider.Surge{Proxies: proxies}.Provide())
|
||||
cache.SetString("clashproxies", provider.Clash{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
},
|
||||
}.Provide())
|
||||
cache.SetString("surgeproxies", provider.Surge{
|
||||
provider.Base{
|
||||
Proxies: &proxies,
|
||||
},
|
||||
}.Provide())
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,6 +2,7 @@ package getter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -41,6 +42,7 @@ func (s *Subscribe) Get() proxy.ProxyList {
|
||||
func (s *Subscribe) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := s.Get()
|
||||
log.Printf("STATISTIC: Subscribe\tcount=%d\turl=%s\n", len(nodes), s.Url)
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package getter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/gocolly/colly"
|
||||
@@ -79,6 +80,7 @@ func (g *TGChannelGetter) Get() proxy.ProxyList {
|
||||
func (g *TGChannelGetter) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := g.Get()
|
||||
log.Printf("STATISTIC: TGChannel\tcount=%d\turl=%s\n", len(nodes), g.Url)
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package getter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("web-fanqiangdang-rss", NewWebFanqiangdangRSSGetter)
|
||||
Register("web-fanqiangdang", NewWebFanqiangdangGetter)
|
||||
}
|
||||
|
||||
@@ -65,54 +65,7 @@ func (w *WebFanqiangdang) Get() proxy.ProxyList {
|
||||
func (w *WebFanqiangdang) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := w.Get()
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
}
|
||||
|
||||
type WebFanqiangdangRSS struct {
|
||||
c *colly.Collector
|
||||
Url string
|
||||
results []string
|
||||
}
|
||||
|
||||
func NewWebFanqiangdangRSSGetter(options tool.Options) (getter Getter, err error) {
|
||||
urlInterface, found := options["url"]
|
||||
if found {
|
||||
url, err := AssertTypeStringNotNull(urlInterface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WebFanqiangdangRSS{
|
||||
c: tool.GetColly(),
|
||||
Url: url,
|
||||
}, nil
|
||||
}
|
||||
return nil, ErrorUrlNotFound
|
||||
}
|
||||
|
||||
func (w *WebFanqiangdangRSS) Get() proxy.ProxyList {
|
||||
w.results = make([]string, 0)
|
||||
w.c.OnHTML("td.t_f", func(e *colly.HTMLElement) {
|
||||
w.results = append(w.results, GrepLinksFromString(e.Text)...)
|
||||
})
|
||||
|
||||
w.c.OnXML("//item//link", func(e *colly.XMLElement) {
|
||||
_ = e.Request.Visit(e.Text)
|
||||
})
|
||||
|
||||
w.results = make([]string, 0)
|
||||
err := w.c.Visit(w.Url)
|
||||
if err != nil {
|
||||
_ = fmt.Errorf("%s", err.Error())
|
||||
}
|
||||
|
||||
return StringArray2ProxyArray(w.results)
|
||||
}
|
||||
|
||||
func (w *WebFanqiangdangRSS) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := w.Get()
|
||||
log.Printf("STATISTIC: Fanqiangdang\tcount=%d\turl=%s\n", len(nodes), w.Url)
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package getter
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/proxy"
|
||||
@@ -34,6 +35,7 @@ func (w *WebFreessrXyz) Get() proxy.ProxyList {
|
||||
func (w *WebFreessrXyz) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := w.Get()
|
||||
log.Printf("STATISTIC: FreeSSRxyz\tcount=%d\turl=%s\n", len(nodes), "api.free-ssr.xyz")
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package getter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/proxy"
|
||||
@@ -33,6 +34,7 @@ func (w *WebFuzz) Get() proxy.ProxyList {
|
||||
func (w *WebFuzz) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := w.Get()
|
||||
log.Printf("STATISTIC: WebFuzz\tcount=%d\turl=%s\n", len(nodes), w.Url)
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package getter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
@@ -39,6 +40,7 @@ func (w *WebFuzzSub) Get() proxy.ProxyList {
|
||||
func (w *WebFuzzSub) Get2Chan(pc chan proxy.Proxy, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
nodes := w.Get()
|
||||
log.Printf("STATISTIC: WebFuzzSub\tcount=%d\turl=%s\n", len(nodes), w.Url)
|
||||
for _, node := range nodes {
|
||||
pc <- node
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/proxy"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
Provide() string
|
||||
}
|
||||
@@ -12,3 +18,72 @@ func checkInList(list []string, item string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
Proxies *proxy.ProxyList `yaml:"proxies"`
|
||||
Types string `yaml:"type"`
|
||||
Country string `yaml:"country"`
|
||||
NotCountry string `yaml:"not_country"`
|
||||
}
|
||||
|
||||
func (b *Base) preFilter() {
|
||||
proxies := make(proxy.ProxyList, 0)
|
||||
|
||||
needFilterType := true
|
||||
needFilterCountry := true
|
||||
needFilterNotCountry := true
|
||||
if b.Types == "" || b.Types == "all" {
|
||||
needFilterType = false
|
||||
}
|
||||
if b.Country == "" || b.Country == "all" {
|
||||
needFilterCountry = false
|
||||
}
|
||||
if b.NotCountry == "" {
|
||||
needFilterNotCountry = false
|
||||
}
|
||||
types := strings.Split(b.Types, ",")
|
||||
countries := strings.Split(b.Country, ",")
|
||||
notCountries := strings.Split(b.NotCountry, ",")
|
||||
|
||||
bProxies := *b.Proxies
|
||||
for _, p := range bProxies {
|
||||
if needFilterType {
|
||||
typeOk := false
|
||||
for _, t := range types {
|
||||
if p.TypeName() == t {
|
||||
typeOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !typeOk {
|
||||
goto exclude
|
||||
}
|
||||
}
|
||||
|
||||
if needFilterNotCountry {
|
||||
for _, c := range notCountries {
|
||||
if strings.Contains(p.BaseInfo().Name, c) {
|
||||
goto exclude
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needFilterCountry {
|
||||
countryOk := false
|
||||
for _, c := range countries {
|
||||
if strings.Contains(p.BaseInfo().Name, c) {
|
||||
countryOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !countryOk {
|
||||
goto exclude
|
||||
}
|
||||
}
|
||||
|
||||
proxies = append(proxies, p)
|
||||
exclude:
|
||||
}
|
||||
|
||||
b.Proxies = &proxies
|
||||
}
|
||||
|
||||
@@ -7,14 +7,12 @@ import (
|
||||
)
|
||||
|
||||
type Clash struct {
|
||||
Proxies proxy.ProxyList `yaml:"proxies"`
|
||||
Types string `yaml:"type"`
|
||||
Country string `yaml:"country"`
|
||||
Base
|
||||
}
|
||||
|
||||
func (c Clash) CleanProxies() (proxies proxy.ProxyList) {
|
||||
proxies = make(proxy.ProxyList, 0)
|
||||
for _, p := range c.Proxies {
|
||||
for _, p := range *c.Proxies {
|
||||
if checkClashSupport(p) {
|
||||
proxies = append(proxies, p)
|
||||
}
|
||||
@@ -23,49 +21,15 @@ func (c Clash) CleanProxies() (proxies proxy.ProxyList) {
|
||||
}
|
||||
|
||||
func (c Clash) Provide() string {
|
||||
c.preFilter()
|
||||
|
||||
var resultBuilder strings.Builder
|
||||
resultBuilder.WriteString("proxies:\n")
|
||||
|
||||
noNeedFilterType := false
|
||||
noNeedFilterCountry := false
|
||||
if c.Types == "" || c.Types == "all" {
|
||||
noNeedFilterType = true
|
||||
}
|
||||
if c.Country == "" || c.Country == "all" {
|
||||
noNeedFilterCountry = true
|
||||
}
|
||||
types := strings.Split(c.Types, ",")
|
||||
countries := strings.Split(c.Country, ",")
|
||||
|
||||
for _, p := range c.Proxies {
|
||||
if !checkClashSupport(p) {
|
||||
continue
|
||||
}
|
||||
|
||||
typeOk := false
|
||||
countryOk := false
|
||||
if !noNeedFilterType {
|
||||
for _, t := range types {
|
||||
if p.TypeName() == t {
|
||||
typeOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !noNeedFilterCountry {
|
||||
for _, c := range countries {
|
||||
if strings.HasPrefix(p.BaseInfo().Name, c) {
|
||||
countryOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noNeedFilterType || typeOk) && (noNeedFilterCountry || countryOk) {
|
||||
for _, p := range *c.Proxies {
|
||||
if checkClashSupport(p) {
|
||||
resultBuilder.WriteString(p.ToClash() + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return resultBuilder.String()
|
||||
}
|
||||
|
||||
|
||||
21
pkg/provider/ssrsub.go
Normal file
21
pkg/provider/ssrsub.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/tool"
|
||||
)
|
||||
|
||||
type SSRSub struct {
|
||||
Base
|
||||
}
|
||||
|
||||
func (sub SSRSub) Provide() string {
|
||||
sub.Types = "ssr"
|
||||
sub.preFilter()
|
||||
var resultBuilder strings.Builder
|
||||
for _, p := range *sub.Proxies {
|
||||
resultBuilder.WriteString(p.Link() + "\n")
|
||||
}
|
||||
return tool.Base64EncodeString(resultBuilder.String(), false)
|
||||
}
|
||||
45
pkg/provider/sssub.go
Normal file
45
pkg/provider/sssub.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/proxy"
|
||||
)
|
||||
|
||||
type SSSub struct {
|
||||
Base
|
||||
}
|
||||
|
||||
type ssJson struct {
|
||||
Remarks string `json:"remarks"`
|
||||
Server string `json:"server"`
|
||||
ServerPort string `json:"server_port"`
|
||||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Plugin string `json:"plugin"`
|
||||
PluginOpts map[string]interface{} `json:"plugin_opts"`
|
||||
}
|
||||
|
||||
func (sub SSSub) Provide() string {
|
||||
sub.Types = "ss"
|
||||
sub.preFilter()
|
||||
proxies := make([]ssJson, 0, sub.Proxies.Len())
|
||||
for _, p := range *sub.Proxies {
|
||||
pp := p.(*proxy.Shadowsocks)
|
||||
proxies = append(proxies, ssJson{
|
||||
Remarks: pp.Name,
|
||||
Server: pp.Server,
|
||||
ServerPort: strconv.Itoa(pp.Port),
|
||||
Method: pp.Cipher,
|
||||
Password: pp.Password,
|
||||
Plugin: pp.Plugin,
|
||||
PluginOpts: pp.PluginOpts,
|
||||
})
|
||||
}
|
||||
text, err := json.Marshal(proxies)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(text)
|
||||
}
|
||||
@@ -7,18 +7,18 @@ import (
|
||||
)
|
||||
|
||||
type Surge struct {
|
||||
Proxies proxy.ProxyList `yaml:"proxies"`
|
||||
Base
|
||||
}
|
||||
|
||||
func (s Surge) Provide() string {
|
||||
var resultBuilder strings.Builder
|
||||
s.preFilter()
|
||||
|
||||
for _, p := range s.Proxies {
|
||||
var resultBuilder strings.Builder
|
||||
for _, p := range *s.Proxies {
|
||||
if checkSurgeSupport(p) {
|
||||
resultBuilder.WriteString(p.ToSurge() + "\n")
|
||||
resultBuilder.WriteString(p.ToClash() + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return resultBuilder.String()
|
||||
}
|
||||
|
||||
|
||||
21
pkg/provider/vmesssub.go
Normal file
21
pkg/provider/vmesssub.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zu1k/proxypool/pkg/tool"
|
||||
)
|
||||
|
||||
type VmessSub struct {
|
||||
Base
|
||||
}
|
||||
|
||||
func (sub VmessSub) Provide() string {
|
||||
sub.Types = "vmess"
|
||||
sub.preFilter()
|
||||
var resultBuilder strings.Builder
|
||||
for _, p := range *sub.Proxies {
|
||||
resultBuilder.WriteString(p.Link() + "\n")
|
||||
}
|
||||
return tool.Base64EncodeString(resultBuilder.String(), false)
|
||||
}
|
||||
@@ -36,6 +36,7 @@ type Proxy interface {
|
||||
String() string
|
||||
ToClash() string
|
||||
ToSurge() string
|
||||
Link() string
|
||||
Identifier() string
|
||||
SetName(name string)
|
||||
SetIP(ip string)
|
||||
|
||||
62
pkg/proxy/link_test.go
Normal file
62
pkg/proxy/link_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSSLink(t *testing.T) {
|
||||
ss, err := ParseSSLink("ss://YWVzLTI1Ni1jZmI6ZUlXMERuazY5NDU0ZTZuU3d1c3B2OURtUzIwMXRRMERAMTcyLjEwNC4xNjEuNTQ6ODA5OQ==#翻墙党223.13新加坡")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(ss)
|
||||
fmt.Println(ss.Link())
|
||||
ss, err = ParseSSLink(ss.Link())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(ss)
|
||||
}
|
||||
|
||||
func TestSSRLink(t *testing.T) {
|
||||
ssr, err := ParseSSRLink("ssr://MTcyLjEwNC4xNjEuNTQ6ODA5OTpvcmlnaW46YWVzLTI1Ni1jZmI6cGxhaW46WlVsWE1FUnVhelk1TkRVMFpUWnVVM2QxYzNCMk9VUnRVekl3TVhSUk1FUT0vP29iZnNwYXJhbT0mcHJvdG9wYXJhbT0mcmVtYXJrcz01Ny03NWFLWjVZV2FNakl6TGpFejVwYXc1WXFnNVoyaCZncm91cD01cGF3NVlxZzVaMmg=")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(ssr)
|
||||
fmt.Println(ssr.Link())
|
||||
ssr, err = ParseSSRLink(ssr.Link())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(ssr)
|
||||
}
|
||||
|
||||
func TestTrojanLink(t *testing.T) {
|
||||
trojan, err := ParseTrojanLink("trojan://65474277@sqcu.hostmsu.ru:55551?allowinsecure=0&peer=mza.hkfq.xyz&mux=1&ws=0&wspath=&wshost=&ss=0&ssmethod=aes-128-gcm&sspasswd=&group=#%E9%A6%99%E6%B8%AFCN2-MZA%E8%8A%82%E7%82%B9-%E5%AE%BF%E8%BF%81%E8%81%94%E9%80%9A%E4%B8%AD%E8%BD%AC")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(trojan)
|
||||
fmt.Println(trojan.Link())
|
||||
trojan, err = ParseTrojanLink(trojan.Link())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(trojan)
|
||||
}
|
||||
|
||||
func TestVmessLink(t *testing.T) {
|
||||
v, err := ParseVmessLink("vmess://ew0KICAidiI6ICIyIiwNCiAgInBzIjogIuW+ruS/oeWFrOS8l+WPtyDlpJrlvannmoTlpKfljYPkuJbnlYwiLA0KICAiYWRkIjogInMyNzEuc25vZGUueHl6IiwNCiAgInBvcnQiOiAiNDQzIiwNCiAgImlkIjogIjZhOTAwZDYzLWNiOTItMzVhMC1hZWYwLTNhMGMxMWFhODUyMyIsDQogICJhaWQiOiAiMSIsDQogICJuZXQiOiAid3MiLA0KICAidHlwZSI6ICJub25lIiwNCiAgImhvc3QiOiAiczI3MS5zbm9kZS54eXoiLA0KICAicGF0aCI6ICIvcGFuZWwiLA0KICAidGxzIjogInRscyINCn0=")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(v)
|
||||
fmt.Println(v.Link())
|
||||
v, err = ParseVmessLink(v.Link())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
fmt.Println(v)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -94,8 +95,7 @@ func (ps ProxyList) NameReIndex() ProxyList {
|
||||
num := len(ps)
|
||||
for i := 0; i < num; i++ {
|
||||
originName := ps[i].BaseInfo().Name
|
||||
// country := string([]rune(originName)[:2])
|
||||
country := string([]rune(originName)[:5])
|
||||
country := strings.SplitN(originName, "_", 2)[0]
|
||||
ps[i].SetName(fmt.Sprintf("%s_%+02v", country, i+1))
|
||||
}
|
||||
return ps
|
||||
|
||||
@@ -65,6 +65,13 @@ func (ss Shadowsocks) Clone() Proxy {
|
||||
return &ss
|
||||
}
|
||||
|
||||
// https://shadowsocks.org/en/config/quick-guide.html
|
||||
func (ss Shadowsocks) Link() (link string) {
|
||||
payload := fmt.Sprintf("%s:%s@%s:%d", ss.Cipher, ss.Password, ss.Server, ss.Port)
|
||||
payload = tool.Base64EncodeString(payload, false)
|
||||
return fmt.Sprintf("ss://%s#%s", payload, ss.Name)
|
||||
}
|
||||
|
||||
func ParseSSLink(link string) (*Shadowsocks, error) {
|
||||
if !strings.HasPrefix(link, "ss://") {
|
||||
return nil, ErrorNotSSRLink
|
||||
|
||||
@@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -61,6 +62,19 @@ func (ssr ShadowsocksR) Clone() Proxy {
|
||||
return &ssr
|
||||
}
|
||||
|
||||
// https://github.com/HMBSbige/ShadowsocksR-Windows/wiki/SSR-QRcode-scheme
|
||||
func (ssr ShadowsocksR) Link() (link string) {
|
||||
payload := fmt.Sprintf("%s:%d:%s:%s:%s:%s",
|
||||
ssr.Server, ssr.Port, ssr.Protocol, ssr.Cipher, ssr.Obfs, tool.Base64EncodeString(ssr.Password, true))
|
||||
query := url.Values{}
|
||||
query.Add("obfsparam", tool.Base64EncodeString(ssr.ObfsParam, true))
|
||||
query.Add("protoparam", tool.Base64EncodeString(ssr.ProtocolParam, true))
|
||||
query.Add("remarks", tool.Base64EncodeString(ssr.Name, true))
|
||||
query.Add("group", tool.Base64EncodeString("proxy.tgbot.co", true))
|
||||
payload = tool.Base64EncodeString(fmt.Sprintf("%s/?%s", payload, query.Encode()), true)
|
||||
return fmt.Sprintf("ssr://%s", payload)
|
||||
}
|
||||
|
||||
func ParseSSRLink(link string) (*ShadowsocksR, error) {
|
||||
if !strings.HasPrefix(link, "ssr") {
|
||||
return nil, ErrorNotSSRLink
|
||||
|
||||
@@ -66,6 +66,24 @@ func (t Trojan) Clone() Proxy {
|
||||
return &t
|
||||
}
|
||||
|
||||
// https://p4gefau1t.github.io/trojan-go/developer/url/
|
||||
func (t Trojan) Link() (link string) {
|
||||
query := url.Values{}
|
||||
if t.SNI != "" {
|
||||
query.Set("sni", url.QueryEscape(t.SNI))
|
||||
}
|
||||
|
||||
uri := url.URL{
|
||||
Scheme: "trojan",
|
||||
User: url.User(url.QueryEscape(t.Password)),
|
||||
Host: net.JoinHostPort(t.Server, strconv.Itoa(t.Port)),
|
||||
RawQuery: query.Encode(),
|
||||
Fragment: t.Name,
|
||||
}
|
||||
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
func ParseTrojanLink(link string) (*Trojan, error) {
|
||||
if !strings.HasPrefix(link, "trojan://") && !strings.HasPrefix(link, "trojan-go://") {
|
||||
return nil, ErrorNotTrojanink
|
||||
|
||||
@@ -86,6 +86,14 @@ func (v Vmess) Clone() Proxy {
|
||||
return &v
|
||||
}
|
||||
|
||||
func (v Vmess) Link() (link string) {
|
||||
vjv, err := json.Marshal(v.toLinkJson())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return fmt.Sprintf("vmess://%s", tool.Base64EncodeBytes(vjv))
|
||||
}
|
||||
|
||||
type vmessLinkJson struct {
|
||||
Add string `json:"add"`
|
||||
V string `json:"v"`
|
||||
@@ -100,6 +108,27 @@ type vmessLinkJson struct {
|
||||
Tls string `json:"tls"`
|
||||
}
|
||||
|
||||
func (v Vmess) toLinkJson() vmessLinkJson {
|
||||
vj := vmessLinkJson{
|
||||
Add: v.Server,
|
||||
Ps: v.Name,
|
||||
Port: v.Port,
|
||||
Id: v.UUID,
|
||||
Aid: strconv.Itoa(v.AlterID),
|
||||
Net: v.Network,
|
||||
Path: v.WSPath,
|
||||
Host: v.ServerName,
|
||||
V: "2",
|
||||
}
|
||||
if v.TLS {
|
||||
vj.Tls = "tls"
|
||||
}
|
||||
if host, ok := v.WSHeaders["HOST"]; ok && host != "" {
|
||||
vj.Host = host
|
||||
}
|
||||
return vj
|
||||
}
|
||||
|
||||
func ParseVmessLink(link string) (*Vmess, error) {
|
||||
if !strings.HasPrefix(link, "vmess") {
|
||||
return nil, ErrorNotVmessLink
|
||||
@@ -190,10 +219,11 @@ func ParseVmessLink(link string) (*Vmess, error) {
|
||||
}
|
||||
port := 443
|
||||
portInterface := vmessJson.Port
|
||||
if i, ok := portInterface.(int); ok {
|
||||
port = i
|
||||
} else if s, ok := portInterface.(string); ok {
|
||||
port, _ = strconv.Atoi(s)
|
||||
switch portInterface.(type) {
|
||||
case int:
|
||||
port = portInterface.(int)
|
||||
case string:
|
||||
port, _ = strconv.Atoi(portInterface.(string))
|
||||
}
|
||||
|
||||
alterId, err := strconv.Atoi(vmessJson.Aid)
|
||||
|
||||
@@ -26,3 +26,14 @@ func Base64DecodeString(src string) (dst string, err error) {
|
||||
dst = string(dstbytes)
|
||||
return
|
||||
}
|
||||
|
||||
func Base64EncodeString(origin string, urlsafe bool) (result string) {
|
||||
if urlsafe {
|
||||
return base64.URLEncoding.EncodeToString([]byte(origin))
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString([]byte(origin))
|
||||
}
|
||||
|
||||
func Base64EncodeBytes(origin []byte) (result string) {
|
||||
return base64.StdEncoding.EncodeToString([]byte(origin))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user