aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rankings/.gitignore3
-rw-r--r--rankings/export.go20
-rw-r--r--rankings/fetch.go81
-rw-r--r--rankings/filter.go37
-rw-r--r--rankings/main.go37
-rw-r--r--rankings/prefetch.go8
6 files changed, 146 insertions, 40 deletions
diff --git a/rankings/.gitignore b/rankings/.gitignore
index 764d23d..48d3792 100644
--- a/rankings/.gitignore
+++ b/rankings/.gitignore
@@ -1,2 +1,3 @@
1.env 1.env
2output/ \ No newline at end of file 2output/
3cache/
diff --git a/rankings/export.go b/rankings/export.go
index 20dfebe..cdb9213 100644
--- a/rankings/export.go
+++ b/rankings/export.go
@@ -2,20 +2,30 @@ package main
2 2
3import ( 3import (
4 "encoding/json" 4 "encoding/json"
5 "log"
5 "os" 6 "os"
6) 7)
7 8
8func exportAll(spRankings, mpRankings, overallRankings *[]*Player) { 9func exportAll(spRankings, mpRankings, overallRankings []*Player) {
9 sp, _ := os.Create("./output/sp.json") 10 err := os.Mkdir("./output", 0775)
10 spRankingsOut, _ := json.Marshal(*spRankings) 11 if err != nil && !os.IsExist(err) {
12 log.Fatalln(err.Error())
13 }
14
15 sp, err := os.Create("./output/sp.json")
16 if err != nil {
17 log.Fatalln(err.Error())
18 }
19
20 spRankingsOut, _ := json.Marshal(spRankings)
11 sp.Write(spRankingsOut) 21 sp.Write(spRankingsOut)
12 sp.Close() 22 sp.Close()
13 mp, _ := os.Create("./output/mp.json") 23 mp, _ := os.Create("./output/mp.json")
14 mpRankingsOut, _ := json.Marshal(*mpRankings) 24 mpRankingsOut, _ := json.Marshal(mpRankings)
15 mp.Write(mpRankingsOut) 25 mp.Write(mpRankingsOut)
16 mp.Close() 26 mp.Close()
17 overall, _ := os.Create("./output/overall.json") 27 overall, _ := os.Create("./output/overall.json")
18 overallRankingsOut, _ := json.Marshal(*overallRankings) 28 overallRankingsOut, _ := json.Marshal(overallRankings)
19 overall.Write(overallRankingsOut) 29 overall.Write(overallRankingsOut)
20 overall.Close() 30 overall.Close()
21} 31}
diff --git a/rankings/fetch.go b/rankings/fetch.go
index ee5d5bb..cf04e81 100644
--- a/rankings/fetch.go
+++ b/rankings/fetch.go
@@ -9,9 +9,10 @@ import (
9 "net/http" 9 "net/http"
10 "os" 10 "os"
11 "strconv" 11 "strconv"
12 "strings"
12) 13)
13 14
14func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *map[string]*Player { 15func fetchLeaderboard(records []Record, overrides map[string]map[string]int, useCache bool) map[string]*Player {
15 log.Println("fetching leaderboard") 16 log.Println("fetching leaderboard")
16 players := map[string]*Player{} 17 players := map[string]*Player{}
17 // first init players map with records from portal gun and doors 18 // first init players map with records from portal gun and doors
@@ -20,8 +21,8 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
20 end := 5000 21 end := 5000
21 22
22 for fetchAnotherPage { 23 for fetchAnotherPage {
23 portalGunEntries := fetchRecordsFromMap(47459, 0, 5000) 24 portalGunEntries := fetchRecordsFromMap(47459, 0, 5000, useCache)
24 fetchAnotherPage = portalGunEntries.needsAnotherPage(&(*records)[0]) 25 fetchAnotherPage = portalGunEntries.needsAnotherPage(&records[0])
25 if fetchAnotherPage { 26 if fetchAnotherPage {
26 start = end + 1 27 start = end + 1
27 end = start + 5000 28 end = start + 5000
@@ -49,8 +50,8 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
49 end = 5000 50 end = 5000
50 51
51 for fetchAnotherPage { 52 for fetchAnotherPage {
52 doorsEntries := fetchRecordsFromMap(47740, start, end) 53 doorsEntries := fetchRecordsFromMap(47740, start, end, useCache)
53 fetchAnotherPage = doorsEntries.needsAnotherPage(&(*records)[51]) 54 fetchAnotherPage = doorsEntries.needsAnotherPage(&records[51])
54 if fetchAnotherPage { 55 if fetchAnotherPage {
55 start = end + 1 56 start = end + 1
56 end = start + 5000 57 end = start + 5000
@@ -83,7 +84,7 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
83 } 84 }
84 } 85 }
85 86
86 for _, record := range *records { 87 for _, record := range records {
87 if record.MapID == 47459 || record.MapID == 47740 { 88 if record.MapID == 47459 || record.MapID == 47740 {
88 continue 89 continue
89 } 90 }
@@ -93,7 +94,7 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
93 end := 5000 94 end := 5000
94 95
95 for fetchAnotherPage { 96 for fetchAnotherPage {
96 entries := fetchRecordsFromMap(record.MapID, start, end) 97 entries := fetchRecordsFromMap(record.MapID, start, end, useCache)
97 fetchAnotherPage = entries.needsAnotherPage(&record) 98 fetchAnotherPage = entries.needsAnotherPage(&record)
98 if fetchAnotherPage { 99 if fetchAnotherPage {
99 start = end + 1 100 start = end + 1
@@ -106,11 +107,11 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
106 } 107 }
107 score := entry.Score 108 score := entry.Score
108 if entry.Score < record.MapWR { 109 if entry.Score < record.MapWR {
109 _, ok := (*overrides)[entry.SteamID] 110 _, ok := overrides[entry.SteamID]
110 if ok { 111 if ok {
111 _, ok := (*overrides)[entry.SteamID][strconv.Itoa(record.MapID)] 112 _, ok := overrides[entry.SteamID][strconv.Itoa(record.MapID)]
112 if ok { 113 if ok {
113 score = (*overrides)[entry.SteamID][strconv.Itoa(record.MapID)] 114 score = overrides[entry.SteamID][strconv.Itoa(record.MapID)]
114 } else { 115 } else {
115 continue // ban 116 continue // ban
116 } 117 }
@@ -136,28 +137,60 @@ func fetchLeaderboard(records *[]Record, overrides *map[string]map[string]int) *
136 } 137 }
137 138
138 } 139 }
139 return &players 140 return players
140} 141}
141 142
142func fetchRecordsFromMap(mapID int, start int, end int) *Leaderboard { 143func fetchRecordsFromMap(mapID int, start int, end int, useCache bool) *Leaderboard {
143 resp, err := http.Get(fmt.Sprintf("https://steamcommunity.com/stats/Portal2/leaderboards/%d?xml=1&start=%d&end=%d", mapID, start, end)) 144 var filename string
145 if useCache {
146 filename := fmt.Sprintf("./cache/lb_%d_%d_%d.xml", mapID, start, end)
147 log.Println("from cache", filename)
148 file, _ := os.ReadFile(filename)
149 if file != nil {
150 leaderboard := Leaderboard{}
151 err := xml.Unmarshal(file, &leaderboard)
152 if err != nil {
153 log.Fatalln("failed to unmarshal cache.", err.Error())
154 }
155 return &leaderboard
156 }
157 }
158
159 url := fmt.Sprintf("https://steamcommunity.com/stats/Portal2/leaderboards/%d?xml=1&start=%d&end=%d", mapID, start, end)
160 resp, err := http.Get(url)
161 log.Println("fetched", url, ":", resp.StatusCode)
144 if err != nil { 162 if err != nil {
145 log.Fatalln(err.Error()) 163 log.Fatalln("failed to fetch leaderboard.", err.Error())
146 } 164 }
147 respBytes, err := io.ReadAll(resp.Body) 165 respBytes, err := io.ReadAll(resp.Body)
148 if err != nil { 166 if err != nil {
149 log.Fatalln(err.Error()) 167 log.Fatalln("failed to read leadeboard body.", err.Error())
150 } 168 }
151 leaderboard := Leaderboard{} 169 leaderboard := Leaderboard{}
152 err = xml.Unmarshal(respBytes, &leaderboard) 170 err = xml.Unmarshal(respBytes, &leaderboard)
153 if err != nil { 171 if err != nil {
154 log.Fatalln(err.Error()) 172 log.Println(string(respBytes))
173 log.Fatalln("failed to unmarshal leaderboard.", err.Error())
155 } 174 }
175
176 if useCache {
177 if err = os.WriteFile(filename, respBytes, 0644); err != nil {
178 log.Fatalln("failed write to file.", err.Error())
179 }
180 }
181
156 return &leaderboard 182 return &leaderboard
157} 183}
158 184
159func fetchPlayerInfo(player *Player) { 185func fetchPlayerInfo(players []*Player) {
160 url := fmt.Sprintf("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=%s&steamids=%s", os.Getenv("API_KEY"), player.SteamID) 186 log.Println("fetching info for", len(players), "players")
187
188 ids := make([]string, len(players))
189 for _, player := range players {
190 ids = append(ids, player.SteamID)
191 }
192
193 url := fmt.Sprintf("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=%s&steamids=%s", os.Getenv("API_KEY"), strings.Join(ids, ","))
161 resp, err := http.Get(url) 194 resp, err := http.Get(url)
162 if err != nil { 195 if err != nil {
163 log.Fatalln(err.Error()) 196 log.Fatalln(err.Error())
@@ -167,6 +200,7 @@ func fetchPlayerInfo(player *Player) {
167 log.Fatalln(err.Error()) 200 log.Fatalln(err.Error())
168 } 201 }
169 type PlayerSummary struct { 202 type PlayerSummary struct {
203 SteamID string `json:"steamid"`
170 PersonaName string `json:"personaname"` 204 PersonaName string `json:"personaname"`
171 AvatarFull string `json:"avatarfull"` 205 AvatarFull string `json:"avatarfull"`
172 } 206 }
@@ -180,6 +214,13 @@ func fetchPlayerInfo(player *Player) {
180 if err := json.Unmarshal(body, &data); err != nil { 214 if err := json.Unmarshal(body, &data); err != nil {
181 log.Fatalln(err.Error()) 215 log.Fatalln(err.Error())
182 } 216 }
183 player.AvatarLink = data.Response.Players[0].AvatarFull 217
184 player.Username = data.Response.Players[0].PersonaName 218 for _, profile := range data.Response.Players {
219 for _, player := range players {
220 if player.SteamID == profile.SteamID {
221 player.AvatarLink = profile.AvatarFull
222 player.Username = profile.PersonaName
223 }
224 }
225 }
185} 226}
diff --git a/rankings/filter.go b/rankings/filter.go
index 1d7233b..2af7911 100644
--- a/rankings/filter.go
+++ b/rankings/filter.go
@@ -2,11 +2,12 @@ package main
2 2
3import ( 3import (
4 "log" 4 "log"
5 "math"
5 "sort" 6 "sort"
6) 7)
7 8
8func filterRankings(spRankings, mpRankings, overallRankings *[]*Player, players *map[string]*Player) { 9func filterRankings(spRankings, mpRankings, overallRankings *[]*Player, players map[string]*Player) {
9 for k, p := range *players { 10 for k, p := range players {
10 if p.SpIterations == 51 { 11 if p.SpIterations == 51 {
11 *spRankings = append(*spRankings, p) 12 *spRankings = append(*spRankings, p)
12 } 13 }
@@ -18,13 +19,14 @@ func filterRankings(spRankings, mpRankings, overallRankings *[]*Player, players
18 *overallRankings = append(*overallRankings, p) 19 *overallRankings = append(*overallRankings, p)
19 } 20 }
20 if p.SpIterations < 51 && p.MpIterations < 48 { 21 if p.SpIterations < 51 && p.MpIterations < 48 {
21 delete(*players, k) 22 delete(players, k)
22 } 23 }
23 } 24 }
24 25
25 log.Println("getting player summaries") 26 log.Println("getting player summaries for", len(players), "players")
26 for _, v := range *players { 27
27 fetchPlayerInfo(v) 28 for _, chunk := range chunkMap(players, 100) {
29 fetchPlayerInfo(chunk)
28 } 30 }
29 31
30 log.Println("sorting the ranks") 32 log.Println("sorting the ranks")
@@ -91,3 +93,26 @@ func filterRankings(spRankings, mpRankings, overallRankings *[]*Player, players
91 (*overallRankings)[idx].OverallRank = rank 93 (*overallRankings)[idx].OverallRank = rank
92 } 94 }
93} 95}
96
97func chunkMap[T any](m map[string]*T, chunkSize int) [][]*T {
98 chunks := make([][]*T, 0, int(math.Ceil(float64(len(m))/float64(chunkSize))))
99 chunk := make([]*T, 0, chunkSize)
100
101 count := 0
102 for _, player := range m {
103 chunk = append(chunk, player)
104 count++
105
106 if count == chunkSize {
107 chunks = append(chunks, chunk)
108 chunk = make([]*T, 0, chunkSize)
109 count = 0
110 }
111 }
112
113 if len(chunk) > 0 {
114 chunks = append(chunks, chunk)
115 }
116
117 return chunks
118}
diff --git a/rankings/main.go b/rankings/main.go
index dfafb0c..552f058 100644
--- a/rankings/main.go
+++ b/rankings/main.go
@@ -10,11 +10,33 @@ import (
10 "github.com/robfig/cron/v3" 10 "github.com/robfig/cron/v3"
11) 11)
12 12
13var useCache = false
14
13func main() { 15func main() {
14 err := godotenv.Load() 16 err := godotenv.Load()
15 if err != nil { 17 if err != nil {
16 log.Fatalln("Error loading .env file:", err.Error()) 18 log.Fatalln("Error loading .env file:", err.Error())
17 } 19 }
20
21 runNow := false
22 for _, arg := range os.Args {
23 if arg == "-n" || arg == "--now" {
24 runNow = true
25 continue
26 }
27 if arg == "-c" || arg == "--cache" {
28 useCache = true
29 continue
30 }
31 }
32
33 useCache = useCache && runNow
34
35 if runNow {
36 run()
37 return
38 }
39
18 c := cron.New() 40 c := cron.New()
19 _, err = c.AddFunc("0 0 * * *", run) 41 _, err = c.AddFunc("0 0 * * *", run)
20 if err != nil { 42 if err != nil {
@@ -29,17 +51,24 @@ func main() {
29 51
30func run() { 52func run() {
31 log.Println("started job") 53 log.Println("started job")
54
32 records := readRecords() 55 records := readRecords()
56 log.Println("loaded", len(records), "records")
57
33 overrides := readOverrides() 58 overrides := readOverrides()
34 players := fetchLeaderboard(records, overrides) 59 log.Println("loaded", len(overrides), "player overrides")
60
61 players := fetchLeaderboard(records, overrides, useCache)
35 62
36 spRankings := []*Player{} 63 spRankings := []*Player{}
37 mpRankings := []*Player{} 64 mpRankings := []*Player{}
38 overallRankings := []*Player{} 65 overallRankings := []*Player{}
39 66
40 log.Println("filtering rankings") 67 log.Println("filtering rankings for", len(players), "players")
41 filterRankings(&spRankings, &mpRankings, &overallRankings, players) 68 filterRankings(&spRankings, &mpRankings, &overallRankings, players)
42 69
43 log.Println("exporting jsons") 70 log.Println("exporting jsons for", len(players), "players")
44 exportAll(&spRankings, &mpRankings, &overallRankings) 71 exportAll(spRankings, mpRankings, overallRankings)
72
73 log.Println("done")
45} 74}
diff --git a/rankings/prefetch.go b/rankings/prefetch.go
index 487a76f..a559b26 100644
--- a/rankings/prefetch.go
+++ b/rankings/prefetch.go
@@ -7,7 +7,7 @@ import (
7 "os" 7 "os"
8) 8)
9 9
10func readRecords() *[]Record { 10func readRecords() []Record {
11 recordsFile, err := os.Open("./input/records.json") 11 recordsFile, err := os.Open("./input/records.json")
12 if err != nil { 12 if err != nil {
13 log.Fatalln(err.Error()) 13 log.Fatalln(err.Error())
@@ -22,10 +22,10 @@ func readRecords() *[]Record {
22 if err != nil { 22 if err != nil {
23 log.Fatalln(err.Error()) 23 log.Fatalln(err.Error())
24 } 24 }
25 return &records 25 return records
26} 26}
27 27
28func readOverrides() *map[string]map[string]int { 28func readOverrides() map[string]map[string]int {
29 overridesFile, err := os.Open("./input/overrides.json") 29 overridesFile, err := os.Open("./input/overrides.json")
30 if err != nil { 30 if err != nil {
31 log.Fatalln(err.Error()) 31 log.Fatalln(err.Error())
@@ -40,5 +40,5 @@ func readOverrides() *map[string]map[string]int {
40 if err != nil { 40 if err != nil {
41 log.Fatalln(err.Error()) 41 log.Fatalln(err.Error())
42 } 42 }
43 return &overrides 43 return overrides
44} 44}