Skip to content

Commit 32b8c72

Browse files
author
Delta-Kronecker
committed
add patt sub
1 parent 4d2afd3 commit 32b8c72

11 files changed

Lines changed: 753 additions & 399 deletions

File tree

.github/workflows/update-configs.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ jobs:
5858
ls -lh config/protocols/ 2>/dev/null || true
5959
ls -lh config/batches/v2ray/ 2>/dev/null || true
6060
ls -lh config/batches/clash/ 2>/dev/null || true
61-
ls -lh config/batches/clash_advanced/ 2>/dev/null || true
6261
6362
if [ -f "config/all_configs.txt" ]; then
6463
echo "Total V2ray configs: $(wc -l < config/all_configs.txt)"
@@ -67,13 +66,8 @@ jobs:
6766
CLASH_COUNT=$(grep -c ' - name:' config/clash.yaml 2>/dev/null || echo 0)
6867
echo "Clash standard proxies: $CLASH_COUNT"
6968
fi
70-
if [ -f "config/clash_advanced.yaml" ]; then
71-
ADV_COUNT=$(grep -c ' - name:' config/clash_advanced.yaml 2>/dev/null || echo 0)
72-
echo "Clash advanced proxies: $ADV_COUNT"
73-
fi
7469
echo "V2ray batches: $(ls config/batches/v2ray/*.txt 2>/dev/null | wc -l)"
7570
echo "Clash standard batches: $(ls config/batches/clash/*.yaml 2>/dev/null | wc -l)"
76-
echo "Clash advanced batches: $(ls config/batches/clash_advanced/*.yaml 2>/dev/null | wc -l)"
7771
7872
echo "=== Logs ==="
7973
ls -lh logs/ 2>/dev/null || echo "No logs directory"

src/farg.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func writeFargFiles(results []configResult) {
170170
writeJSONArray(filepath.Join("config", "farg", "protocols", proto+".json"), entries)
171171
}
172172
numBatches := writeFargBatches(all)
173-
fmt.Printf("📁 Farg: wrote %d xray configs into config/farg (all) + %d protocol files + %d batch files\n",
173+
fmt.Printf(" Farg: wrote %d xray configs into config/farg (all) + %d protocol files + %d batch files\n",
174174
len(all), len(byProto), numBatches)
175175
}
176176

@@ -442,15 +442,15 @@ func buildFargRouting() fargRouting {
442442
func writeJSONArray(path string, entries any) {
443443
f, err := os.Create(path)
444444
if err != nil {
445-
fmt.Printf(" Cannot write %s: %v\n", path, err)
445+
fmt.Printf(" Cannot write %s: %v\n", path, err)
446446
return
447447
}
448448
defer f.Close()
449449
enc := json.NewEncoder(f)
450450
enc.SetIndent("", " ")
451451
enc.SetEscapeHTML(false)
452452
if err := enc.Encode(entries); err != nil {
453-
fmt.Printf(" Cannot encode %s: %v\n", path, err)
453+
fmt.Printf(" Cannot encode %s: %v\n", path, err)
454454
}
455455
}
456456

src/fetcher.go

Lines changed: 142 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,71 @@ func loadSubsFromFile(path string) []string {
3131
return urls
3232
}
3333

34+
// dedupSubFile removes duplicate URLs from sub.txt (case-insensitive).
35+
func dedupSubFile(path string) {
36+
data, err := os.ReadFile(path)
37+
if err != nil {
38+
return
39+
}
40+
41+
lines := strings.Split(string(data), "\n")
42+
seen := make(map[string]bool)
43+
var unique []string
44+
45+
for _, line := range lines {
46+
trimmed := strings.TrimSpace(line)
47+
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
48+
unique = append(unique, line)
49+
continue
50+
}
51+
key := strings.ToLower(trimmed)
52+
if !seen[key] {
53+
seen[key] = true
54+
unique = append(unique, line)
55+
}
56+
}
57+
58+
_ = os.WriteFile(path, []byte(strings.Join(unique, "\n")), 0644)
59+
}
60+
61+
// cleanSubFile removes failed URLs from sub.txt.
62+
func cleanSubFile(path string, failedURLs []string) {
63+
if len(failedURLs) == 0 {
64+
return
65+
}
66+
67+
data, err := os.ReadFile(path)
68+
if err != nil {
69+
return
70+
}
71+
72+
failedSet := make(map[string]bool)
73+
for _, u := range failedURLs {
74+
failedSet[strings.TrimSpace(u)] = true
75+
}
76+
77+
lines := strings.Split(string(data), "\n")
78+
var kept []string
79+
removed := 0
80+
for _, line := range lines {
81+
trimmed := strings.TrimSpace(line)
82+
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
83+
kept = append(kept, line)
84+
continue
85+
}
86+
if failedSet[trimmed] {
87+
removed++
88+
continue
89+
}
90+
kept = append(kept, line)
91+
}
92+
93+
if removed > 0 {
94+
_ = os.WriteFile(path, []byte(strings.Join(kept, "\n")), 0644)
95+
fmt.Printf(" sub.txt: removed %d failed/duplicate URLs\n", removed)
96+
}
97+
}
98+
3499
// ── fetchAll (legacy base64/text links) ──────────────────────────────────────
35100

36101
func fetchAll(base64Links, textLinks []string) ([]string, []string) {
@@ -58,7 +123,7 @@ func fetchAll(base64Links, textLinks []string) ([]string, []string) {
58123

59124
total := len(jobs)
60125
numBatches := (total + batchSize - 1) / batchSize
61-
fmt.Printf("📡 Fetching %d sources in %d batches of %d (timeout=%s retries=%d)\n",
126+
fmt.Printf(" Fetching %d sources in %d batches of %d (timeout=%s retries=%d)\n",
62127
total, numBatches, batchSize, fetchTimeout, retryCount)
63128

64129
var mu sync.Mutex
@@ -127,15 +192,15 @@ func fetchAll(base64Links, textLinks []string) ([]string, []string) {
127192
}
128193
mu.Unlock()
129194
}
130-
fmt.Printf(" Fetch done — ok=%d fail=%d total_lines=%d\n", okCount, failCount, len(lines))
195+
fmt.Printf(" Fetch done — ok=%d fail=%d total_lines=%d\n", okCount, failCount, len(lines))
131196
return lines, failed
132197
}
133198

134199
// ── fetchAllFromSubs (sub.txt mode) ──────────────────────────────────────────
135200

136-
func fetchAllFromSubs(subURLs []string) ([]string, []string) {
201+
func fetchAllFromSubs(subURLs []string) ([]string, []string, []string) {
137202
const batchSize = 20
138-
const fetchTimeout = 8 * time.Second
203+
const fetchTimeout = 20 * time.Second
139204

140205
retryCount := cfg.Validation.FetchRetryCount
141206
retryDelay := time.Duration(cfg.Validation.FetchRetryDelayMs) * time.Millisecond
@@ -148,79 +213,93 @@ func fetchAllFromSubs(subURLs []string) ([]string, []string) {
148213

149214
total := len(subURLs)
150215
numBatches := (total + batchSize - 1) / batchSize
151-
fmt.Printf("📡 Fetching %d sources in %d batches (timeout=%s retries=%d)\n",
216+
fmt.Printf(" Fetching %d sources in %d batches (timeout=%s retries=%d)\n",
152217
total, numBatches, fetchTimeout, retryCount)
153218

154-
var mu sync.Mutex
155219
var lines []string
156220
var failed []string
221+
var failedURLs []string
157222
var okCount, failCount int
158223

159-
for batchIdx := 0; batchIdx < numBatches; batchIdx++ {
160-
start := batchIdx * batchSize
161-
end := start + batchSize
162-
if end > total {
163-
end = total
164-
}
165-
batch := subURLs[start:end]
166-
167-
var wg sync.WaitGroup
168-
type batchResult struct {
169-
url string
170-
lines []string
171-
err error
172-
status int
173-
}
174-
results := make([]batchResult, len(batch))
175-
176-
for i, u := range batch {
177-
wg.Add(1)
178-
go func(idx int, rawURL string) {
179-
defer wg.Done()
180-
var fr fetchResult
181-
for attempt := 0; attempt <= retryCount; attempt++ {
182-
if attempt > 0 && retryDelay > 0 {
183-
time.Sleep(retryDelay)
224+
fetchBatch := func(urls []string) (ok int, fail int, fURLs []string) {
225+
bNumBatches := (len(urls) + batchSize - 1) / batchSize
226+
for batchIdx := 0; batchIdx < bNumBatches; batchIdx++ {
227+
start := batchIdx * batchSize
228+
end := start + batchSize
229+
if end > len(urls) {
230+
end = len(urls)
231+
}
232+
batch := urls[start:end]
233+
234+
var wg sync.WaitGroup
235+
type batchResult struct {
236+
url string
237+
lines []string
238+
err error
239+
status int
240+
}
241+
results := make([]batchResult, len(batch))
242+
243+
for i, u := range batch {
244+
wg.Add(1)
245+
go func(idx int, rawURL string) {
246+
defer wg.Done()
247+
var fr fetchResult
248+
for attempt := 0; attempt <= retryCount; attempt++ {
249+
if attempt > 0 && retryDelay > 0 {
250+
time.Sleep(retryDelay)
251+
}
252+
fr = fetchRaw(rawURL, fetchTimeout)
253+
if fr.err == nil && fr.statusCode == http.StatusOK {
254+
break
255+
}
184256
}
185-
fr = fetchRaw(rawURL, fetchTimeout)
186-
if fr.err == nil && fr.statusCode == http.StatusOK {
187-
break
257+
if fr.err != nil || fr.statusCode != http.StatusOK {
258+
results[idx] = batchResult{url: rawURL, err: fr.err, status: fr.statusCode}
259+
return
188260
}
189-
}
190-
if fr.err != nil || fr.statusCode != http.StatusOK {
191-
results[idx] = batchResult{url: rawURL, err: fr.err, status: fr.statusCode}
192-
return
193-
}
194-
extracted := smartDecode(fr.content)
195-
results[idx] = batchResult{url: rawURL, lines: extracted, status: fr.statusCode}
196-
}(i, u)
197-
}
198-
wg.Wait()
261+
extracted := smartDecode(fr.content)
262+
results[idx] = batchResult{url: rawURL, lines: extracted, status: fr.statusCode}
263+
}(i, u)
264+
}
265+
wg.Wait()
199266

200-
mu.Lock()
201-
for _, r := range results {
202-
if r.err != nil || r.status != http.StatusOK {
203-
status := "error"
204-
if r.status > 0 {
205-
status = fmt.Sprintf("HTTP %d", r.status)
267+
for _, r := range results {
268+
if r.err != nil || r.status != http.StatusOK {
269+
fail++
270+
fURLs = append(fURLs, r.url)
271+
continue
206272
}
207-
failCount++
208-
failed = append(failed, fmt.Sprintf("%s (%s)", r.url, status))
209-
if gLog != nil {
210-
gLog.writeLine(fmt.Sprintf("[FETCH] FAIL %s status=%s", r.url, status))
273+
if len(r.lines) == 0 {
274+
fail++
275+
fURLs = append(fURLs, r.url)
276+
continue
211277
}
212-
continue
278+
ok++
279+
lines = append(lines, r.lines...)
213280
}
214-
okCount++
215-
if gLog != nil {
216-
gLog.writeLine(fmt.Sprintf("[FETCH] OK %s lines=%d", r.url, len(r.lines)))
217-
}
218-
lines = append(lines, r.lines...)
219281
}
220-
mu.Unlock()
282+
return
221283
}
222-
fmt.Printf(" ✅ Fetch done — ok=%d fail=%d total_lines=%d\n", okCount, failCount, len(lines))
223-
return lines, failed
284+
285+
// ── First pass ──
286+
okCount, failCount, failedURLs = fetchBatch(subURLs)
287+
failed = failedURLs
288+
289+
// ── Retry passes ──
290+
for pass := 0; pass < retryCount && len(failedURLs) > 0; pass++ {
291+
fmt.Printf(" Retry pass %d — retrying %d failed sources (delay=%s)...\n",
292+
pass+1, len(failedURLs), retryDelay)
293+
time.Sleep(retryDelay)
294+
retryOK, retryFail, retryFailed := fetchBatch(failedURLs)
295+
okCount += retryOK
296+
failCount += retryFail
297+
failedURLs = retryFailed
298+
failed = retryFailed
299+
}
300+
301+
fmt.Printf(" Fetch done — ok=%d fail=%d total_lines=%d\n", okCount, failCount, len(lines))
302+
return lines, failed, failedURLs
224303
}
225304

226305
// ── fetchRaw ──────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)