Skip to content

Commit f4bfc79

Browse files
First commit
0 parents  commit f4bfc79

File tree

10 files changed

+615
-0
lines changed

10 files changed

+615
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build.sh
2+
release/

README.md

Whitespace-only changes.

cmd/main/main.go

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
// cmd/main/main.go
2+
package main
3+
4+
import (
5+
"bufio"
6+
"context"
7+
"fmt"
8+
"os"
9+
"regexp"
10+
"strings"
11+
"time"
12+
13+
"ProxyRiskScoreChecker/internal/logging"
14+
"ProxyRiskScoreChecker/internal/models"
15+
"ProxyRiskScoreChecker/internal/proxyvalidate"
16+
"ProxyRiskScoreChecker/internal/riskscore"
17+
)
18+
19+
const (
20+
IPInfoEndpoint = "http://ipinfo.io/json"
21+
IPQSEndpointFmt = "https://ipqualityscore.com/api/json/ip/%s/%s?strictness=%s"
22+
InputProxiesFileName = "proxies.txt"
23+
OutputFileName = "proxies_risk_score_0.txt"
24+
ValidProxiesFileName = "validproxys.txt"
25+
EnvAPIKey = "API_KEY"
26+
RequestTimeout = 10 * time.Second
27+
ValidationTimeout = 5 * time.Second
28+
)
29+
30+
type proxyLogger struct{}
31+
32+
func (l *proxyLogger) Log(logType logging.LogType, format string, args ...interface{}) {
33+
var prefix string
34+
switch logType {
35+
case logging.LogSuccess:
36+
prefix = "[+] "
37+
case logging.LogError:
38+
prefix = "[-] "
39+
case logging.LogQuestion:
40+
prefix = "[?] "
41+
case logging.LogInfo:
42+
prefix = "[*] "
43+
}
44+
message := fmt.Sprintf(format, args...)
45+
fmt.Println(prefix + message)
46+
}
47+
48+
type proxyConverter struct{}
49+
50+
func (c *proxyConverter) ConvertProxyFormat(proxy string) string {
51+
host, port, user, password, protocol := ParseProxy(proxy)
52+
if host == "" || port == "" {
53+
return ""
54+
}
55+
if user != "" && password != "" {
56+
return fmt.Sprintf("%s://%s:%s@%s:%s", protocol, user, password, host, port)
57+
}
58+
return fmt.Sprintf("%s://%s:%s", protocol, host, port)
59+
}
60+
61+
type ProxyService struct {
62+
Validator models.ProxyValidator
63+
RiskChecker riskscore.RiskScoreValidator
64+
RequestTimeout time.Duration
65+
logger logging.Logger
66+
converter proxyvalidate.ProxyConverter
67+
}
68+
69+
// NewProxyService creates a new service instance
70+
func NewProxyService(logger logging.Logger) *ProxyService {
71+
converter := &proxyConverter{}
72+
validator := proxyvalidate.NewProxyValidator(
73+
ValidationTimeout,
74+
logger,
75+
converter,
76+
)
77+
riskChecker := riskscore.NewRiskScoreService(
78+
RequestTimeout,
79+
logger,
80+
converter,
81+
)
82+
return &ProxyService{
83+
Validator: validator,
84+
RiskChecker: riskChecker,
85+
RequestTimeout: RequestTimeout,
86+
logger: logger,
87+
converter: converter,
88+
}
89+
}
90+
91+
func LogQuestionInput(logger logging.Logger, format string, args ...any) string {
92+
logger.Log(logging.LogQuestion, format, args...)
93+
reader := bufio.NewReader(os.Stdin)
94+
input, _ := reader.ReadString('\n')
95+
return strings.TrimSpace(input)
96+
}
97+
98+
func GetAPIKey() (string, error) {
99+
apiKey := os.Getenv(EnvAPIKey)
100+
if apiKey == "" {
101+
return "", fmt.Errorf("API_KEY environment variable is not set. Please set it before running the application")
102+
}
103+
return apiKey, nil
104+
}
105+
106+
func ReadProxiesFromFile(filename string) ([]string, error) {
107+
file, err := os.Open(filename)
108+
if err != nil {
109+
return nil, fmt.Errorf("failed to open file %s: %v", filename, err)
110+
}
111+
defer file.Close()
112+
var proxies []string
113+
scanner := bufio.NewScanner(file)
114+
for scanner.Scan() {
115+
line := strings.TrimSpace(scanner.Text())
116+
if line != "" {
117+
proxies = append(proxies, line)
118+
}
119+
}
120+
if err := scanner.Err(); err != nil {
121+
return nil, fmt.Errorf("error scanning file %s: %v", filename, err)
122+
}
123+
return proxies, nil
124+
}
125+
126+
func DetectProxyProtocol(logger logging.Logger, proxy string) string {
127+
proxy = strings.ToLower(proxy)
128+
if strings.HasPrefix(proxy, "http://") {
129+
return "http"
130+
} else if strings.HasPrefix(proxy, "https://") {
131+
return "https"
132+
} else if strings.HasPrefix(proxy, "socks5://") {
133+
return "socks5"
134+
}
135+
parts := strings.Split(proxy, ":")
136+
if len(parts) > 0 {
137+
switch parts[0] {
138+
case "http", "https", "socks5":
139+
return parts[0]
140+
}
141+
}
142+
logger.Log(logging.LogInfo, "No protocol detected for proxy %s, using HTTP as default", proxy)
143+
return "http"
144+
}
145+
146+
func ParseProxy(proxy string) (host, port, user, password, protocol string) {
147+
proxy = strings.TrimSpace(proxy)
148+
protocolWithAuthPattern := regexp.MustCompile(`^(http|https|socks5)://(.+):(.+)@(.+):(\d+)$`)
149+
if match := protocolWithAuthPattern.FindStringSubmatch(proxy); match != nil {
150+
protocol = match[1]
151+
user = match[2]
152+
password = match[3]
153+
host = match[4]
154+
port = match[5]
155+
return
156+
}
157+
protocolNoAuthPattern := regexp.MustCompile(`^(http|https|socks5)://(.+):(\d+)$`)
158+
if match := protocolNoAuthPattern.FindStringSubmatch(proxy); match != nil {
159+
protocol = match[1]
160+
host = match[2]
161+
port = match[3]
162+
return
163+
}
164+
protocolPrefix := regexp.MustCompile(`^(http|https|socks5):/?/?`)
165+
if match := protocolPrefix.FindStringSubmatch(proxy); match != nil {
166+
protocol = match[1]
167+
proxy = protocolPrefix.ReplaceAllString(proxy, "")
168+
}
169+
userPassHostPattern := regexp.MustCompile(`^(.+):(.+)@(.+):(\d+)$`)
170+
if match := userPassHostPattern.FindStringSubmatch(proxy); match != nil {
171+
user = match[1]
172+
password = match[2]
173+
host = match[3]
174+
port = match[4]
175+
return
176+
}
177+
parts := strings.Split(proxy, ":")
178+
switch len(parts) {
179+
case 4:
180+
host = parts[0]
181+
port = parts[1]
182+
user = parts[2]
183+
password = parts[3]
184+
case 3:
185+
if parts[0] == "http" || parts[0] == "https" || parts[0] == "socks5" {
186+
protocol = parts[0]
187+
host = parts[1]
188+
port = parts[2]
189+
} else {
190+
host = parts[0]
191+
port = parts[1]
192+
user = parts[2]
193+
}
194+
case 2:
195+
host = parts[0]
196+
port = parts[1]
197+
default:
198+
return "", "", "", "", ""
199+
}
200+
if protocol == "" {
201+
protocol = "http"
202+
}
203+
return
204+
}
205+
206+
func ConvertProxyFormat(logger logging.Logger, proxy string) string {
207+
host, port, user, password, protocol := ParseProxy(proxy)
208+
if host == "" || port == "" {
209+
logger.Log(logging.LogError, "Invalid proxy format (missing host or port): %s", proxy)
210+
return ""
211+
}
212+
logger.Log(logging.LogInfo, "Parsed proxy - Protocol: %s, Host: %s, Port: %s, Auth: %t",
213+
protocol, host, port, (user != "" && password != ""))
214+
if user != "" && password != "" {
215+
logger.Log(logging.LogInfo, "Using proxy with authentication")
216+
return fmt.Sprintf("%s://%s:%s@%s:%s", protocol, user, password, host, port)
217+
} else {
218+
logger.Log(logging.LogInfo, "Using proxy without authentication")
219+
return fmt.Sprintf("%s://%s:%s", protocol, host, port)
220+
}
221+
}
222+
223+
func (s *ProxyService) ValidateAndSaveProxies(proxyList []string, filename string) ([]string, error) {
224+
var validProxies []string
225+
ctx := context.Background()
226+
for _, proxy := range proxyList {
227+
formattedProxy := s.converter.ConvertProxyFormat(proxy)
228+
if formattedProxy == "" {
229+
s.logger.Log(logging.LogError, "Invalid proxy format: %s", proxy)
230+
continue
231+
}
232+
if s.Validator.ValidateProxy(ctx, formattedProxy) {
233+
validProxies = append(validProxies, proxy)
234+
}
235+
}
236+
if err := saveProxiesToFile(validProxies, filename); err != nil {
237+
return nil, fmt.Errorf("failed to save proxies: %w", err)
238+
}
239+
return validProxies, nil
240+
}
241+
242+
func prepareProxies(logger logging.Logger) ([]string, string, string, error) {
243+
apiKey, err := GetAPIKey()
244+
if err != nil {
245+
logger.Log(logging.LogError, "%v", err)
246+
logger.Log(logging.LogInfo, "Set the API_KEY environment variable with your IPQS API key")
247+
logger.Log(logging.LogInfo, "Example: export API_KEY=your_api_key_here")
248+
return nil, "", "", err
249+
}
250+
logger.Log(logging.LogSuccess, "API key loaded successfully from environment variable")
251+
strictnessLevel := LogQuestionInput(logger, "Enter strictness level (0-3) (leave blank for 0)")
252+
if strictnessLevel == "" {
253+
strictnessLevel = "0"
254+
}
255+
inputFileName := LogQuestionInput(logger, "Enter proxy file name (leave blank for default '%s')", InputProxiesFileName)
256+
if inputFileName == "" {
257+
inputFileName = InputProxiesFileName
258+
}
259+
proxyInput, err := ReadProxiesFromFile(inputFileName)
260+
if err != nil {
261+
logger.Log(logging.LogError, "Failed to read proxies from file: %v", err)
262+
return nil, "", "", err
263+
}
264+
logger.Log(logging.LogSuccess, "Successfully read %d proxies from %s", len(proxyInput), inputFileName)
265+
if len(proxyInput) == 0 {
266+
logger.Log(logging.LogError, "No proxies found in the file. Exiting.")
267+
return nil, "", "", fmt.Errorf("no proxies found in input file")
268+
}
269+
return proxyInput, apiKey, strictnessLevel, nil
270+
}
271+
272+
func saveProxiesToFile(proxies []string, filename string) error {
273+
file, err := os.Create(filename)
274+
if err != nil {
275+
return fmt.Errorf("failed to create file %s: %v", filename, err)
276+
}
277+
defer file.Close()
278+
for _, proxy := range proxies {
279+
if _, err := file.WriteString(proxy + "\n"); err != nil {
280+
return fmt.Errorf("failed to write proxy: %v", err)
281+
}
282+
}
283+
return nil
284+
}
285+
286+
func Run(logger logging.Logger) error {
287+
service := NewProxyService(logger)
288+
proxyInput, apiKey, strictnessLevel, err := prepareProxies(logger)
289+
if err != nil {
290+
return err
291+
}
292+
validProxies, err := service.ValidateAndSaveProxies(proxyInput, ValidProxiesFileName)
293+
if err != nil {
294+
return fmt.Errorf("%w", err)
295+
}
296+
if len(validProxies) == 0 {
297+
return fmt.Errorf("no valid proxies found")
298+
}
299+
filteredProxies := service.RiskChecker.FilterProxies(validProxies, apiKey, strictnessLevel)
300+
if err := saveProxiesToFile(filteredProxies, OutputFileName); err != nil {
301+
return err
302+
}
303+
logger.Log(logging.LogSuccess, "Found %d clean proxies", len(filteredProxies))
304+
return nil
305+
}
306+
307+
func main() {
308+
logger := &proxyLogger{}
309+
logger.Log(logging.LogInfo, "Starting Proxy Risk Score Checker")
310+
if err := Run(logger); err != nil {
311+
logger.Log(logging.LogError, "Application error: %v", err)
312+
os.Exit(1)
313+
}
314+
logger.Log(logging.LogSuccess, "Operation completed successfully")
315+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module ProxyRiskScoreChecker
2+
3+
go 1.24.1

internal/logging/logging.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// internal/logging/logging.go
2+
package logging
3+
4+
type LogType int
5+
6+
const (
7+
LogSuccess LogType = iota
8+
LogError
9+
LogQuestion
10+
LogInfo
11+
)
12+
13+
type Logger interface {
14+
Log(logType LogType, format string, args ...interface{})
15+
}

internal/models/models.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// internal/models/models.go
2+
package models
3+
4+
import "context"
5+
6+
type ProxyValidator interface {
7+
ValidateProxy(ctx context.Context, proxy string) bool
8+
}

internal/proxyvalidate/types.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// internal/proxyvalidate/types.go
2+
package proxyvalidate
3+
4+
import (
5+
"ProxyRiskScoreChecker/internal/logging"
6+
"time"
7+
)
8+
9+
type ProxyValidator struct {
10+
ValidationTimeout time.Duration
11+
logger logging.Logger
12+
proxyConverter ProxyConverter
13+
}
14+
15+
type ProxyConverter interface {
16+
ConvertProxyFormat(proxy string) string
17+
}

0 commit comments

Comments
 (0)