initial commit
This commit is contained in:
commit
269c635f98
15 changed files with 1067 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
95
.gitignore
vendored
Normal file
95
.gitignore
vendored
Normal 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
3
Readme.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Berkutschi Ski Jumping Data Scraper
|
||||||
|
|
||||||
|
This is very much WIP
|
59
berkutschi/http.go
Normal file
59
berkutschi/http.go
Normal 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
246
berkutschi/pollType.go
Normal 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
35
berkutschi/types.go
Normal 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
129
berkutschi/websocket.go
Normal 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
7
default.nix
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{ pkgs ? import <nixpkgs> { } }:
|
||||||
|
with pkgs;
|
||||||
|
mkShell {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
go_1_17
|
||||||
|
];
|
||||||
|
}
|
41
flake.lock
Normal file
41
flake.lock
Normal 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
15
flake.nix
Normal 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
18
go.mod
Normal 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
98
go.sum
Normal 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
165
main.go
Normal 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
30
package.nix
Normal 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
125
types.go
Normal 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"`
|
||||||
|
}
|
Loading…
Reference in a new issue