package api import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "os" "sync" "time" "github.com/mr1hm/go-discord-bot/internal/config" "github.com/mr1hm/go-discord-bot/internal/tft" ) type Friends struct { Friendlies []tft.RiotAccountResponse } const ( riot_accounts = "https://americas.api.riotgames.com/riot/account/v1/accounts/by-riot-id/" riot_tft_matches = "https://americas.api.riotgames.com/tft/match/v1/matches/by-puuid/" riot_tft_match_info = "https://americas.api.riotgames.com/tft/match/v1/matches/" ) var ( InfoLogger = log.New(os.Stdout, "[ API ] INFO:\t", log.Ldate|log.Ltime) ErrLogger = log.New(os.Stdout, "[ API ] ERROR:\t", log.Ldate|log.Ltime) FriendsList = Friends{ Friendlies: []tft.RiotAccountResponse{ { GameName: "defter", Tagline: "1337", }, { GameName: "lavem", Tagline: "8996", }, { GameName: "sammypoon", Tagline: "NA1", }, // The following users are commented out since fetching data for additional users can easily exceed Riot's API rate limitations // { // Username: "Ugoff", // Tagline: "NA1", // }, // { // Username: "nukleas", // Tagline: "nukle", // }, // { // Username: "Sicario", // Tagline: "4122", // }, }, } start = time.Now() SyncData sync.Map ) func GetAccounts(ctx context.Context) { ctx, cancelCtx := context.WithCancel(ctx) accounts_ch := make(chan tft.RiotAccountResponse) go GetAccount(ctx, accounts_ch) for _, acc := range FriendsList.Friendlies { accounts_ch <- tft.RiotAccountResponse{ GameName: acc.GameName, Tagline: acc.Tagline, } } cancelCtx() } func GetAccount(ctx context.Context, accounts_ch <-chan tft.RiotAccountResponse) { for { select { case <-ctx.Done(): if err := ctx.Err(); err != nil { fmt.Printf("GetAccount error: %s\n", err) } fmt.Printf("GetAccount: finished\n") elapsed := time.Since(start) InfoLogger.Printf("Time Elapsed from GetAccount(): %v", elapsed.Milliseconds()) return case acc := <-accounts_ch: go func() { req, err := http.NewRequest(http.MethodGet, riot_accounts+acc.GameName+"/"+acc.Tagline, nil) if err != nil { ErrLogger.Printf("GetAccount() - Failed to create new request: %v", err) } req.Header.Add("X-Riot-Token", config.RiotAPIKey) resp, err := http.DefaultClient.Do(req) if err != nil { ErrLogger.Printf("GetAccount() - Request failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { ErrLogger.Printf("GetAccount() - Request status was not 200: %v", resp.Status) b, err := io.ReadAll(resp.Body) if err != nil { ErrLogger.Fatalln(err) } ErrLogger.Printf("GetAccount() - Error: %v\n", string(b)) } var resp_payload tft.RiotAccountResponse if err := json.NewDecoder(resp.Body).Decode(&resp_payload); err != nil { ErrLogger.Printf("GetAccount() - Decode failed: %v", err) } // Set new values in UserData.Data set_err := tft.UserData.Set("account", resp_payload.PUUID, resp_payload) if set_err != nil { ErrLogger.Printf("GetAccount() - Set failed: %v", set_err) } return }() } } } func GetTFTMatches(ctx context.Context) { fmt.Println("GetTFTMatches()...") ctx, cancelCtx := context.WithCancel(ctx) pid_ch := make(chan string) go GetTFTMatchIDsByPUUID(ctx, pid_ch) tft.UserData.Mtx.RLock() for puuid := range tft.UserData.Data { pid_ch <- puuid } tft.UserData.Mtx.RUnlock() cancelCtx() } func GetTFTMatchIDsByPUUID(ctx context.Context, pid_ch <-chan string) { for { select { case <-ctx.Done(): if err := ctx.Err(); err != nil { ErrLogger.Printf("GetTFTMatchIDsByPUUID() - err: %v", err) } InfoLogger.Printf("GetTFTMatchIDsByPUUID() - finished %+v", tft.UserData.Data) elapsed := time.Since(start) InfoLogger.Printf("Time Elapsed from GetTFTMatchIDsByPUUID(): %vms", elapsed.Milliseconds()) return case puuid := <-pid_ch: go func() { req, err := http.NewRequest(http.MethodGet, riot_tft_matches+string(puuid)+"/ids?start=0&count=2", nil) if err != nil { ErrLogger.Printf("GetTFTMatchIDsByPUUID() - Failed to create new request: %v", err) } req.Header.Add("X-Riot-Token", config.RiotAPIKey) resp, err := http.DefaultClient.Do(req) if err != nil { ErrLogger.Printf("GetMatchIDsByPUUID() - Request failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { ErrLogger.Printf("GetMatchIDsByPUUID() - Request failed, status was not 200: %v", resp.Status) } var resp_payload tft.RiotMatchesResponse if err := json.NewDecoder(resp.Body).Decode(&resp_payload); err != nil { ErrLogger.Printf("GetMatchIDsByPUUID() - Decode failed: %v", err) } set_err := tft.UserData.Set("matches", puuid, resp_payload) if set_err != nil { ErrLogger.Printf("GetTFTMatchIDsByPUUID() - Set failed: %v", set_err) } return }() } } } func GetTFTMatchDetails(ctx context.Context) { fmt.Println("GetTFTMatchDetails()...") ctx, cancelCtx := context.WithCancel(ctx) mid_ch := make(chan string) go GetTFTMatchDetailsByMatchID(ctx, mid_ch) match_ids := []string{} // Read from data set and loop through new slice to prevent locking for too long tft.UserData.Mtx.RLock() for puuid := range tft.UserData.Data { match_ids = append(match_ids, puuid) } tft.UserData.Mtx.RUnlock() for _, puuid := range match_ids { for _, mid := range tft.UserData.Data[puuid].Matches { mid_ch <- mid } } cancelCtx() } func GetTFTMatchDetailsByMatchID(ctx context.Context, mid_ch <-chan string) { for { select { case <-ctx.Done(): if err := ctx.Err(); err != nil { ErrLogger.Printf("GetTFTMatchDetailsByMatchID() - err: %v", err) } InfoLogger.Printf("GetTFTMatchDetailsByMatchID() - finished %+v", tft.UserData.Data) elapsed := time.Since(start) InfoLogger.Printf("Time elapsed from GetTFTMatchDetailsByMatchID(): %vms", elapsed.Milliseconds()) return case mid := <-mid_ch: go func() { req, err := http.NewRequest(http.MethodGet, riot_tft_match_info+mid, nil) if err != nil { ErrLogger.Printf("GetTFTMatchDetailsByMatchID() - Failed to create new request: %v", err) } req.Header.Add("X-Riot-Token", config.RiotAPIKey) resp, err := http.DefaultClient.Do(req) if err != nil { ErrLogger.Printf("GetTFTMatchDetailsByMatchID() - Request failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { ErrLogger.Printf("GetMatchDetailsByMatchID() - Request failed, status was not 200: %v", resp.Status) } var resp_payload tft.RiotMatchResponse if err := json.NewDecoder(resp.Body).Decode(&resp_payload); err != nil { ErrLogger.Printf("GetMatchDetailsByMatchID() - Decode failed: %v", err) } set_err := tft.UserData.Set("match_details", resp_payload.Metadata.MatchID, resp_payload) if set_err != nil { ErrLogger.Printf("GetMatchDetailsByMatchID() - Error in Set(): %v", set_err) } return }() } } }