Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
8b80a73452 | |||
20366ed3e4 | |||
1c52aae429 | |||
c0bd927903 |
10
endpoints.go
10
endpoints.go
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func stats(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
data, err := statsResponse(w, ps, nil)
|
||||
data, err := statsResponse(w, r, ps, nil)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
@ -25,7 +25,7 @@ func stats(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
}
|
||||
|
||||
func profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
cacheKey := generateCacheKey(ps) + "-profile"
|
||||
cacheKey := generateCacheKey(r, ps) + "-profile"
|
||||
|
||||
res, err := cacheProvider.Get(cacheKey)
|
||||
|
||||
@ -36,7 +36,7 @@ func profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
}
|
||||
|
||||
// Cache result for profile specifically
|
||||
data, err := statsResponse(w, ps, profilePatch)
|
||||
data, err := statsResponse(w, r, ps, profilePatch)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
@ -63,7 +63,7 @@ func heroes(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
|
||||
sort.Strings(names)
|
||||
|
||||
cacheKey := generateCacheKey(ps) + "-heroes-" + hex.EncodeToString(md5.New().Sum([]byte(strings.Join(names, ","))))
|
||||
cacheKey := generateCacheKey(r, ps) + "-heroes-" + hex.EncodeToString(md5.New().Sum([]byte(strings.Join(names, ","))))
|
||||
|
||||
res, err := cacheProvider.Get(cacheKey)
|
||||
|
||||
@ -108,7 +108,7 @@ func heroes(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
}
|
||||
|
||||
// Create a patch to remove all but specified heroes
|
||||
data, err := statsResponse(w, ps, patch)
|
||||
data, err := statsResponse(w, r, ps, patch)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
|
11
go.mod
11
go.mod
@ -4,19 +4,12 @@ go 1.12
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.5.1-0.20190109230704-3dcf72e6c17f
|
||||
github.com/andybalholm/cascadia v1.1.0 // indirect
|
||||
github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||
github.com/go-redis/redis v6.15.6+incompatible
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.26
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/rs/cors v1.7.0
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
s32x.com/ovrstat v0.0.0-20191125185405-f48c0d280dc1
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
|
||||
s32x.com/ovrstat v0.0.0-20200131231416-4cb42edd331d
|
||||
)
|
||||
|
33
go.sum
33
go.sum
@ -29,11 +29,24 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.1.7-0.20190725203903-8cabd1e123d6 h1:ImU76UGnZN9+25dYuM+4uMhP0TFf/nT61rLy1RPPKUM=
|
||||
github.com/labstack/echo/v4 v4.1.7-0.20190725203903-8cabd1e123d6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
|
||||
github.com/labstack/echo/v4 v4.1.14 h1:h8XP66UfB3tUm+L3QPw7tmwAu3pJaA/nyfHPCcz46ic=
|
||||
github.com/labstack/echo/v4 v4.1.14/go.mod h1:Q5KZ1vD3V5FEzjM79hjwVrC3ABr7F5IdM23bXQMRDGg=
|
||||
github.com/labstack/gommon v0.2.9 h1:heVeuAYtevIQVYkGj6A41dtfT91LrvFG220lavpWhrU=
|
||||
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
|
||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -43,20 +56,30 @@ github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/seilem/ovrstat v0.0.0-20200123200456-7b7e24d39506 h1:xAdnQYUFimq8Uiz4Io7QlnNGV+4BtagWGC+DDxVG3Fo=
|
||||
github.com/seilem/ovrstat v0.0.0-20200123200456-7b7e24d39506/go.mod h1:UzsLSEoY8B4FByz4e+5GGKebJio+H+axo3RxLr7d7Mw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||
@ -69,6 +92,8 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -81,9 +106,15 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
@ -106,3 +137,5 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
s32x.com/ovrstat v0.0.0-20191125185405-f48c0d280dc1 h1:q5zjrLdxx3oo3i49aBKMMdppFoLCEEq6cSD8lWNCoAY=
|
||||
s32x.com/ovrstat v0.0.0-20191125185405-f48c0d280dc1/go.mod h1:UzsLSEoY8B4FByz4e+5GGKebJio+H+axo3RxLr7d7Mw=
|
||||
s32x.com/ovrstat v0.0.0-20200131231416-4cb42edd331d h1:G3p3gNYpF4WsklRFb4U2FWgXhicc2RDWgL+zNc//JWc=
|
||||
s32x.com/ovrstat v0.0.0-20200131231416-4cb42edd331d/go.mod h1:UzsLSEoY8B4FByz4e+5GGKebJio+H+axo3RxLr7d7Mw=
|
||||
|
81
main.go
81
main.go
@ -4,10 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.meow.tf/ow-api/ow-api/cache"
|
||||
"git.meow.tf/ow-api/ow-api/json-patch"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/rs/cors"
|
||||
"golang.org/x/net/context"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@ -17,12 +19,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "2.3.1"
|
||||
Version = "2.3.5"
|
||||
|
||||
OpAdd = "add"
|
||||
OpRemove = "remove"
|
||||
)
|
||||
|
||||
type ApiVersion int
|
||||
|
||||
const (
|
||||
VersionOne ApiVersion = iota
|
||||
VersionTwo
|
||||
)
|
||||
|
||||
type gamesStats struct {
|
||||
Played int64 `json:"played"`
|
||||
Won int64 `json:"won"`
|
||||
@ -145,11 +154,30 @@ func loadHeroNames() {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
versionRegexp = regexp.MustCompile("^/(v\\d+)/")
|
||||
)
|
||||
|
||||
func injectPlatform(platform string, handler httprouter.Handle) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
ps = append(ps, httprouter.Param{Key: "platform", Value: platform})
|
||||
|
||||
handler(w, r, ps)
|
||||
ctx := context.Background()
|
||||
|
||||
m := versionRegexp.FindStringSubmatch(r.RequestURI)
|
||||
|
||||
if m != nil {
|
||||
version := VersionOne
|
||||
|
||||
switch m[1] {
|
||||
case "v2":
|
||||
version = VersionTwo
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, "version", version)
|
||||
}
|
||||
|
||||
handler(w, r.WithContext(ctx), ps)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,15 +185,21 @@ var (
|
||||
tagRegexp = regexp.MustCompile("-(\\d+)$")
|
||||
)
|
||||
|
||||
func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch.Patch) ([]byte, error) {
|
||||
func statsResponse(w http.ResponseWriter, r *http.Request, ps httprouter.Params, patch *jsonpatch.Patch) ([]byte, error) {
|
||||
var stats *ovrstat.PlayerStats
|
||||
var err error
|
||||
|
||||
version := VersionOne
|
||||
|
||||
if v := r.Context().Value("version"); v != nil {
|
||||
version = v.(ApiVersion)
|
||||
}
|
||||
|
||||
tag := ps.ByName("tag")
|
||||
|
||||
tag = strings.Replace(tag, "#", "-", -1)
|
||||
|
||||
cacheKey := generateCacheKey(ps)
|
||||
cacheKey := generateCacheKey(r, ps)
|
||||
|
||||
platform := ps.ByName("platform")
|
||||
|
||||
@ -267,6 +301,31 @@ func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch
|
||||
urlBase := iconUrl[0 : strings.Index(iconUrl, "rank-icons/")+11]
|
||||
|
||||
ratingIcon = urlBase + iconFor(rating)
|
||||
|
||||
if version == VersionTwo {
|
||||
m := make(map[string]ovrstat.Rating)
|
||||
|
||||
ratingsPatches := make([]patchOperation, len(stats.Ratings))
|
||||
|
||||
for i, rating := range stats.Ratings {
|
||||
m[rating.Role] = rating
|
||||
ratingsPatches[i] = patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/ratings/" + rating.Role + "/role",
|
||||
}
|
||||
}
|
||||
|
||||
extra = append(extra, patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/ratings",
|
||||
}, patchOperation{
|
||||
Op: OpAdd,
|
||||
Path: "/ratings",
|
||||
Value: m,
|
||||
})
|
||||
|
||||
extra = append(extra, ratingsPatches...)
|
||||
}
|
||||
}
|
||||
|
||||
extra = append(extra, patchOperation{
|
||||
@ -330,6 +389,16 @@ func iconFor(rating int) string {
|
||||
return "rank-BronzeTier.png"
|
||||
}
|
||||
|
||||
func generateCacheKey(ps httprouter.Params) string {
|
||||
return ps.ByName("platform") + "-" + ps.ByName("tag")
|
||||
func generateCacheKey(r *http.Request, ps httprouter.Params) string {
|
||||
version := VersionOne
|
||||
|
||||
if v := r.Context().Value("version"); v != nil {
|
||||
version = v.(ApiVersion)
|
||||
}
|
||||
|
||||
return versionToString(version) + "-" + ps.ByName("platform") + "-" + ps.ByName("tag")
|
||||
}
|
||||
|
||||
func versionToString(version ApiVersion) string {
|
||||
return fmt.Sprintf("v%d", version)
|
||||
}
|
||||
|
268
pro/main.go
Normal file
268
pro/main.go
Normal file
@ -0,0 +1,268 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/miekg/dns"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"s32x.com/ovrstat/ovrstat"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cloudfrontUrl, err := lookupCloudfrontURL()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("Unable to find url:", err)
|
||||
}
|
||||
|
||||
log.Println("URL:", cloudfrontUrl)
|
||||
|
||||
ips, err := lookupIpv6Dns(cloudfrontUrl)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("Unable to find ips:", err)
|
||||
}
|
||||
|
||||
log.Println("IPs:", ips)
|
||||
|
||||
localIps, err := checkIpv6Local()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("Unable to get local ips")
|
||||
}
|
||||
|
||||
log.Println("Local IPs:", localIps)
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
localAddr, err := net.ResolveIPAddr("ip", "")
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// You also need to do this to make it work and not give you a
|
||||
// "mismatched local address type ip"
|
||||
// This will make the ResolveIPAddr a TCPAddr without needing to
|
||||
// say what SRC port number to use.
|
||||
localTCPAddr := net.TCPAddr{
|
||||
IP: localAddr.IP,
|
||||
}
|
||||
|
||||
defaultDialer := &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: &localTCPAddr,
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
oldNetwork := network
|
||||
oldAddr := addr
|
||||
|
||||
if addr == "playoverwatch.com:443" {
|
||||
network = "tcp6"
|
||||
addr = "[" + ips[r.Intn(len(ips))] + "]:443"
|
||||
}
|
||||
|
||||
log.Println("Dial using", addr)
|
||||
|
||||
c, err := dialer.DialContext(ctx, network, addr)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Fallback")
|
||||
c, err = defaultDialer.DialContext(ctx, oldNetwork, oldAddr)
|
||||
}
|
||||
|
||||
return c, err
|
||||
},
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: "playoverwatch.com",
|
||||
},
|
||||
}
|
||||
|
||||
http.DefaultClient = &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
stats, err := ovrstat.PCStats("cats-11481")
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("Error retrieving:", err)
|
||||
}
|
||||
|
||||
log.Println(stats.Name+" is level", stats.Level)
|
||||
}
|
||||
|
||||
func lookupCloudfrontURL() (string, error) {
|
||||
res, err := http.Get("https://playoverwatch.com/en-us/")
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
|
||||
links := doc.Find("link").Map(func(i int, s *goquery.Selection) string {
|
||||
v, exists := s.Attr("href")
|
||||
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return v
|
||||
})
|
||||
|
||||
for _, link := range links {
|
||||
u, err := url.Parse(link)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(u.Host, "cloudfront.net") {
|
||||
return u.Host, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("no cloudfront url")
|
||||
}
|
||||
|
||||
func lookupIpv6Dns(host string) ([]string, error) {
|
||||
c := &dns.Client{
|
||||
Net: "udp",
|
||||
}
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.Id = dns.Id()
|
||||
msg.RecursionDesired = true
|
||||
msg.Question = make([]dns.Question, 1)
|
||||
msg.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}
|
||||
|
||||
in, _, err := c.Exchange(msg, "8.8.8.8:53")
|
||||
|
||||
result := make([]string, 0)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in != nil && in.Rcode != dns.RcodeSuccess {
|
||||
return result, errors.New(dns.RcodeToString[in.Rcode])
|
||||
}
|
||||
|
||||
for _, record := range in.Answer {
|
||||
if t, ok := record.(*dns.AAAA); ok {
|
||||
result = append(result, t.AAAA.String())
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func checkIpv6Local() ([]string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]string, 0)
|
||||
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
log.Println("Check", ip.String())
|
||||
|
||||
if ip == nil || ip.To4() == nil || isPrivateIP(ip) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("Checking", ip.String())
|
||||
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
for ip := ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
|
||||
ret = append(ret, ip.String())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func inc(ip net.IP) {
|
||||
for j := len(ip) - 1; j >= 0; j-- {
|
||||
ip[j]++
|
||||
if ip[j] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var privateIPBlocks []*net.IPNet
|
||||
|
||||
func init() {
|
||||
for _, cidr := range []string{
|
||||
"127.0.0.0/8", // IPv4 loopback
|
||||
"10.0.0.0/8", // RFC1918
|
||||
"172.16.0.0/12", // RFC1918
|
||||
"192.168.0.0/16", // RFC1918
|
||||
"169.254.0.0/16", // RFC3927 link-local
|
||||
"::1/128", // IPv6 loopback
|
||||
"fe80::/10", // IPv6 link-local
|
||||
"fc00::/7", // IPv6 unique local addr
|
||||
} {
|
||||
_, block, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("parse error on %q: %v", cidr, err))
|
||||
}
|
||||
privateIPBlocks = append(privateIPBlocks, block)
|
||||
}
|
||||
}
|
||||
|
||||
func isPrivateIP(ip net.IP) bool {
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, block := range privateIPBlocks {
|
||||
if block.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -7,12 +7,13 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "https://playoverwatch.com/en-us/career"
|
||||
|
||||
apiURL = "https://playoverwatch.com/en-us/career/platforms/"
|
||||
apiURL = "https://playoverwatch.com/en-us/search/account-by-name/"
|
||||
)
|
||||
|
||||
type Platform struct {
|
||||
@ -86,20 +87,12 @@ func ValidateEndpoint() error {
|
||||
return err
|
||||
}
|
||||
|
||||
code, err := pd.Html()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
split := careerInitRegexp.FindStringSubmatch(code)
|
||||
|
||||
if split == nil || len(split) < 2 {
|
||||
return errNoCareerInit
|
||||
}
|
||||
|
||||
// Validate API response
|
||||
if err := validateApi(split[1]); err != nil {
|
||||
if err := validateApi(strings.Replace(url[strings.LastIndex(url, "/")+1:], "-", "%23", -1)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -154,13 +147,13 @@ func validateCareerStats(careerStatsSelector *goquery.Selection, parent string)
|
||||
}
|
||||
|
||||
selectors := []string{
|
||||
"div.row div.js-stats", // Top level
|
||||
"div.row div.js-stats div.column.xs-12", // stat boxes
|
||||
"div.row div.js-stats div.column.xs-12 .stat-title", // stat boxes
|
||||
"div.row div.js-stats div.column.xs-12 table.DataTable", // data table
|
||||
"div.row div.js-stats div.column.xs-12 table.DataTable tbody", // data table tbody
|
||||
"div.row div.js-stats div.column.xs-12 table.DataTable tbody tr", // data table tbody tr
|
||||
"div.row div.js-stats div.column.xs-12 table.DataTable tbody tr td", // data table tbody tr td
|
||||
"div.row div.js-stats", // Top level
|
||||
"div.row div.js-stats div.column", // stat boxes
|
||||
"div.row div.js-stats div.column .stat-title", // stat boxes
|
||||
"div.row div.js-stats div.column table.DataTable", // data table
|
||||
"div.row div.js-stats div.column table.DataTable tbody", // data table tbody
|
||||
"div.row div.js-stats div.column table.DataTable tbody tr", // data table tbody tr
|
||||
"div.row div.js-stats div.column table.DataTable tbody tr td", // data table tbody tr td
|
||||
}
|
||||
|
||||
return validateElementsExist(careerStatsSelector, parent, selectors...)
|
||||
|
Reference in New Issue
Block a user