initial commit

This commit is contained in:
gari 2022-01-29 19:25:03 +01:00
commit 269c635f98
15 changed files with 1067 additions and 0 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

95
.gitignore vendored Normal file
View File

@ -0,0 +1,95 @@
#nix flake build output
result/
*.log
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

3
Readme.md Normal file
View File

@ -0,0 +1,3 @@
# Berkutschi Ski Jumping Data Scraper
This is very much WIP

59
berkutschi/http.go Normal file
View File

@ -0,0 +1,59 @@
package berkutschi
import (
"context"
"fmt"
"time"
"github.com/monaco-io/request"
"github.com/rs/zerolog/log"
)
var pollURL = "https://live.berkutschi.com/events/"
func Poll(event int) (PollData, error) {
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second)
c := request.Client{
Context: ctx,
URL: fmt.Sprintf("%s%d.json", pollURL, event),
Method: "GET",
}
data := new(PollData)
resp := c.Send().Scan(data)
if !resp.OK() {
return PollData{}, resp.Error()
}
return *data, nil
}
func (b *Berkutschi) registerClient() error {
var body = struct {
Channel string `json:"channel"`
ID string `json:"id"`
SupportedConnectionTypes []string `json:"supportedConnectionTypes"`
Version string `json:"version"`
}{
Channel: "/meta/handshake",
Version: "1.0",
ID: "1",
SupportedConnectionTypes: []string{"websocket"},
}
var result []BerkutschiClientRegisterResponse
c := request.Client{
URL: "https://live.berkutschi.com/faye",
Method: "POST",
JSON: body,
}
resp := c.Send().Scan(&result)
if !resp.OK() {
// handle error
log.Error().Err(fmt.Errorf("%v", resp.Error())).Fields(struct{ Event int }{Event: b.event}).Send()
return resp.Error()
}
b.log.Debug().Msgf("%+v", result)
b.clientID = result[0].ClientID
//TODO check length of result
return nil
}

246
berkutschi/pollType.go Normal file
View File

@ -0,0 +1,246 @@
package berkutschi
type PollData struct {
Data struct {
AvailableStates []string `json:"available_states"`
Current struct {
Bib string `json:"bib"`
Club string `json:"club"`
Cumul struct {
Points float64 `json:"points"`
Rank int64 `json:"rank"`
} `json:"cumul"`
DateOfBirth string `json:"date_of_birth"`
Dnf bool `json:"dnf"`
DNS bool `json:"dns"`
Dq bool `json:"dq"`
Dqp bool `json:"dqp"`
Dtb1 interface{} `json:"dtb1"`
Dtb2 interface{} `json:"dtb2"`
Dtb3 interface{} `json:"dtb3"`
Firstname string `json:"firstname"`
Fiscode string `json:"fiscode"`
Gatecomp float64 `json:"gatecomp"`
Gatesnumber interface{} `json:"gatesnumber"`
Image string `json:"image"`
Image2 string `json:"image2"`
Image3 string `json:"image3"`
Judge struct {
One struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"1"`
Two struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"2"`
Three struct {
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"3"`
Four struct {
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"4"`
Five struct {
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"5"`
} `json:"judge"`
Judgetotal struct {
Points float64 `json:"points"`
Rank int64 `json:"rank"`
} `json:"judgetotal"`
Lastname string `json:"lastname"`
Length struct {
Length float64 `json:"length"`
Points float64 `json:"points"`
} `json:"length"`
Nat string `json:"nat"`
Nps bool `json:"nps"`
Points struct {
Points float64 `json:"points"`
Rank int64 `json:"rank"`
} `json:"points"`
Speed struct {
Speed string `json:"speed"`
} `json:"speed"`
Wind struct {
Compensation float64 `json:"compensation"`
Wind float64 `json:"wind"`
} `json:"wind"`
} `json:"current"`
Messages []struct {
Text string `json:"text"`
Timestamp string `json:"timestamp"`
} `json:"messages"`
Next struct {
Bib string `json:"bib"`
Club string `json:"club"`
DateOfBirth string `json:"date_of_birth"`
Firstname string `json:"firstname"`
Fiscode string `json:"fiscode"`
Image string `json:"image"`
Image2 string `json:"image2"`
Image3 string `json:"image3"`
Lastname string `json:"lastname"`
Nat string `json:"nat"`
} `json:"next"`
Raceinfo struct {
Discipline interface{} `json:"discipline"`
Event string `json:"event"`
Gender string `json:"gender"`
Judges struct {
One struct {
Nation string `json:"nation"`
} `json:"1"`
Two struct {
Nation string `json:"nation"`
} `json:"2"`
Three struct {
Nation string `json:"nation"`
} `json:"3"`
Four struct {
Nation string `json:"nation"`
} `json:"4"`
Five struct {
Nation string `json:"nation"`
} `json:"5"`
} `json:"judges"`
Mvalue string `json:"mvalue"`
No string `json:"no"`
Team string `json:"team"`
} `json:"raceinfo"`
Results []struct {
Bib int64 `json:"bib"`
DNS bool `json:"dns"`
Dq bool `json:"dq"`
FinalRank int64 `json:"final_rank"`
Gatecomp float64 `json:"gatecomp"`
Gatesnumber interface{} `json:"gatesnumber"`
Length1 float64 `json:"length1"`
LengthPoints1 float64 `json:"length_points1"`
Name string `json:"name"`
Nation string `json:"nation"`
Nps bool `json:"nps"`
Points1 float64 `json:"points1"`
Q bool `json:"q"`
Qualified string `json:"qualified"`
Speed string `json:"speed"`
Total float64 `json:"total"`
Wind struct {
Compensation float64 `json:"compensation"`
Wind float64 `json:"wind"`
} `json:"wind"`
} `json:"results"`
Startlist struct {
Jumpers []struct {
Bib string `json:"bib"`
Club string `json:"club"`
DateOfBirth string `json:"date_of_birth"`
Firstname string `json:"firstname"`
Fiscode string `json:"fiscode"`
Image string `json:"image"`
Image2 string `json:"image2"`
Image3 string `json:"image3"`
Lastname string `json:"lastname"`
Nat string `json:"nat"`
} `json:"jumpers"`
Runno string `json:"runno"`
} `json:"startlist"`
Status string `json:"status"`
Team bool `json:"team"`
} `json:"data"`
Event struct {
Canceled bool `json:"canceled"`
Cancelled bool `json:"cancelled"`
CompetitionActs []struct {
Date string `json:"date"`
Sort string `json:"sort"`
Time string `json:"time"`
} `json:"competition_acts"`
CreatedAt string `json:"created_at"`
Date string `json:"date"`
EndOfPeriod bool `json:"end_of_period"`
Fiscodex string `json:"fiscodex"`
Gender string `json:"gender"`
Hill struct {
BackwindFactor string `json:"backwind_factor"`
BuildingYear int64 `json:"building_year"`
Certificate string `json:"certificate"`
Contact string `json:"contact"`
CreatedAt interface{} `json:"created_at"`
Description string `json:"description"`
GateFactor string `json:"gate_factor"`
HeadwindFactor string `json:"headwind_factor"`
Height interface{} `json:"height"`
HillImages []struct {
HillThumb string `json:"hill_thumb"`
HillThumb150 string `json:"hill_thumb150"`
HillThumb200 string `json:"hill_thumb200"`
HillThumb480 string `json:"hill_thumb480"`
PhotoFileName string `json:"photo_file_name"`
} `json:"hill_images"`
HillJumpers []struct {
Distance string `json:"distance"`
HillID int64 `json:"hill_id"`
ID int64 `json:"id"`
Jumper struct {
Name string `json:"name"`
Nation struct {
FlagPath string `json:"flag_path"`
ID int64 `json:"id"`
Name string `json:"name"`
Shortname string `json:"shortname"`
Slug string `json:"slug"`
Visible bool `json:"visible"`
} `json:"nation"`
} `json:"jumper"`
JumperID int64 `json:"jumper_id"`
RecordDate string `json:"record_date"`
} `json:"hill_jumpers"`
HillRecord string `json:"hill_record"`
HillSize string `json:"hill_size"`
ID int64 `json:"id"`
InrunLength string `json:"inrun_length"`
Location struct {
Lat string `json:"lat"`
Lng string `json:"lng"`
Name string `json:"name"`
Nation struct {
FlagPath string `json:"flag_path"`
Shortname string `json:"shortname"`
} `json:"nation"`
} `json:"location"`
LocationID int64 `json:"location_id"`
Name string `json:"name"`
OutrunGradient string `json:"outrun_gradient"`
PointK string `json:"point_k"`
Size string `json:"size"`
Slug string `json:"slug"`
Speed string `json:"speed"`
StandCapacity int64 `json:"stand_capacity"`
TableGradient string `json:"table_gradient"`
TableHeight string `json:"table_height"`
TowerHeight interface{} `json:"tower_height"`
UpdatedAt string `json:"updated_at"`
Visible bool `json:"visible"`
Windfinder string `json:"windfinder"`
} `json:"hill"`
HillID int64 `json:"hill_id"`
ID int64 `json:"id"`
Remarks string `json:"remarks"`
Season string `json:"season"`
Sorts []string `json:"sorts"`
Start string `json:"start"`
Team int64 `json:"team"`
UpdatedAt string `json:"updated_at"`
} `json:"event"`
}

35
berkutschi/types.go Normal file
View File

@ -0,0 +1,35 @@
package berkutschi
type BerkutschiClientRegisterResponse struct {
Advice struct {
Interval int64 `json:"interval"`
Reconnect string `json:"reconnect"`
Timeout int64 `json:"timeout"`
} `json:"advice"`
Channel string `json:"channel"`
ClientID string `json:"clientId"`
ID string `json:"id"`
Successful bool `json:"successful"`
SupportedConnectionTypes []string `json:"supportedConnectionTypes"`
Version string `json:"version"`
}
type BerkutschiClientMessages interface {
Marshal() ([]byte, error)
}
type BerkutschiConnectMessages []BerkutschiConnectMessage
type BerkutschiConnectMessage struct {
Channel string `json:"channel"`
ClientID string `json:"clientId"`
ConnectionType string `json:"connectionType"`
ID string `json:"id"`
}
type BerkutschiSubscribeMessages []BerkutschiSubscribeMessage
type BerkutschiSubscribeMessage struct {
Channel string `json:"channel"`
ClientID string `json:"clientId"`
ID string `json:"id"`
Subscription string `json:"subscription"`
}

129
berkutschi/websocket.go Normal file
View File

@ -0,0 +1,129 @@
package berkutschi
import (
"context"
"encoding/json"
"fmt"
"net/url"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"nhooyr.io/websocket"
)
var u = url.URL{Scheme: "wss", Host: "live.berkutschi.com", Path: "/faye"}
type Berkutschi struct {
conn *websocket.Conn
event int
clientID string
log zerolog.Logger
ctx context.Context
TX chan BerkutschiClientMessages
RX chan []byte
}
func Init(event int) *Berkutschi {
l := log.With().Int("event", event).Logger()
b := &Berkutschi{
event: event,
TX: make(chan BerkutschiClientMessages),
RX: make(chan []byte),
log: l,
}
b.registerClient()
b.connect()
b.connectAndSubscribe()
return b
}
func (b *Berkutschi) connectAndSubscribe() {
connectMessage := BerkutschiConnectMessage{
Channel: "/meta/connect",
ClientID: b.clientID,
ConnectionType: "websocket",
ID: "2",
}
subscribeMessage := BerkutschiSubscribeMessage{
Channel: "/meta/subscribe",
ClientID: b.clientID,
Subscription: fmt.Sprintf("/messages/%d", b.event),
ID: "3",
}
b.TX <- BerkutschiConnectMessages{connectMessage}
b.log.Debug().Msgf("connected to berkutschi")
b.TX <- BerkutschiSubscribeMessages{subscribeMessage}
b.log.Debug().Msgf("subscribed to event %d", b.event)
}
func (b *Berkutschi) connect() error {
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
c, _, err := websocket.Dial(ctx, u.String(), nil)
if err != nil {
b.log.Err(fmt.Errorf("Error connecting to websocket: %s", err)).Send()
}
b.log.Debug().Msgf("Connected to websocket")
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer cancel()
for {
select {
case <-ctx.Done():
return
default:
_, message, err := c.Read(ctx)
if err != nil {
b.log.Error().Err(fmt.Errorf("Error reading from websocket, reconnecting: %s", err)).Send()
return
}
b.RX <- message
}
}
}()
go func() {
defer cancel()
for {
select {
case <-ctx.Done():
return
case message := <-b.TX:
byteMessage, _ := message.Marshal()
err := c.Write(ctx, websocket.MessageText, byteMessage)
if err != nil {
b.log.Error().Err(fmt.Errorf("Error writing to websocket, reconnecting: %s", err)).Send()
return
}
b.log.Debug().Msgf("Sent message: %v", message)
}
}
}()
go func() {
<-ctx.Done()
go b.closeAndReconnect()
}()
b.conn = c
return err
}
func (b *Berkutschi) closeAndReconnect() error {
b.registerClient()
b.log.Debug().Msgf("Reconnecting websocket connection")
b.conn.Close(websocket.StatusInternalError, "")
b.connect()
b.connectAndSubscribe()
return nil
}
func (m BerkutschiConnectMessages) Marshal() ([]byte, error) {
return json.Marshal(m)
}
func (m BerkutschiSubscribeMessages) Marshal() ([]byte, error) {
return json.Marshal(m)
}

7
default.nix Normal file
View File

@ -0,0 +1,7 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
mkShell {
nativeBuildInputs = [
go_1_17
];
}

41
flake.lock Normal file
View File

@ -0,0 +1,41 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1642700792,
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1643119265,
"narHash": "sha256-mmDEctIkHSWcC/HRpeaw6QOe+DbNOSzc0wsXAHOZWwo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b05d2077ebe219f6a47825767f8bab5c6211d200",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

15
flake.nix Normal file
View File

@ -0,0 +1,15 @@
{
description = "berkutschi";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem
(system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
devShell = import ./default.nix { inherit pkgs; };
defaultPackage = import ./package.nix { inherit pkgs;};
}
);
}

18
go.mod Normal file
View File

@ -0,0 +1,18 @@
module berkutschi
go 1.17
require (
github.com/gorilla/websocket v1.4.2
github.com/maurodelazeri/gorilla-reconnect v0.0.0-20180328170005-42501a5438b9
github.com/mehdioa/nlog v0.0.0-20210327090009-d60bf476a16a
github.com/monaco-io/request v1.0.15
)
require (
github.com/jpillora/backoff v1.0.0 // indirect
github.com/klauspost/compress v1.10.3 // indirect
github.com/rs/zerolog v1.26.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)

98
go.sum Normal file
View File

@ -0,0 +1,98 @@
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/maurodelazeri/gorilla-reconnect v0.0.0-20180328170005-42501a5438b9 h1:ZuBaZYBKi9zJFaqXyw0hN46nvBcU315To+SN+Qh0wjU=
github.com/maurodelazeri/gorilla-reconnect v0.0.0-20180328170005-42501a5438b9/go.mod h1:jawYJmNk6FVmenPRjYlbV+OcQ9fBGqv3dZwZnUSoR48=
github.com/mehdioa/nlog v0.0.0-20210327090009-d60bf476a16a h1:p/otnVjZSfec48nsELzyGDwoAMtD+xoG8a6c5TW8Fuw=
github.com/mehdioa/nlog v0.0.0-20210327090009-d60bf476a16a/go.mod h1:Co+vI+QcF2CG+gvNdG+kvzh2f9EE+YiF0OSdWoAmlOU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monaco-io/request v1.0.15 h1:krhHE0yL6yL+xP0YwX5udyd+xnfQfHccop3Nsixec54=
github.com/monaco-io/request v1.0.15/go.mod h1:voq81GC2YDynQM3W/4D6oqGhqb+u7zULvbLzFkTmDfo=
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/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=

165
main.go Normal file
View File

@ -0,0 +1,165 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"berkutschi/berkutschi"
)
const event = 3860
const logFile = "my.log"
var Judges = [5]JugdeRaceInfo{}
func main() {
zerolog.TimeFieldFormat = time.RFC1123Z
zerolog.SetGlobalLevel(zerolog.InfoLevel)
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC1123Z}
multi := zerolog.MultiLevelWriter(consoleWriter, f)
log.Logger = zerolog.New(multi).With().Timestamp().Caller().Logger()
pollData, err := berkutschi.Poll(event)
if err != nil {
log.Panic().Err(fmt.Errorf("Error while polling: %s", err)).Send()
}
fillJudgeraceinfo(pollData)
b := berkutschi.Init(event)
go func() {
jumperInfo := JumperInfo{}
jumperscore := JumperScore{}
for {
message := <-b.RX
var jumpMessage BerkutschiJumpUpdateMessages
err := json.Unmarshal(message, &jumpMessage)
//log.Debug().Msgf("Received message: %v", string(message))
if err == nil && jumpMessage[0].Data.Current.Lastname != "" {
jumper, err := fillJumperInfo(jumpMessage)
if err != nil {
continue
}
if newJumperInfoData(jumperInfo, jumper) {
jumperInfo = jumper
log.Info().Msgf("New jumper info: %+v", jumperInfo)
}
score, _ := fillJumperScore(jumpMessage)
if score.Rank != 0 && newJumperScoreData(jumperscore, score) {
jumperscore = score
log.Info().Msgf("New jumper score: %+v", jumperscore)
}
} else if err != nil {
log.Error().RawJSON("receivedMessage", message).Err(fmt.Errorf("%v", err))
} else {
if jumpMessage[0].Data.Next.Lastname == "" {
log.Debug().RawJSON("receivedMessage", message).Msg("Received message i cant decode")
}
}
}
}()
ctx, _ := context.WithCancel(context.Background())
<-ctx.Done()
}
func fillJumperInfo(m BerkutschiJumpUpdateMessages) (JumperInfo, error) {
var err error
JumperInfo := JumperInfo{}
if m[0].Data.Current.Firstname == "" || m[0].Data.Current.Lastname == "" {
err = fmt.Errorf("Name is empty")
return JumperInfo, err
}
JumperInfo.Name = fmt.Sprint(m[0].Data.Current.Firstname, " ", strings.ToUpper(m[0].Data.Current.Lastname))
JumperInfo.Bib = m[0].Data.Current.Bib
JumperInfo.Nation = strings.ToUpper(m[0].Data.Current.Nat)
JumperInfo.Image = m[0].Data.Current.Image2
return JumperInfo, err
}
func fillJumperScore(m BerkutschiJumpUpdateMessages) (JumperScore, error) {
var err error
JumperScore := JumperScore{}
if m[0].Data.Current.Firstname == "" || m[0].Data.Current.Lastname == "" {
err = fmt.Errorf("Name is empty")
return JumperScore, err
}
JumperScore.Name = fmt.Sprint(m[0].Data.Current.Firstname, " ", strings.ToUpper(m[0].Data.Current.Lastname))
JumperScore.Bib = m[0].Data.Current.Bib
JumperScore.Nation = strings.ToUpper(m[0].Data.Current.Nat)
JumperScore.Points = m[0].Data.Current.Cumul.Points
JumperScore.Rank = m[0].Data.Current.Cumul.Rank
JumperScore.Wind = m[0].Data.Current.Wind.Wind
JumperScore.Length = fmt.Sprintf("%vm", m[0].Data.Current.Length.Length)
for i, _ := range JumperScore.Judges {
switch i {
case 0:
JumperScore.Judges[i].Score = m[0].Data.Current.Judge.One.Rate
JumperScore.Judges[i].Nation = Judges[i].Nation
JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.One.Discard
case 1:
JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Two.Rate
JumperScore.Judges[i].Nation = Judges[i].Nation
JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Two.Discard
case 2:
JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Three.Rate
JumperScore.Judges[i].Nation = Judges[i].Nation
JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Three.Discard
case 3:
JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Four.Rate
JumperScore.Judges[i].Nation = Judges[i].Nation
JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Four.Discard
case 4:
JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Five.Rate
JumperScore.Judges[i].Nation = Judges[i].Nation
JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Five.Discard
}
}
return JumperScore, err
}
func fillJudgeraceinfo(data berkutschi.PollData) {
for i, _ := range Judges {
switch i {
case 0:
Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.One.Nation)
case 1:
Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Two.Nation)
case 2:
Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Three.Nation)
case 3:
Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Four.Nation)
case 4:
Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Five.Nation)
}
}
}
func newJumperInfoData(oldData, newData JumperInfo) bool {
if oldData.Name != newData.Name || oldData.Bib != newData.Bib || oldData.Nation != newData.Nation || oldData.Image != newData.Image {
return true
}
return false
}
func newJumperScoreData(oldData, newData JumperScore) bool {
if oldData.Name != newData.Name || oldData.Bib != newData.Bib || oldData.Nation != newData.Nation ||
oldData.Points != newData.Points || oldData.Rank != newData.Rank || oldData.Wind != newData.Wind || oldData.Length != newData.Length {
return true
}
for i, oldJudge := range oldData.Judges {
if oldJudge.Score != newData.Judges[i].Score || oldJudge.Nation != newData.Judges[i].Nation || oldJudge.Discard != newData.Judges[i].Discard {
return true
}
}
return false
}

30
package.nix Normal file
View File

@ -0,0 +1,30 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
let
version = "0.0.1";
deps = [];
nativeDeps = [
];
in
pkgs.buildGo117Module {
pname = "berkutschi";
inherit version;
src = ./.;
buildInputs = [
] ++deps;
nativeBuildInputs = [
] ++nativeDeps;
tags = [ ];
allowGoReference = true;
vendorSha256 = "sha256-qXi32q5PjnvYj6LUUTT9vF3VICj6mFnMUdVA1FJMSD0=";
meta = {
description = "A Client for the Berkutschi WS Api";
homepage = "https://git.entr0py.de/garionion/berkutschi";
};
}

125
types.go Normal file
View File

@ -0,0 +1,125 @@
package main
type JugdeRaceInfo struct {
Nation string
}
type JumperInfo struct {
Name string
Bib string
Nation string
Image string
}
type JumperScore struct {
Name string
Bib string
Nation string
Points float64
Rank int
Wind float64
Length string
Judges [5]Judge
}
type Judge struct {
Nation string
Score float64
Discard bool
}
type BerkutschiJumpUpdateMessages []BerkutschiJumpUpdateMessage
type BerkutschiJumpUpdateMessage struct {
Channel string `json:"channel"`
Data struct {
Current struct {
Bib string `json:"bib"`
Club string `json:"club"`
Cumul struct {
Points float64 `json:"points"`
Rank int `json:"rank"`
} `json:"cumul"`
DateOfBirth string `json:"date_of_birth"`
Dnf bool `json:"dnf"`
DNS bool `json:"dns"`
Dq bool `json:"dq"`
Dqp bool `json:"dqp"`
Dtb1 interface{} `json:"dtb1"`
Dtb2 interface{} `json:"dtb2"`
Dtb3 interface{} `json:"dtb3"`
Firstname string `json:"firstname"`
Fiscode string `json:"fiscode"`
Gatecomp float64 `json:"gatecomp"`
Gatesnumber interface{} `json:"gatesnumber"`
Image string `json:"image"`
Image2 string `json:"image2"`
Image3 string `json:"image3"`
Judge struct {
One struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"1"`
Two struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"2"`
Three struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"3"`
Four struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"4"`
Five struct {
Discard bool `json:"discard"`
ID string `json:"id"`
Order int64 `json:"order"`
Rate float64 `json:"rate"`
} `json:"5"`
} `json:"judge"`
Judgetotal struct {
Points float64 `json:"points"`
Rank int64 `json:"rank"`
} `json:"judgetotal"`
Lastname string `json:"lastname"`
Length struct {
Length float64 `json:"length"`
Points float64 `json:"points"`
} `json:"length"`
Nat string `json:"nat"`
Nps bool `json:"nps"`
Points struct {
Points float64 `json:"points"`
Rank int `json:"rank"`
} `json:"points"`
Speed struct {
Speed string `json:"speed"`
} `json:"speed"`
Wind struct {
Compensation float64 `json:"compensation"`
Wind float64 `json:"wind"`
} `json:"wind"`
} `json:"current"`
Next struct {
Bib string `json:"bib"`
Club string `json:"club"`
DateOfBirth string `json:"date_of_birth"`
Firstname string `json:"firstname"`
Fiscode string `json:"fiscode"`
Image string `json:"image"`
Image2 string `json:"image2"`
Image3 string `json:"image3"`
Lastname string `json:"lastname"`
Nat string `json:"nat"`
} `json:"next"`
} `json:"data"`
}