import from unified repo
This commit is contained in:
410
utils/utils.go
Normal file
410
utils/utils.go
Normal file
@@ -0,0 +1,410 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"csgowtfd/csgo"
|
||||
"csgowtfd/ent"
|
||||
"csgowtfd/ent/match"
|
||||
"csgowtfd/ent/player"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/Philipp15b/go-steamapi"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.uber.org/ratelimit"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Conf struct {
|
||||
Logging struct {
|
||||
Level string
|
||||
}
|
||||
Steam struct {
|
||||
Username string
|
||||
APIKey string `yaml:"api_key"`
|
||||
RatePerSecond int `yaml:"rate_per_sec"`
|
||||
}
|
||||
}
|
||||
|
||||
type DBWithLock struct {
|
||||
Client *ent.Client
|
||||
Lock *sync.RWMutex
|
||||
}
|
||||
|
||||
type CommunityXML struct {
|
||||
SteamID64 uint64 `xml:"steamID64"`
|
||||
AvatarURL string `xml:"avatarFull"`
|
||||
VacBanned bool `xml:"vacBanned"`
|
||||
ProfileName string `xml:"steamID"`
|
||||
Error string `xml:"error"`
|
||||
VanityURL string `xml:"customURL"`
|
||||
}
|
||||
|
||||
type shareCodeResponse struct {
|
||||
Result struct {
|
||||
Code string `json:"nextcode"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
const (
|
||||
steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1"
|
||||
steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1"
|
||||
shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s"
|
||||
)
|
||||
|
||||
var (
|
||||
SteamId64RegEx = regexp.MustCompile(`^\d{17}$`)
|
||||
ShareCodeRegEx = regexp.MustCompile(`^CSGO(?:-?[ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789]{5}){5}$`)
|
||||
AuthCodeRegEx = regexp.MustCompile(`^[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{5}-[ABCDEFGHJKLMNOPQRSTUVWXYZ23456789]{4}$`)
|
||||
)
|
||||
|
||||
func SendJSON(data interface{}, w http.ResponseWriter) error {
|
||||
playerJson, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = w.Write(playerJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsAuthCodeValid(player *ent.Player, lock *sync.RWMutex, apiKey string, shareCode string, authCode string, rl ratelimit.Limiter) (bool, error) {
|
||||
var tMatch *ent.Match
|
||||
var err error
|
||||
if shareCode == "" {
|
||||
lock.RLock()
|
||||
tMatch, err = player.QueryMatches().Order(ent.Desc(match.FieldDate)).First(context.Background())
|
||||
lock.RUnlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err := getNextShareCode(tMatch.ShareCode, apiKey, authCode, player.Steamid, rl)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
_, err := getNextShareCode(shareCode, apiKey, authCode, player.Steamid, rl)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetNewShareCodesForPlayer(player *ent.Player, lock *sync.RWMutex, apiKey string, rl ratelimit.Limiter) ([]string, error) {
|
||||
lock.RLock()
|
||||
tMatch, err := player.QueryMatches().Order(ent.Desc(match.FieldDate)).First(context.Background())
|
||||
lock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rCodes []string
|
||||
newShareCode, err := getNextShareCode(tMatch.ShareCode, apiKey, player.AuthCode, player.Steamid, rl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for newShareCode != "n/a" {
|
||||
rCodes = append(rCodes, newShareCode)
|
||||
newShareCode, err = getNextShareCode(rCodes[len(rCodes)-1], apiKey, player.AuthCode, player.Steamid, rl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
err = player.Update().SetSharecodeUpdated(time.Now().UTC()).Exec(context.Background())
|
||||
lock.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rCodes, nil
|
||||
}
|
||||
|
||||
func getNextShareCode(lastCode string, apiKey string, authCode string, steamId uint64, rl ratelimit.Limiter) (string, error) {
|
||||
if lastCode == "" || apiKey == "" || authCode == "" || steamId == 0 {
|
||||
return "", fmt.Errorf("invalid arguments")
|
||||
}
|
||||
|
||||
rl.Take()
|
||||
log.Debugf("[SC] STEAMPI with %s", fmt.Sprintf(shareCodeURLEntry, apiKey, steamId, authCode, lastCode))
|
||||
r, err := http.Get(fmt.Sprintf(shareCodeURLEntry, apiKey, steamId, authCode, lastCode))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if r.StatusCode == 202 {
|
||||
return "n/a", nil
|
||||
} else if r.StatusCode != 200 {
|
||||
return "", fmt.Errorf("bad response from steam api (HTTP%d)", r.StatusCode)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
bJson, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rJson := new(shareCodeResponse)
|
||||
err = json.Unmarshal(bJson, rJson)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rJson.Result.Code, nil
|
||||
}
|
||||
|
||||
func GetPlayer(db *DBWithLock, id interface{}, apiKey string, rl ratelimit.Limiter) (*ent.Player, error) {
|
||||
switch e := id.(type) {
|
||||
case uint64:
|
||||
return GetPlayerFromSteamID64(db, e, apiKey, rl)
|
||||
case string:
|
||||
if SteamId64RegEx.MatchString(e) {
|
||||
steamID64, err := strconv.ParseUint(e, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return GetPlayerFromSteamID64(db, steamID64, apiKey, rl)
|
||||
}
|
||||
|
||||
return GetPlayerFromVanityURL(db, e)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid arguments")
|
||||
}
|
||||
}
|
||||
|
||||
func GetPlayerFromVanityURL(db *DBWithLock, id string) (*ent.Player, error) {
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("invalid arguments")
|
||||
}
|
||||
|
||||
db.Lock.RLock()
|
||||
tPlayer, err := db.Client.Player.Query().Where(player.VanityURL(strings.ToLower(id))).Only(context.Background())
|
||||
db.Lock.RUnlock()
|
||||
if err == nil {
|
||||
return tPlayer, nil
|
||||
} else {
|
||||
profile, err := SteamProfile2XML(id, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if profile.Error != "" {
|
||||
return nil, fmt.Errorf("profile not found")
|
||||
}
|
||||
|
||||
db.Lock.Lock()
|
||||
nPlayer, err := db.Client.Player.Create().SetSteamid(profile.SteamID64).SetVanityURL(strings.ToLower(profile.VanityURL)).SetVac(profile.VacBanned).SetAvatarURL(profile.AvatarURL).SetName(profile.ProfileName).Save(context.Background())
|
||||
db.Lock.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nPlayer, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetPlayerFromSteamID64(db *DBWithLock, steamID uint64, apiKey string, rl ratelimit.Limiter) (*ent.Player, error) {
|
||||
db.Lock.RLock()
|
||||
tPlayer, err := db.Client.Player.Query().Where(player.Steamid(steamID)).Only(context.Background())
|
||||
db.Lock.RUnlock()
|
||||
if err == nil {
|
||||
return tPlayer, nil
|
||||
} else {
|
||||
db.Lock.Lock()
|
||||
nPlayer, err := db.Client.Player.Create().SetSteamid(steamID).Save(context.Background())
|
||||
db.Lock.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nPlayer, err = UpdatePlayerFromSteam(nPlayer, apiKey, db.Lock, rl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nPlayer, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GCInfoParser(channel chan *csgo.Demo, dl *csgo.DemoMatchLoader, dp *csgo.DemoParser, db *DBWithLock, apiKey string, rl ratelimit.Limiter) {
|
||||
for {
|
||||
select {
|
||||
case demo := <-channel:
|
||||
if !dl.GCReady {
|
||||
time.Sleep(5 * time.Second)
|
||||
channel <- demo
|
||||
}
|
||||
|
||||
matchId, _, _, err := csgo.DecodeSharecode(demo.ShareCode)
|
||||
Check(err)
|
||||
if matchId == 0 {
|
||||
log.Warningf("Can't parse match with sharecode %s", demo.ShareCode)
|
||||
continue
|
||||
}
|
||||
|
||||
db.Lock.RLock()
|
||||
iMatch, err := db.Client.Match.Query().Where(match.MatchID(matchId)).Only(context.Background())
|
||||
db.Lock.RUnlock()
|
||||
if err != nil {
|
||||
switch e := err.(type) {
|
||||
case *ent.NotFoundError:
|
||||
break
|
||||
default:
|
||||
Check(e)
|
||||
}
|
||||
} else {
|
||||
if iMatch.DemoParsed == false && !iMatch.DemoExpired {
|
||||
log.Infof("Match %d is loaded, but not parsed. Try parsing.", demo.MatchId)
|
||||
demo.MatchId = matchId
|
||||
demo.Url = iMatch.ReplayURL
|
||||
dp.ParseDemo(demo)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Skipped match %d: already parsed", matchId)
|
||||
continue
|
||||
}
|
||||
|
||||
matchDetails, err := dl.GetMatchDetails(demo.ShareCode)
|
||||
Check(err)
|
||||
matchZero := matchDetails.GetMatches()[0]
|
||||
lastRound := matchZero.GetRoundstatsall()[len(matchZero.Roundstatsall)-1]
|
||||
var players []*ent.Player
|
||||
|
||||
for _, accountId := range lastRound.GetReservation().GetAccountIds() {
|
||||
tPlayer, err := GetPlayer(db, csgo.AccountId2SteamId(accountId), apiKey, rl)
|
||||
Check(err)
|
||||
players = append(players, tPlayer)
|
||||
}
|
||||
|
||||
demo.Url = lastRound.GetMap()
|
||||
demo.MatchId = matchZero.GetMatchid()
|
||||
|
||||
db.Lock.Lock()
|
||||
tMatch, err := db.Client.Match.Create().
|
||||
SetMatchID(matchZero.GetMatchid()).
|
||||
AddPlayers(players...).
|
||||
SetDate(time.Unix(int64(matchZero.GetMatchtime()), 0).UTC()).
|
||||
SetMaxRounds(int(lastRound.GetMaxRounds())).
|
||||
SetDuration(int(lastRound.GetMatchDuration())).
|
||||
SetShareCode(demo.ShareCode).
|
||||
SetReplayURL(lastRound.GetMap()).
|
||||
SetScoreTeamA(int(lastRound.GetTeamScores()[0])).
|
||||
SetScoreTeamB(int(lastRound.GetTeamScores()[1])).
|
||||
SetMatchResult(int(lastRound.GetMatchResult())).
|
||||
Save(context.Background())
|
||||
db.Lock.Unlock()
|
||||
Check(err)
|
||||
|
||||
for i, mPlayer := range players {
|
||||
var teamId int
|
||||
if i > 4 {
|
||||
teamId = 2
|
||||
} else {
|
||||
teamId = 1
|
||||
}
|
||||
db.Lock.Lock()
|
||||
err := db.Client.Stats.Create().
|
||||
SetMatches(tMatch).
|
||||
SetPlayers(mPlayer).
|
||||
SetTeamID(teamId).
|
||||
SetKills(int(lastRound.GetKills()[i])).
|
||||
SetDeaths(int(lastRound.GetDeaths()[i])).
|
||||
SetAssists(int(lastRound.GetAssists()[i])).
|
||||
SetMvp(int(lastRound.GetMvps()[i])).
|
||||
SetScore(int(lastRound.GetScores()[i])).
|
||||
SetHeadshot(int(lastRound.GetEnemyHeadshots()[i])).
|
||||
Exec(context.Background())
|
||||
db.Lock.Unlock()
|
||||
Check(err)
|
||||
}
|
||||
|
||||
dp.ParseDemo(demo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SteamProfile2XML(id string, steamID64 uint64) (*CommunityXML, error) {
|
||||
var r *http.Response
|
||||
var err error
|
||||
if steamID64 != 0 {
|
||||
r, err = http.Get(fmt.Sprintf(steamID64Entry, steamID64))
|
||||
} else {
|
||||
r, err = http.Get(fmt.Sprintf(steamVanityURLEntry, id))
|
||||
}
|
||||
Check(err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
Check(err)
|
||||
}(r.Body)
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cXML := &CommunityXML{}
|
||||
err = xml.Unmarshal(body, cXML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cXML, nil
|
||||
}
|
||||
|
||||
func UpdatePlayerFromSteam(player *ent.Player, apiKey string, lock *sync.RWMutex, rl ratelimit.Limiter) (*ent.Player, error) {
|
||||
profile, err := SteamProfile2XML("", player.Steamid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if profile.Error != "" {
|
||||
return nil, fmt.Errorf("profile not found")
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
tPlayer, err := player.Update().SetName(profile.ProfileName).SetVac(profile.VacBanned).SetAvatarURL(profile.AvatarURL).SetSteamUpdated(time.Now().UTC()).SetVanityURL(strings.ToLower(profile.VanityURL)).SetVanityURLReal(profile.VanityURL).SetSteamUpdated(time.Now().UTC()).Save(context.Background())
|
||||
lock.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rl.Take()
|
||||
bans, err := steamapi.GetPlayerBans([]uint64{profile.SteamID64}, apiKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(bans) > 0 && bans[0].NumberOfVACBans > 0 {
|
||||
banDate := time.Now().UTC().AddDate(0, 0, -1*int(bans[0].DaysSinceLastBan))
|
||||
|
||||
lock.Lock()
|
||||
err := tPlayer.Update().SetVacCount(int(bans[0].NumberOfVACBans)).SetVacDate(banDate).Exec(context.Background())
|
||||
lock.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return tPlayer, nil
|
||||
}
|
||||
|
||||
func Check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user