diff --git a/README.md b/README.md index 87d4312..c21d2e4 100644 --- a/README.md +++ b/README.md @@ -2,26 +2,43 @@ [![GoDoc](https://godoc.org/git.tcp.direct/kayos/proxygonanza?status.svg)](https://godoc.org/git.tcp.direct/kayos/proxygonanza) [![Go Report Card](https://goreportcard.com/badge/github.com/yunginnanet/proxygonanza)](https://goreportcard.com/report/github.com/yunginnanet/proxygonanza) +# Table of Contents + +1. [ProxyGonanza](#proxygonanza) + 1. [Import Path](#import-path) + 1. [Constants and Variables](#constants-and-variables) + 1. [API Types](#api-types) + 1. [type AddAuthIPResponse](#type-addauthipresponse) + 1. [type AuthIP](#type-authip) + 1. [type AuthIPResponse](#type-authipresponse) + 1. [type DelAuthIPResponse](#type-delauthipresponse) + 1. [type Package](#type-package) + 1. [type PackageDetails](#type-packagedetails) + 1. [type PackageResponse](#type-packageresponse) + 1. [type PackageStatistics](#type-packagestatistics) + 1. [type UserPackage](#type-userpackage) + 1. [Client](#client) + 1. [type APIClient](#type-apiclient) + 1. [Constructors](#constructors) + 1. [func NewAPIClient](#func--newapiclient) + 1. [func NewCustomClient](#func--newcustomclient) + 1. [Methods](#methods) + 1. [func (*APIClient) AddAuthIP](#func-apiclient-addauthip) + 1. [func (*APIClient) AddCurrentIPtoAllPackages](#func-apiclient-addcurrentiptoallpackages) + 1. [func (*APIClient) DeleteAllAuthIPs](#func-apiclient-deleteallauthips) + 1. [func (*APIClient) DeleteAuthIPByID](#func-apiclient-deleteauthipbyid) + 1. [func (*APIClient) DeleteAuthIPByIP](#func-apiclient-deleteauthipbyip) + 1. [func (*APIClient) DeleteOtherAuthIPs](#func-apiclient-deleteotherauthips) + 1. [func (*APIClient) GetAllSOCKSIPsAndPorts](#func-apiclient-getallsocksipsandports) + 1. [func (*APIClient) GetAuthIPs](#func-apiclient-getauthips) + 1. [func (*APIClient) GetPackageSOCKS](#func-apiclient-getpackagesocks) + 1. [func (*APIClient) GetProxyPackages](#func-apiclient-getproxypackages) + +## Import Path + `import "git.tcp.direct/kayos/proxygonanza"` -## Documentation - -1. [Getting Started](#getting-started) - 1. [type APIClient](#type-apiclient) - 1. [func NewAPIClient](#func--newapiclient) - 1. [func NewCustomClient](#func--newcustomclient) -2. [Functions](#Functions) - 1. [func (*APIClient) AddAuthIP](#func-apiclient-addauthip) - 1. [func (*APIClient) AddCurrentIPtoAllPackages](#func-apiclient-addcurrentiptoallpackages) - 1. [func (*APIClient) DeleteAllAuthIPs](#func-apiclient-deleteallauthips) - 1. [func (*APIClient) DeleteAuthIPByID](#func-apiclient-deleteauthipbyid) - 1. [func (*APIClient) DeleteAuthIPByIP](#func-apiclient-deleteauthipbyip) - 1. [func (*APIClient) GetAllSOCKSIPsAndPorts](#func-apiclient-getallsocksipsandports) - 1. [func (*APIClient) GetAuthIPs](#func-apiclient-getauthips) - 1. [func (*APIClient) GetPackageSOCKS](#func-apiclient-getpackagesocks) - 1. [func (*APIClient) GetProxyPackages](#func-apiclient-getproxypackages) -3. [Additional Details](https://godoc.org/git.tcp.direct/kayos/proxygonanza) ---- +## Constants and Variables ```go const ( @@ -29,7 +46,158 @@ const ( ) ``` -### Getting Started +## API Types + +### type AddAuthIPResponse + +```go +type AddAuthIPResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + Data struct { + ID int `json:"id"` + } `json:"data"` +} +``` + +AddAuthIPResponse represents an API response from proxybonanza.com. + +### type AuthIP + +```go +type AuthIP struct { + UserpackageID int `json:"userpackage_id"` + ID interface{} `json:"id"` + IP string `json:"ip"` +} +``` + +AuthIP is an IP address authorized to use the proxies in the related package. + +### type AuthIPResponse + +```go +type AuthIPResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + AuthIPData []AuthIP `json:"data"` + Pages pagination `json:"pagination"` +} +``` + +AuthIPResponse represents an API response from proxybonanza.com. + +### type DelAuthIPResponse + +```go +type DelAuthIPResponse struct { + Success bool `json:"success"` +} +``` + +DelAuthIPResponse represents an API response from proxybonanza.com. + +### type Package + +```go +type Package struct { + ID int + AuthIPs []AuthIP + AllTimeStats PackageStatistics + HourlyStats map[time.Time]PackageStatistics +} +``` + +Package contains what we know about a particular proxybonanza package. + +### type PackageDetails + +```go +type PackageDetails struct { + Name string `json:"name"` + Bandwidth int64 `json:"bandwidth"` + Price interface{} `json:"price"` + HowmanyIPs int `json:"howmany_ips"` + PricePerGig interface{} `json:"price_per_gig"` + PackageType string `json:"package_type"` + HowmanyAuthips int `json:"howmany_authips"` + IPType int `json:"ip_type"` + PriceUserFormatted string `json:"price_user_formatted"` +} +``` + +PackageDetails represents an API response from proxybonanza.com containing proxy +package information. + +### type PackageResponse + +```go +type PackageResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + PackageData []UserPackage `json:"data"` + Pages pagination `json:"pagination"` +} +``` + +PackageResponse represents an API response from proxybonanza.com containing +proxy package information. + +### type PackageStatistics + +```go +type PackageStatistics struct { + UserpackageID int `json:"userpackage_id"` + Date string `json:"date"` + BndHTTP int `json:"bnd_http"` + ConnHTTP int `json:"conn_http"` + BndSocks int `json:"bnd_socks"` + ConnSocks int `json:"conn_socks"` + BndTotal int `json:"bnd_total"` + ConnTotal int `json:"conn_total"` +} +``` + +PackageStatistics represents the statistics for the related proxy package. + +### type UserPackage + +```go +type UserPackage struct { + ID int `json:"id"` + CustomName interface{} `json:"custom_name"` + Login string `json:"login"` + Password string `json:"password"` + Expires time.Time `json:"expires"` + Bandwidth int64 `json:"bandwidth"` + + LowBanwidthNotificationPercent int `json:"low_banwidth_notification_percent"` + Package PackageDetails `json:"package"` + BandwidthGb float64 `json:"bandwidth_gb"` + AdditionalBandwidthGb float64 `json:"additional_bandwidth_gb"` + BandwidthPercentLeftHuman string `json:"bandwidth_percent_left_human"` + ExpirationDateHuman string `json:"expiration_date_human"` + Name string `json:"name"` +} +``` + +UserPackage represents a proxy package purchased from proxybonanza.com. + +## Client + +### type APIClient + +```go +type APIClient struct { + Key string + KnownPackages map[int]PackageDetails + Debug bool +} +``` + +APIClient is a client for ProxyBonanza.com. + +### Constructors #### func NewAPIClient @@ -47,9 +215,7 @@ func NewCustomClient(key string, client *http.Client) *APIClient NewCustomClient insantiates a proxybonanza API client with the given key and the given http.Client. ---- - -### Functions +### Methods #### func (*APIClient) AddAuthIP @@ -64,7 +230,8 @@ AddAuthIP adds a new IP to the corresponding/provided proxy package ID. func (api *APIClient) AddCurrentIPtoAllPackages() (success int) ``` AddCurrentIPtoAllPackages adds your current WAN IP to all packages on your -account. It returns the amount of successful packages that it was applied to. +account. It returns the amount of successful packages that it was applied to. It +will skip packages that are already using the current IP. #### func (*APIClient) DeleteAllAuthIPs @@ -88,6 +255,15 @@ func (api *APIClient) DeleteAuthIPByIP(ipa net.IP) (err error) DeleteAuthIPByIP will iterate through all the authips on your account and delete one that matches the given IP. +#### func (*APIClient) DeleteOtherAuthIPs + +```go +func (api *APIClient) DeleteOtherAuthIPs() ([]int, error) +``` +DeleteOtherAuthIPs deletes all authenticaiton IPs from your account's packages +that do not match your current IP address. Returns a slice of authentication IP +IDs that were deleted and any errors that occurred. + #### func (*APIClient) GetAllSOCKSIPsAndPorts ```go diff --git a/actions.go b/actions.go index 5426e9a..4365b28 100644 --- a/actions.go +++ b/actions.go @@ -102,14 +102,15 @@ func (api *APIClient) GetProxyPackages() ([]UserPackage, error) { func (api *APIClient) GetAuthIPs() ([]AuthIP, error) { body, err := api.getReq(authips) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get auth IPs: %w", err) } var auths AuthIPResponse err = json.Unmarshal(body, &auths) if err != nil { - return nil, err + print(string(body)) + return nil, fmt.Errorf("failed to unmarshal auth ips: %w", err) } if !auths.Success { @@ -142,6 +143,36 @@ func (api *APIClient) DeleteAllAuthIPs() (int, error) { return done, err } +// DeleteOtherAuthIPs deletes all authenticaiton IPs from your account's packages that do not match your current IP address. +// Returns a slice of authentication IP IDs that were deleted and any errors that occurred. +func (api *APIClient) DeleteOtherAuthIPs() ([]int, error) { + myip, err := getMyIP() + if err != nil { + return []int{}, fmt.Errorf("failed to get my IP: %w", err) + } + aips, err := api.GetAuthIPs() + if err != nil { + return []int{}, fmt.Errorf("failed to get auth IPs: %w", err) + } + todo := len(aips) + done := 0 + var deleted []int + for _, aip := range aips { + if net.ParseIP(aip.IP).Equal(myip) { + todo-- + continue + } + if api.DeleteAuthIPByID(int(aip.ID.(float64))) { + done++ + deleted = append(deleted, int(aip.ID.(float64))) + } + } + if done < todo { + err = errors.New("failed to delete some IPs") + } + return deleted, err +} + // AddAuthIP adds a new IP to the corresponding/provided proxy package ID. func (api *APIClient) AddAuthIP(ip net.IP, packageID int) (AddAuthIPResponse, error) { failadd := AddAuthIPResponse{Success: false} @@ -178,7 +209,11 @@ func (api *APIClient) DeleteAuthIPByIP(ipa net.IP) (err error) { if ipa.IsPrivate() || ipa.IsUnspecified() || ipa.IsLoopback() { return errors.New("IP is invalid") } - aips, err := api.GetAuthIPs() + var aips []AuthIP + aips, err = api.GetAuthIPs() + if err != nil { + return + } for _, aip := range aips { if net.ParseIP(aip.IP).Equal(ipa) { target := int(aip.ID.(float64)) @@ -192,16 +227,38 @@ func (api *APIClient) DeleteAuthIPByIP(ipa net.IP) (err error) { // AddCurrentIPtoAllPackages adds your current WAN IP to all packages on your account. // It returns the amount of successful packages that it was applied to. +// It will skip packages that are already using the current IP. func (api *APIClient) AddCurrentIPtoAllPackages() (success int) { - packs, err := api.GetProxyPackages() - if err != nil { - fmt.Println(err) + myip, myIPErr := getMyIP() + if myIPErr != nil { + // TODO: probably don't handle any of these errors like this - avoiding breaking changes for now. + println("ProxyGonanza failed to retrieve own IP: " + myIPErr.Error()) return } - - myip := getMyIP() - for _, p := range packs { - _, err := api.AddAuthIP(myip, p.ID) + aips, aipErr := api.GetAuthIPs() + if aipErr != nil { + println("ProxyGonanza failed to get auth IPs: " + aipErr.Error()) + return + } + packs, packErr := api.GetProxyPackages() + if packErr != nil { + println("ProxyGonanza failed to get proxy packages: " + packErr.Error()) + return 0 + } + var skips []int + for _, aip := range aips { + aipIP := net.ParseIP(aip.IP) + if aipIP.Equal(myip) { + skips = append(skips, aip.UserpackageID) + } + } + for _, pack := range packs { + for _, skip := range skips { + if pack.ID == skip { + continue + } + } + _, err := api.AddAuthIP(myip, pack.ID) if err == nil { success++ } diff --git a/api.go b/api.go index 285dc54..a00af63 100644 --- a/api.go +++ b/api.go @@ -88,7 +88,7 @@ type UserPackage struct { Expires time.Time `json:"expires"` Bandwidth int64 `json:"bandwidth"` - // FIXME: + // TODO: // See https://github.com/yunginnanet/ProxyGonanza/issues/1 // LastIPChange time.Time `json:"last_ip_change"` diff --git a/cmd/README.md b/cmd/README.md new file mode 100644 index 0000000..ff293ab --- /dev/null +++ b/cmd/README.md @@ -0,0 +1 @@ +##### This folder is intended to be used as contains example implementations of the parent library. diff --git a/cmd/authips/addip.go b/cmd/authips/addip.go new file mode 100644 index 0000000..b10f59d --- /dev/null +++ b/cmd/authips/addip.go @@ -0,0 +1,49 @@ +package main + +import ( + "errors" + "fmt" + "os" + + "git.tcp.direct/kayos/proxygonanza" + "git.tcp.direct/kayos/proxygonanza/internal" +) + +func init() { + internal.ParseArgs() +} + +var ErrNoNonMatchingIPs = errors.New("no non-matching IPs found or deleted") + +func purge(c *proxygonanza.APIClient) error { + println("clearing all auth IPs that do not match current IP...") + deleted, err := c.DeleteOtherAuthIPs() + if err != nil { + return fmt.Errorf("failed to delete other auth IPs: %w", err) + } + if len(deleted) == 0 { + return ErrNoNonMatchingIPs + } + println("deleted other auth IP IDs:") + for _, del := range deleted { + fmt.Println(del) + } + return nil +} + +func main() { + c := proxygonanza.NewAPIClient(internal.APIKey) + if internal.Purge { + if err := purge(c); err != nil && !errors.Is(err, ErrNoNonMatchingIPs) { + println(err.Error()) + os.Exit(1) + } + } + println("adding current IP to all packages...") + count := c.AddCurrentIPtoAllPackages() + if count == 0 { + println("all authentication IPs are already set") + os.Exit(0) + } + fmt.Printf("successfully added your external IP to %d packages\n", count) +} diff --git a/example/cmd/getpackages/main.go b/cmd/getpackages/main.go similarity index 85% rename from example/cmd/getpackages/main.go rename to cmd/getpackages/main.go index 408caa1..c533a89 100644 --- a/example/cmd/getpackages/main.go +++ b/cmd/getpackages/main.go @@ -5,15 +5,15 @@ import ( "fmt" "git.tcp.direct/kayos/proxygonanza" - "git.tcp.direct/kayos/proxygonanza/example" + "git.tcp.direct/kayos/proxygonanza/internal" ) func init() { - example.ParseArgs() + internal.ParseArgs() } func main() { - c := proxygonanza.NewAPIClient(example.APIKey) + c := proxygonanza.NewAPIClient(internal.APIKey) println("getting proxy packages...") packs, err := c.GetProxyPackages() diff --git a/example/cmd/getsocks/main.go b/cmd/getsocks/main.go similarity index 60% rename from example/cmd/getsocks/main.go rename to cmd/getsocks/main.go index 40ba474..cfae8be 100644 --- a/example/cmd/getsocks/main.go +++ b/cmd/getsocks/main.go @@ -1,19 +1,20 @@ package main import ( + "fmt" "os" "git.tcp.direct/kayos/proxygonanza" - "git.tcp.direct/kayos/proxygonanza/example" + "git.tcp.direct/kayos/proxygonanza/internal" ) func init() { - example.ParseArgs() + internal.ParseArgs() } func main() { - c := proxygonanza.NewAPIClient(example.APIKey) - if example.Debug { + c := proxygonanza.NewAPIClient(internal.APIKey) + if internal.Debug { c.Debug = true println("debug enabled") } @@ -23,6 +24,6 @@ func main() { os.Exit(1) } for _, line := range socks { - println(line) + fmt.Fprint(os.Stdout, line+"\n") } } diff --git a/example/cmd/authips/addip.go b/example/cmd/authips/addip.go deleted file mode 100644 index ab8df39..0000000 --- a/example/cmd/authips/addip.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "fmt" - "git.tcp.direct/kayos/proxygonanza" - "git.tcp.direct/kayos/proxygonanza/example" - "strconv" -) - -func init() { - example.ParseArgs() -} - -func purge(c *proxygonanza.APIClient) { - println("clearing all auth IPs...") - - deleted, err := c.DeleteAllAuthIPs() - if err != nil { - println(err.Error()) - return - } - - println("deleted " + strconv.Itoa(deleted) + " IPs successfully") -} - -func main() { - c := proxygonanza.NewAPIClient(example.APIKey) - - if example.Purge { - purge(c) - } - - println("adding current IP to all packages...") - count := c.AddCurrentIPtoAllPackages() - if count == 0 { - println("failed!") - return - } - - fmt.Printf("successfully added your external IP to %d packages\n", count) -} diff --git a/example/cmd/resetauthips/main.go b/example/cmd/resetauthips/main.go deleted file mode 100644 index a7b77f2..0000000 --- a/example/cmd/resetauthips/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "fmt" - "strconv" - - "git.tcp.direct/kayos/proxygonanza" - "git.tcp.direct/kayos/proxygonanza/example" -) - -func init() { - example.ParseArgs() -} - -func main() { - println("clearing all auth IPs...") - - c := proxygonanza.NewAPIClient(example.APIKey) - deleted, err := c.DeleteAllAuthIPs() - if err != nil { - println(err.Error()) - return - } - - println("deleted " + strconv.Itoa(deleted) + " IPs successfully") - - println("adding current IP to all packages...") - count := c.AddCurrentIPtoAllPackages() - if count == 0 { - println("failed!") - return - } - - fmt.Printf("successfully added your external IP to %d packages\n", count) -} diff --git a/example/common.go b/internal/common.go similarity index 56% rename from example/common.go rename to internal/common.go index 20296c9..8911cff 100644 --- a/example/common.go +++ b/internal/common.go @@ -1,7 +1,8 @@ -package example +package internal import ( "fmt" + "net/http" "os" ) @@ -16,7 +17,10 @@ func ParseArgs() { fmt.Printf("\t ~*~ ProxyGonanza ~*~ \nhttps://git.tcp.direct/kayos/proxygonanza\n\nFatal: missing API Key \n\nUsage: %s [--verbose|-v] ''\n\n", os.Args[0]) os.Exit(1) } - for _, arg := range os.Args { + for i, arg := range os.Args { + if i == 0 { + continue + } switch arg { case "-d", "--debug", "-v", "--verbose": Debug = true @@ -28,3 +32,12 @@ func ParseArgs() { } } + +// CloseBody is crude error handling for any potential errors closing the response body. +func CloseBody(res *http.Response) { + err := res.Body.Close() + if err != nil { + println("WARN: ProxyGonanza failed to close body for request to ", + res.Request.RequestURI+": "+err.Error()) + } +} diff --git a/requests.go b/requests.go index 9b744ae..7417d87 100644 --- a/requests.go +++ b/requests.go @@ -1,12 +1,14 @@ package proxygonanza import ( - "fmt" "io/ioutil" "net" "net/http" "net/url" + "os" "strings" + + "git.tcp.direct/kayos/proxygonanza/internal" ) func (api *APIClient) newRequest(method, u string) (r *http.Request) { @@ -37,11 +39,11 @@ func (api *APIClient) postReq(endpoint string, post map[string]string) ([]byte, } enc := params.Encode() req, err := http.NewRequest("POST", APIBaseURL+endpoint, strings.NewReader(enc)) - req.Header.Add("accept", "application/json") - req.Header.Add("Authorization", api.Key) if err != nil { return nil, err } + req.Header.Add("accept", "application/json") + req.Header.Add("Authorization", api.Key) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") @@ -80,8 +82,7 @@ func (api *APIClient) deleteReq(endpoint string) ([]byte, error) { } func processBody(res *http.Response) ([]byte, error) { - defer res.Body.Close() - + defer internal.CloseBody(res) body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err @@ -89,16 +90,23 @@ func processBody(res *http.Response) ([]byte, error) { return body, nil } -func getMyIP() net.IP { - res, err := http.DefaultClient.Get("https://wtfismyip.com/text") - if err != nil { - fmt.Println(err) - return net.IP{} +func getMyIP() (final net.IP, err error) { + endpoint := "https://wtfismyip.com/text" + envopt := os.Getenv("PROXYBONANZA_GETIP") + if envopt != "" { + endpoint = envopt } - body, err := ioutil.ReadAll(res.Body) + var res *http.Response + res, err = http.DefaultClient.Get(endpoint) + defer internal.CloseBody(res) if err != nil { - fmt.Println(err) - return net.IP{} + return } - return net.ParseIP(strings.TrimSpace(string(body))) + var body []byte + body, err = ioutil.ReadAll(res.Body) + if err != nil { + return + } + final = net.ParseIP(strings.TrimSpace(string(body))) + return }