35 Commits

Author SHA1 Message Date
00067dd390 Update ovrstat 2024-01-14 05:18:19 -05:00
b54c86ea6c Remove unnecessary values from jsonpatch 2022-12-16 22:53:11 -05:00
73995ba91e Fix stats by replacing dashes with # 2022-12-13 22:27:41 -05:00
bbd839da44 Fix hero loading 2022-12-13 22:22:34 -05:00
8577dcea56 Remove release to push a docker image 2022-12-13 22:16:24 -05:00
f472f363b6 Update to OW2 2022-12-13 22:11:33 -05:00
6613cfe23e Use ovrstat with season marker 2022-08-20 22:07:07 -04:00
ac872c1681 Only build on push, not release 2021-04-28 22:40:45 -04:00
e31652d481 Update goc output path 2021-04-28 22:37:34 -04:00
42a82d3e97 Fix binary path 2021-04-28 22:36:35 -04:00
f345fe3b2c Update build system 2021-04-28 22:35:10 -04:00
8b8b818651 Proper compression identifier 2021-04-20 22:54:22 -04:00
06740b6b86 Change to gzip compression 2021-04-20 22:37:38 -04:00
6d5c00653b Update ovrstat 2021-04-20 22:35:14 -04:00
ead6298a55 Update ovrstat 2020-10-22 23:52:27 -04:00
454419abec Add license file 2020-10-03 16:46:22 -04:00
b53357aad7 Resolve issue with PSN names 2020-10-03 16:42:21 -04:00
fee92259b1 Add distro to build repo stage, move docker up 2020-08-16 18:35:54 -04:00
c964c73824 Increment version 2020-08-16 18:31:45 -04:00
c2882857a4 Build docker image 2020-08-16 18:25:12 -04:00
f207f57536 Patch issue with v3 not being recognized 2020-06-15 20:48:47 -04:00
30fbf1b611 Load hero names from heroes page 2020-06-15 20:40:59 -04:00
70900ea2c7 Move keyed items to v3 2020-06-15 20:32:07 -04:00
322e908147 Force new version 2020-06-14 05:42:18 -04:00
308f853b81 Push to repo first, don't upload in build step 2020-06-14 05:19:17 -04:00
d5de8568dd Force rebuild with right arch flag 2020-06-14 05:15:10 -04:00
8b80a73452 Add keyed ratings 2020-06-14 02:12:26 -04:00
20366ed3e4 Update ovrstat and validator 2020-04-16 20:29:23 -04:00
1c52aae429 Update ovrstat 2020-01-31 18:17:06 -05:00
c0bd927903 Use fork for ovrstat until updated 2020-01-23 21:52:09 -05:00
3ca4448a7e Fix accidentally removed region parameter 2019-12-24 12:17:40 -05:00
8c42945e67 Support for switch, v2 of api without region 2019-12-23 21:47:48 -05:00
c15121a873 Update ovrstat 2019-09-25 22:05:49 -04:00
f954619223 Fix Platinum missing extension 2019-08-25 18:38:38 -04:00
d010926c07 Resolve issue with map not observing ordering 2019-08-25 14:35:33 -04:00
14 changed files with 641 additions and 283 deletions

View File

@ -3,48 +3,56 @@ name: default
steps:
- name: build
image: golang:latest
image: tystuyfzand/goc
group: build
volumes:
- name: build
path: /build
commands:
- mkdir -p /build/i386 /build/amd64 /build/armv7 /build/arm64
- GOOS=linux GOARCH=386 go build -o /build/i386/owapi_i386
- go build -o /build/amd64/owapi_amd64
- GOOS=linux GOARCH=arm GOARM=7 go build -o /build/armv7/owapi_armv7
- GOOS=linux GOARCH=arm64 go build -o /build/arm64/owapi_arm64
- GOOS=linux,windows GOARCH=386,arm,arm64,amd64 goc -o /build/owapi
- name: package
image: tystuyfzand/fpm
commands:
- export VERSION=`grep "Version" main.go | head -n 1 | awk '{print $3}' | sed -e 's/^"//' -e 's/"$//' | tr -d '\n'`
- echo "v$VERSION" > /build/version.txt
- chmod +x packaging/build-package.sh packaging/package-upload.sh
- ARCH=i386 packaging/build-package.sh
- ARCH=amd64 packaging/build-package.sh
- ARCH=armv7 packaging/build-package.sh
- ARCH=arm64 packaging/build-package.sh
- packaging/package-upload.sh
- chmod +x packaging/build-package.sh
- ARCH=386,amd64,arm,arm64 packaging/build-package.sh
volumes:
- name: build
path: /build
environment:
UPLOAD_URL:
from_secret: upload_url
- name: release
image: tystuyfzand/drone-gitea-release
volumes:
- name: build
path: /build
# - name: release
# image: tystuyfzand/drone-gitea-release
# volumes:
# - name: build
# path: /build
# settings:
# gitea_server: https://git.meow.tf
# tag_file: /build/version.txt
# title_file: /build/version.txt
# files: [ '/build/owapi_*' ]
# environment:
# PLUGIN_API_KEY:
# from_secret: gitea_token
- name: docker
image: plugins/docker
settings:
gitea_server: https://git.meow.tf
tag_file: /build/version.txt
title_file: /build/version.txt
files: [ '/build/*/owapi_*' ]
environment:
PLUGIN_API_KEY:
from_secret: gitea_token
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: registry.meow.tf/ow-api/ow-api
registry: registry.meow.tf
tags:
- latest
volumes:
- name: build
temp: {}
trigger:
branch:
- master
event:
- push

3
.gitignore vendored
View File

@ -1 +1,2 @@
vendor/
*.iml
.idea/

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM golang:alpine AS builder
ADD . /build
RUN cd /build && go build -o ow-api
FROM golang:alpine
COPY --from=builder /build/ow-api /usr/bin/ow-api
CMD [ "/usr/bin/ow-api" ]

5
LICENSE Normal file
View File

@ -0,0 +1,5 @@
Copyright 2020 Tyler Stuyfzand <admin@meow.tf>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -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)

39
go.mod
View File

@ -1,21 +1,28 @@
module git.meow.tf/ow-api/ow-api
go 1.12
go 1.21
require (
github.com/PuerkitoBio/goquery v1.5.1-0.20190109230704-3dcf72e6c17f
github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668
github.com/go-redis/redis v6.14.2+incompatible
github.com/julienschmidt/httprouter v1.2.0
github.com/labstack/echo/v4 v4.1.10 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/rs/cors v1.6.0
github.com/starboy/httpclient v0.0.0-20180910064439-d6d6e42b6080 // indirect
github.com/tystuyfzand/ovrstat v0.0.0-20181127024708-dc4fc9f39eb7
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
s32x.com/ovrstat v0.0.0-20190816015343-36b2cbd419d3
github.com/PuerkitoBio/goquery v1.8.1
github.com/bluele/gcache v0.0.2
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
github.com/go-redis/redis v6.15.9+incompatible
github.com/julienschmidt/httprouter v1.3.0
github.com/miekg/dns v1.1.57
github.com/ow-api/ovrstat v0.0.0-20240114101623-f548f90469ce
github.com/rs/cors v1.10.1
github.com/stoewer/go-strcase v1.3.0
golang.org/x/net v0.20.0
)
require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.20.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/tools v0.17.0 // indirect
)

206
go.sum
View File

@ -1,105 +1,155 @@
github.com/PuerkitoBio/goquery v1.5.1-0.20190109230704-3dcf72e6c17f h1:cWOyRTtBcTBjB0c+GyaQaXgP3g1HVM1KbvZL/Q5QNAM=
github.com/PuerkitoBio/goquery v1.5.1-0.20190109230704-3dcf72e6c17f/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833 h1:yCfXxYaelOyqnia8F/Yng47qhmfC9nKTRIbYRrRueq4=
github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/labstack/echo/v4 v4.0.1-0.20190313005416-e3717be4beda/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno=
github.com/labstack/echo/v4 v4.1.7-0.20190725203903-8cabd1e123d6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
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/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
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/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/ow-api/ovrstat v0.0.0-20240114101623-f548f90469ce h1:lsCA1fRmpwUXB3C57oGT/EBXuoKL0cQ7fpQzxCq/Zwg=
github.com/ow-api/ovrstat v0.0.0-20240114101623-f548f90469ce/go.mod h1:3LSzrUHphNkAVC65H14Nrpsk6mw6KTxTggfD4MtrSQA=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/starboy/httpclient v0.0.0-20180910064439-d6d6e42b6080 h1:2MLHNkv6Jl3E58fFOlOeshNo6eqanZV+yGPbJukRQIQ=
github.com/starboy/httpclient v0.0.0-20180910064439-d6d6e42b6080/go.mod h1:ZaOOtAQcXELCP9jugRrAv1qg3ctLlGnz++lH0KpIZ24=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
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/tystuyfzand/ovrstat v0.0.0-20181127024708-dc4fc9f39eb7 h1:ZHUhYZJiBOfJ9NPDT/YEKHOCc0tTtQiLGpTcW0M2mBM=
github.com/tystuyfzand/ovrstat v0.0.0-20181127024708-dc4fc9f39eb7/go.mod h1:GgVOFCqOnwT77FsVfvySTbw6u3rMW9rqUxvY7nGgpjg=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
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=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
s32x.com/ovrstat v0.0.0-20190506040748-69ba35e9ff58 h1:1ioAQKrIwwtxMm4uZySpqj9imNuyCDbL7dk9ccxntmQ=
s32x.com/ovrstat v0.0.0-20190506040748-69ba35e9ff58/go.mod h1:ukrDi0b+Kdtd+dI4M0HILhkMGtMZ4v3l2C82meAMzyw=
s32x.com/ovrstat v0.0.0-20190816015343-36b2cbd419d3 h1:04gLpRbm72K2OTsTnflheHmuuV7A0VOT33HNNo0UAnM=
s32x.com/ovrstat v0.0.0-20190816015343-36b2cbd419d3/go.mod h1:UzsLSEoY8B4FByz4e+5GGKebJio+H+axo3RxLr7d7Mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

249
main.go
View File

@ -2,27 +2,38 @@ package main
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/PuerkitoBio/goquery"
"github.com/julienschmidt/httprouter"
"github.com/ow-api/ovrstat/ovrstat"
"github.com/rs/cors"
"github.com/stoewer/go-strcase"
"golang.org/x/net/context"
"log"
"net/http"
"regexp"
"s32x.com/ovrstat/ovrstat"
"strings"
"time"
)
const (
Version = "2.2.0"
Version = "2.4.7"
OpAdd = "add"
OpRemove = "remove"
)
type ApiVersion int
const (
VersionOne ApiVersion = iota
VersionTwo
VersionThree
)
type gamesStats struct {
Played int64 `json:"played"`
Won int64 `json:"won"`
@ -48,6 +59,8 @@ var (
profilePatch *jsonpatch.Patch
heroNames []string
platforms = []string{ovrstat.PlatformPC, ovrstat.PlatformConsole}
)
func main() {
@ -74,24 +87,12 @@ func main() {
router := httprouter.New()
// PC
router.GET("/v1/stats/pc/:region/:tag/heroes/:heroes", injectPlatform("pc", heroes))
router.GET("/v1/stats/pc/:region/:tag/profile", injectPlatform("pc", profile))
router.GET("/v1/stats/pc/:region/:tag/complete", injectPlatform("pc", stats))
router.HEAD("/status", statusHandler)
router.GET("/status", statusHandler)
// Console
router.GET("/v1/stats/psn/:tag/heroes/:heroes", injectPlatform("psn", heroes))
router.GET("/v1/stats/psn/:tag/profile", injectPlatform("psn", profile))
router.GET("/v1/stats/psn/:tag/complete", injectPlatform("psn", stats))
router.GET("/v1/stats/xbl/:tag/heroes/:heroes", injectPlatform("xbl", heroes))
router.GET("/v1/stats/xbl/:tag/profile", injectPlatform("xbl", profile))
router.GET("/v1/stats/xbl/:tag/complete", injectPlatform("xbl", stats))
registerVersionOne(router)
// Version
router.GET("/v1/version", versionHandler)
router.HEAD("/v1/status", statusHandler)
router.GET("/v1/status", statusHandler)
registerVersionTwo(router)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
@ -106,63 +107,119 @@ func main() {
log.Fatal(http.ListenAndServe(*flagBind, c.Handler(router)))
}
func registerVersionOne(router *httprouter.Router) {
for _, platform := range platforms {
router.GET("/v1/stats/"+platform+"/:region/:tag/heroes/:heroes", injectPlatform(platform, heroes))
router.GET("/v1/stats/"+platform+"/:region/:tag/profile", injectPlatform(platform, profile))
router.GET("/v1/stats/"+platform+"/:region/:tag/complete", injectPlatform(platform, stats))
}
// Version
router.GET("/v1/version", versionHandler)
router.HEAD("/v1/status", statusHandler)
router.GET("/v1/status", statusHandler)
}
func registerVersionTwo(router *httprouter.Router) {
for _, platform := range platforms {
router.GET("/v2/stats/"+platform+"/:tag/heroes/:heroes", injectPlatform(platform, heroes))
router.GET("/v2/stats/"+platform+"/:tag/profile", injectPlatform(platform, profile))
router.GET("/v2/stats/"+platform+"/:tag/complete", injectPlatform(platform, stats))
router.GET("/v3/stats/"+platform+"/:tag/heroes/:heroes", injectPlatform(platform, heroes))
router.GET("/v3/stats/"+platform+"/:tag/profile", injectPlatform(platform, profile))
router.GET("/v3/stats/"+platform+"/:tag/complete", injectPlatform(platform, stats))
}
// Version
router.GET("/v2/version", versionHandler)
router.GET("/v3/version", versionHandler)
}
func loadHeroNames() {
stats, err := ovrstat.PCStats("cats-11481")
res, err := http.Get("https://overwatch.blizzard.com/en-us/heroes/")
if err != nil {
return
}
m := make(map[string]bool)
defer res.Body.Close()
for k := range stats.QuickPlayStats.TopHeroes {
m[k] = true
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return
}
for k := range stats.QuickPlayStats.CareerStats {
m[k] = true
}
links := doc.Find(".heroCard")
heroNames = make([]string, 0)
for k := range m {
heroNames = append(heroNames, k)
}
}
links.Each(func(_ int, s *goquery.Selection) {
val, exists := s.Attr("data-hero-id")
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)
if !exists {
return
}
heroNames = append(heroNames, strcase.LowerCamelCase(val))
})
log.Println("Loaded heroes", heroNames)
}
var (
tagRegexp = regexp.MustCompile("-(\\d+)$")
versionRegexp = regexp.MustCompile("^/(v\\d+)/")
)
func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch.Patch) ([]byte, error) {
func injectPlatform(platform string, handler httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if platform == "psn" || platform == "xbl" || platform == "nintendo-switch" {
platform = ovrstat.PlatformConsole
}
ps = append(ps, httprouter.Param{Key: "platform", Value: platform})
ctx := context.Background()
m := versionRegexp.FindStringSubmatch(r.RequestURI)
if m != nil {
version := VersionOne
switch m[1] {
case "v2":
version = VersionTwo
case "v3":
version = VersionThree
}
ctx = context.WithValue(ctx, "version", version)
}
handler(w, r.WithContext(ctx), ps)
}
}
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)
if region := ps.ByName("region"); region != "" {
if !tagRegexp.MatchString(tag) {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.New("bad tag")
}
stats, err = ovrstat.PCStats(tag)
} else if platform := ps.ByName("platform"); platform != "" {
stats, err = ovrstat.ConsoleStats(platform, tag)
} else {
return nil, errors.New("unknown region/platform")
}
platform := ps.ByName("platform")
stats, err = ovrstat.Stats(platform, strings.Replace(tag, "-", "#", -1))
if err != nil {
return nil, err
@ -188,22 +245,10 @@ func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch
games.Played = valueOrDefault(hs.Game, "gamesPlayed", 0)
games.Won = valueOrDefault(hs.Game, "gamesWon", 0)
awards := &awardsStats{}
awards.Cards = valueOrDefault(hs.MatchAwards, "cards", 0)
awards.Medals = valueOrDefault(hs.MatchAwards, "medals", 0)
awards.Bronze = valueOrDefault(hs.MatchAwards, "medalsBronze", 0)
awards.Silver = valueOrDefault(hs.MatchAwards, "medalsSilver", 0)
awards.Gold = valueOrDefault(hs.MatchAwards, "medalsGold", 0)
extra = append(extra, patchOperation{
Op: OpAdd,
Path: "/quickPlayStats/games",
Value: games,
}, patchOperation{
Op: OpAdd,
Path: "/quickPlayStats/awards",
Value: awards,
})
}
@ -213,54 +258,40 @@ func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch
games.Played = valueOrDefault(hs.Game, "gamesPlayed", 0)
games.Won = valueOrDefault(hs.Game, "gamesWon", 0)
awards := &awardsStats{}
awards.Cards = valueOrDefault(hs.MatchAwards, "cards", 0)
awards.Medals = valueOrDefault(hs.MatchAwards, "medals", 0)
awards.Bronze = valueOrDefault(hs.MatchAwards, "medalsBronze", 0)
awards.Silver = valueOrDefault(hs.MatchAwards, "medalsSilver", 0)
awards.Gold = valueOrDefault(hs.MatchAwards, "medalsGold", 0)
extra = append(extra, patchOperation{
Op: OpAdd,
Path: "/competitiveStats/games",
Value: games,
}, patchOperation{
Op: OpAdd,
Path: "/competitiveStats/awards",
Value: awards,
})
}
rating := 0
var ratingIcon string
if len(stats.Ratings) > 0 {
totalRating := 0
iconUrl := ""
if version == VersionThree {
m := make(map[string]ovrstat.Rating)
for _, rating := range stats.Ratings {
totalRating += rating.Level
iconUrl = rating.RankIcon
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",
}
rating = int(totalRating / len(stats.Ratings))
urlBase := iconUrl[0 : strings.Index(iconUrl, "rank-icons/")+11]
ratingIcon = urlBase + iconFor(rating)
}
extra = append(extra, patchOperation{
Op: OpAdd,
Path: "/rating",
Value: rating,
Op: OpRemove,
Path: "/ratings",
}, patchOperation{
Op: OpAdd,
Path: "/ratingIcon",
Value: ratingIcon,
Path: "/ratings",
Value: m,
})
extra = append(extra, ratingsPatches...)
}
}
b, err := json.Marshal(stats)
if err != nil {
@ -294,36 +325,16 @@ func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch
return b, err
}
var icons = map[int]string{
4000: "rank-GrandmasterTier.png",
3500: "rank-MasterTier.png",
3000: "rank-DiamondTier.png",
2500: "rank-PlatinumTier.png",
2000: "rank-GoldTier.png",
1500: "rank-SilverTier.png",
0: "rank-BronzeTier.png",
}
func generateCacheKey(r *http.Request, ps httprouter.Params) string {
version := VersionOne
func iconFor(rating int) string {
for r, icon := range icons {
if rating >= r {
return icon
}
if v := r.Context().Value("version"); v != nil {
version = v.(ApiVersion)
}
return ""
return versionToString(version) + "-" + ps.ByName("platform") + "-" + ps.ByName("tag")
}
func generateCacheKey(ps httprouter.Params) string {
var cacheKey string
tag := ps.ByName("tag")
if region := ps.ByName("region"); region != "" {
cacheKey = "pc-" + tag
} else if platform := ps.ByName("platform"); platform != "" {
cacheKey = platform + "-" + tag
}
return cacheKey
func versionToString(version ApiVersion) string {
return fmt.Sprintf("v%d", version)
}

View File

@ -2,12 +2,12 @@ package main
import (
"encoding/json"
"github.com/tystuyfzand/ovrstat/ovrstat"
"github.com/ow-api/ovrstat/ovrstat"
"testing"
)
func Test_Stats(t *testing.T) {
stats, err := ovrstat.PCStats("us", "cats-11481")
stats, err := ovrstat.Stats(ovrstat.PlatformPC, "cats-11481")
if err != nil {
t.Fatal(err)

View File

@ -1,10 +1,12 @@
fpm -s dir -t deb -p /build/$ARCH/owapi_${VERSION}_${ARCH}.deb \
-n ow-api -v $VERSION \
for arch in $(echo $ARCH | sed "s/,/ /g"); do
fpm -s dir -t deb -p /build/owapi_${VERSION}_${arch}.deb \
-n ow-api -v $VERSION -a $arch \
--deb-priority optional --force \
--deb-compression bzip2 \
--deb-compression gz \
--description "Overwatch API Server" \
-m "Tyler Stuyfzand <admin@meow.tf>" --vendor "Meow.tf" \
--before-install packaging/scripts/preinst.deb \
--after-install packaging/scripts/postinst.deb \
-a $ARCH /build/$ARCH/owapi_${ARCH}=/usr/bin/owapi \
/build/owapi_linux_${arch}=/usr/bin/owapi \
packaging/owapi.service=/lib/systemd/system/owapi.service
done

View File

@ -1 +1,3 @@
curl -X POST "$UPLOAD_URL" -F "file_i386=@/build/i386/owapi_${VERSION}_i386.deb" -F "file_amd64=@/build/amd64/owapi_${VERSION}_amd64.deb" -F "file_armv7=@/build/armv7/owapi_${VERSION}_armv7.deb"
curl -X POST "$UPLOAD_URL" -F "file_i386=@/build/i386/owapi_${VERSION}_i386.deb" \
-F "file_amd64=@/build/amd64/owapi_${VERSION}_amd64.deb" \
-F "file_armv7=@/build/armv7/owapi_${VERSION}_armv7.deb"

268
pro/main.go Normal file
View File

@ -0,0 +1,268 @@
package main
import (
"context"
"crypto/tls"
"errors"
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/miekg/dns"
"github.com/ow-api/ovrstat/ovrstat"
"log"
"math/rand"
"net"
"net/http"
"net/url"
"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.Stats(ovrstat.PlatformPC, "cats-11481")
if err != nil {
log.Fatalln("Error retrieving:", err)
}
log.Println(stats.Name+" is level", stats.Endorsement)
}
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
}

View File

@ -3,8 +3,8 @@ package main
import (
"encoding/json"
jsonpatch "git.meow.tf/ow-api/ow-api/json-patch"
"github.com/ow-api/ovrstat/ovrstat"
"net/http"
"s32x.com/ovrstat/ovrstat"
)
func valueOrDefault(m map[string]interface{}, key string, d int64) int64 {

View File

@ -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 {
@ -46,7 +47,7 @@ var (
"div.player-level div.u-vertical-center",
"div.player-level",
"div.player-rank",
"div.endorsement-level div.u-center",
"div.EndorsementIcon-tooltip div.u-center",
"div.EndorsementIcon",
"div.masthead p.masthead-detail.h4 span",
}
@ -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
}
@ -155,12 +148,12 @@ 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 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...)