updated deps, added golangci linter

This commit is contained in:
2022-11-20 18:11:52 +01:00
parent d10a134a34
commit 5b938f4d06
8 changed files with 231 additions and 154 deletions

View File

@@ -7,7 +7,6 @@ import (
"git.harting.dev/csgowtf/csgowtfd/utils"
"github.com/an0nfunc/go-steam/v3"
"github.com/an0nfunc/go-steam/v3/csgo/protocol/protobuf"
"github.com/an0nfunc/go-steam/v3/netutil"
"github.com/an0nfunc/go-steam/v3/protocol/gamecoordinator"
"github.com/an0nfunc/go-steam/v3/protocol/steamlang"
"github.com/go-redis/cache/v8"
@@ -34,9 +33,9 @@ type DemoMatchLoaderConfig struct {
AuthCode string
Sentry string
LoginKey string
Db *ent.Client
DB *ent.Client
Worker int
ApiKey string
APIKey string
RateLimit *rate.Limiter
Cache *cache.Cache
SprayTimeout int
@@ -48,7 +47,6 @@ type DemoMatchLoader struct {
GCReady bool
steamLogin *steam.LogOnDetails
matchRecv chan *protobuf.CMsgGCCStrike15V2_MatchList
cmList []*netutil.PortAddr
sentryFile string
loginKey string
db *ent.Client
@@ -63,18 +61,20 @@ type DemoMatchLoader struct {
LoggedIn bool
}
func AccountId2SteamId(accId uint32) uint64 {
return uint64(accId) + 76561197960265728
func AccountID2SteamID(accID uint32) uint64 {
return uint64(accID) + 76561197960265728 //nolint:gomnd
}
func SteamId2AccountId(steamId uint64) uint32 {
return uint32(steamId - 76561197960265728)
func SteamID2AccountID(steamID uint64) uint32 {
return uint32(steamID - 76561197960265728) //nolint:gomnd
}
func playerStatsFromRound(round *protobuf.CMsgGCCStrike15V2_MatchmakingServerRoundStats, p *ent.Player) (kills int32, deaths int32, assists int32, headshots int32, score int32, mvps int32) {
func playerStatsFromRound(round *protobuf.CMsgGCCStrike15V2_MatchmakingServerRoundStats, p *ent.Player) (kills int32,
deaths int32, assists int32, headshots int32, score int32, mvps int32) {
for i, acc := range round.GetReservation().GetAccountIds() {
if AccountId2SteamId(acc) == p.ID {
return round.GetKills()[i], round.GetDeaths()[i], round.GetAssists()[i], round.GetEnemyHeadshots()[i], round.GetScores()[i], round.GetMvps()[i]
if AccountID2SteamID(acc) == p.ID {
return round.GetKills()[i], round.GetDeaths()[i], round.GetAssists()[i], round.GetEnemyHeadshots()[i],
round.GetScores()[i], round.GetMvps()[i]
}
}
@@ -149,25 +149,23 @@ func (dml *DemoMatchLoader) getMatchDetails(sharecode string) (*protobuf.CMsgGCC
return nil, fmt.Errorf("gc not ready")
}
matchId, outcomeId, tokenId, err := DecodeSharecode(sharecode)
matchID, outcomeID, tokenID, err := DecodeSharecode(sharecode)
if err != nil {
return nil, err
}
err = dml.requestDemoInfo(matchId, outcomeId, uint32(tokenId))
err = dml.requestDemoInfo(matchID, outcomeID, uint32(tokenID))
if err != nil {
return nil, err
}
for {
select {
case matchDetails := <-dml.matchRecv:
if *matchDetails.Matches[0].Matchid == matchId {
return matchDetails, nil
} else {
dml.matchRecv <- matchDetails
}
for matchDetails := range dml.matchRecv {
if *matchDetails.Matches[0].Matchid == matchID {
return matchDetails, nil
} else {
dml.matchRecv <- matchDetails
}
}
return nil, err
}
func (dml *DemoMatchLoader) connectToSteam() error {
@@ -185,15 +183,15 @@ func (dml *DemoMatchLoader) connectToSteam() error {
func (dml *DemoMatchLoader) Setup(config *DemoMatchLoaderConfig) error {
dml.loginKey = config.LoginKey
dml.sentryFile = config.Sentry
dml.db = config.Db
dml.db = config.DB
dml.dp = &DemoParser{}
dml.parseMap = map[string]bool{}
dml.parseMapL = new(sync.RWMutex)
dml.cache = config.Cache
dml.connectFeedback = make(chan int, 10)
dml.connectFeedback = make(chan int, 10) //nolint:gomnd
dml.connectionWait = retry.WithCappedDuration(time.Minute*time.Duration(config.RetryTimeout), retry.NewExponential(time.Minute))
dml.connectionWaitTmpl = retry.WithCappedDuration(time.Minute*time.Duration(config.RetryTimeout), retry.NewExponential(time.Minute))
err := dml.dp.Setup(config.Db, config.Worker, config.SprayTimeout)
err := dml.dp.Setup(config.DB, config.Worker, config.SprayTimeout)
if err != nil {
return err
}
@@ -225,15 +223,15 @@ func (dml *DemoMatchLoader) Setup(config *DemoMatchLoaderConfig) error {
if err != nil {
return err
}
dml.matchRecv = make(chan *protobuf.CMsgGCCStrike15V2_MatchList, 1000)
dml.parseDemo = make(chan *Demo, 1000)
dml.matchRecv = make(chan *protobuf.CMsgGCCStrike15V2_MatchList, 1000) //nolint:gomnd
dml.parseDemo = make(chan *Demo, 1000) //nolint:gomnd
go dml.connectLoop()
go dml.steamEventHandler()
go dml.demoWorker()
for i := 0; i < config.Worker; i++ {
go dml.gcWorker(config.ApiKey, config.RateLimit)
go dml.gcWorker(config.APIKey, config.RateLimit)
}
dml.connectFeedback <- LoginFailed
@@ -258,28 +256,25 @@ func (dml *DemoMatchLoader) LoadDemo(demo *Demo) error {
}
func (dml *DemoMatchLoader) connectLoop() {
for {
select {
case res := <-dml.connectFeedback:
switch res {
case LoginFailed:
if sleep, ok := dml.connectionWait.Next(); !ok {
time.Sleep(sleep)
} else {
panic("retry should never be stop")
}
if !dml.LoggedIn {
log.Infof("[DL] Connecting to steam...")
err := dml.connectToSteam()
if err != nil {
log.Warningf("[DL] Error connecting to steam: %v", err)
}
}
case LoginSuccess:
log.Info("[DL] Steam login successfully restored")
dml.connectionWait = dml.connectionWaitTmpl
for res := range dml.connectFeedback {
switch res {
case LoginFailed:
if sleep, ok := dml.connectionWait.Next(); !ok {
time.Sleep(sleep)
} else {
panic("retry should never be stop")
}
if !dml.LoggedIn {
log.Infof("[DL] Connecting to steam...")
err := dml.connectToSteam()
if err != nil {
log.Warningf("[DL] Error connecting to steam: %v", err)
}
}
case LoginSuccess:
log.Info("[DL] Steam login successfully restored")
dml.connectionWait = dml.connectionWaitTmpl
}
}
}
@@ -355,16 +350,19 @@ func (dml *DemoMatchLoader) greetGC() {
}
}
func (dml *DemoMatchLoader) requestDemoInfo(matchId uint64, conclusionId uint64, tokenId uint32) error {
func (dml *DemoMatchLoader) requestDemoInfo(matchID, conclusionID uint64, tokenID uint32) error {
if !dml.GCReady {
return fmt.Errorf("gc not ready")
}
msg := protobuf.CMsgGCCStrike15V2_MatchListRequestFullGameInfo{Matchid: &matchId,
Outcomeid: &conclusionId,
Token: &tokenId}
msg := protobuf.CMsgGCCStrike15V2_MatchListRequestFullGameInfo{
Matchid: &matchID,
Outcomeid: &conclusionID,
Token: &tokenID,
}
dml.client.GC.Write(gamecoordinator.NewGCMsgProtobuf(APPID, uint32(protobuf.ECsgoGCMsg_k_EMsgGCCStrike15_v2_MatchListRequestFullGameInfo), &msg))
dml.client.GC.Write(gamecoordinator.NewGCMsgProtobuf(APPID,
uint32(protobuf.ECsgoGCMsg_k_EMsgGCCStrike15_v2_MatchListRequestFullGameInfo), &msg))
return nil
}
@@ -379,46 +377,46 @@ func (dml *DemoMatchLoader) handleDemo(demo *Demo, apiKey string, rl *rate.Limit
defer dml.unlockDemo(demo)
if !dml.GCReady {
log.Infof("[DL] Postponing match %d (%s): GC not ready", demo.MatchId, demo.ShareCode)
log.Infof("[DL] Postponing match %d (%s): GC not ready", demo.MatchID, demo.ShareCode)
time.Sleep(5 * time.Second)
dml.parseDemo <- demo
return nil
}
matchId, _, _, err := DecodeSharecode(demo.ShareCode)
if err != nil || matchId == 0 {
matchID, _, _, err := DecodeSharecode(demo.ShareCode)
if err != nil || matchID == 0 {
return fmt.Errorf("error decoding sharecode %s: %w", demo.ShareCode, err)
}
iMatch, err := dml.db.Match.Get(context.Background(), matchId)
iMatch, err := dml.db.Match.Get(context.Background(), matchID)
if err != nil && !ent.IsNotFound(err) {
return fmt.Errorf("error looking up match: %w", err)
} else if err == nil {
if iMatch.DemoParsed == false && iMatch.Date.After(time.Now().UTC().AddDate(0, 0, -30)) {
if !iMatch.DemoParsed && iMatch.Date.After(time.Now().UTC().AddDate(0, 0, -30)) {
log.Infof("[DL] Match %d is loaded, but not parsed. Try parsing.", iMatch.ID)
demo.MatchId = matchId
demo.Url = iMatch.ReplayURL
demo.MatchID = matchID
demo.URL = iMatch.ReplayURL
demo.DecryptionKey = iMatch.DecryptionKey
err := dml.dp.ParseDemo(demo)
if err != nil {
return fmt.Errorf("error parsing match %d: %w", demo.MatchId, err)
return fmt.Errorf("error parsing match %d: %w", demo.MatchID, err)
}
return nil
}
log.Infof("[DL] Skipped match %d: already loaded", matchId)
log.Infof("[DL] Skipped match %d: already loaded", matchID)
return nil
}
log.Infof("[DL] Requesting match %d from GC", matchId)
log.Infof("[DL] Requesting match %d from GC", matchID)
t := time.Now()
matchDetails, err := dml.getMatchDetails(demo.ShareCode)
if err != nil {
return fmt.Errorf("error getting match-details for %d: %w", demo.MatchId, err)
return fmt.Errorf("error getting match-details for %d: %w", demo.MatchID, err)
}
log.Infof("[DL] Recieved matchdetails for match %d (%s)", matchId, time.Since(t))
log.Infof("[DL] Received matchdetails for match %d (%s)", matchID, time.Since(t))
// init tx
tx, err := dml.db.Tx(context.Background())
@@ -430,17 +428,17 @@ func (dml *DemoMatchLoader) handleDemo(demo *Demo, apiKey string, rl *rate.Limit
lastRound := matchZero.GetRoundstatsall()[len(matchZero.Roundstatsall)-1]
var players []*ent.Player
for _, accountId := range lastRound.GetReservation().GetAccountIds() {
tPlayer, err := utils.Player(tx.Client(), AccountId2SteamId(accountId), apiKey, rl)
for _, accountID := range lastRound.GetReservation().GetAccountIds() {
tPlayer, err := utils.Player(tx.Client(), AccountID2SteamID(accountID), apiKey, rl)
if err != nil {
err = utils.Rollback(tx, err)
return fmt.Errorf("error getting player for steamid %d: %w", AccountId2SteamId(accountId), err)
return fmt.Errorf("error getting player for steamid %d: %w", AccountID2SteamID(accountID), err)
}
players = append(players, tPlayer)
}
demo.Url = lastRound.GetMap()
demo.MatchId = matchZero.GetMatchid()
demo.URL = lastRound.GetMap()
demo.MatchID = matchZero.GetMatchid()
demo.DecryptionKey = []byte(strings.ToUpper(fmt.Sprintf("%016x", matchZero.GetWatchablematchinfo().GetClDecryptdataKeyPub())))
tMatch, err := tx.Match.Create().
@@ -463,14 +461,14 @@ func (dml *DemoMatchLoader) handleDemo(demo *Demo, apiKey string, rl *rate.Limit
for i, mPlayer := range players {
var (
teamId int
teamID int
mk2, mk3, mk4, mk5 uint
)
if i > 4 {
teamId = 2
teamID = 2
} else {
teamId = 1
teamID = 1
}
var oldKills int32
@@ -494,7 +492,7 @@ func (dml *DemoMatchLoader) handleDemo(demo *Demo, apiKey string, rl *rate.Limit
err = tx.MatchPlayer.Create().
SetMatches(tMatch).
SetPlayers(mPlayer).
SetTeamID(teamId).
SetTeamID(teamID).
SetKills(int(kills)).
SetDeaths(int(deaths)).
SetAssists(int(assists)).
@@ -539,7 +537,7 @@ func (dml *DemoMatchLoader) handleDemo(demo *Demo, apiKey string, rl *rate.Limit
err = dml.dp.ParseDemo(demo)
if err != nil {
return fmt.Errorf("error queueing demo %d for parsing: %w", demo.MatchId, err)
return fmt.Errorf("error queueing demo %d for parsing: %w", demo.MatchID, err)
}
return nil
}

View File

@@ -8,9 +8,7 @@ import (
"errors"
"fmt"
"git.harting.dev/csgowtf/csgowtfd/ent"
"git.harting.dev/csgowtf/csgowtfd/ent/match"
"git.harting.dev/csgowtf/csgowtfd/ent/matchplayer"
"git.harting.dev/csgowtf/csgowtfd/ent/player"
"git.harting.dev/csgowtf/csgowtfd/utils"
"github.com/golang/geo/r2"
"github.com/markus-wa/demoinfocs-golang/v3/pkg/demoinfocs"
@@ -24,14 +22,13 @@ import (
type Demo struct {
ShareCode string
MatchId uint64
Url string
MatchID uint64
URL string
DecryptionKey []byte
}
type DemoParser struct {
demoQueue chan *Demo
tempDir string
db *ent.Client
sprayTimeout int
Done chan *Demo
@@ -108,8 +105,8 @@ func (s *Sprays) Avg() (avg [][]float32) {
return
}
func (dp *DemoParser) Setup(db *ent.Client, worker int, sprayTimeout int) error {
dp.demoQueue = make(chan *Demo, 1000)
func (dp *DemoParser) Setup(db *ent.Client, worker, sprayTimeout int) error {
dp.demoQueue = make(chan *Demo, 1000) //nolint:gomnd
dp.db = db
dp.sprayTimeout = sprayTimeout
dp.Done = make(chan *Demo, worker)
@@ -129,9 +126,12 @@ func (dp *DemoParser) ParseDemo(demo *Demo) error {
}
func (d *Demo) download() (io.Reader, error) {
log.Debugf("[DP] Downloading replay for %d", d.MatchId)
log.Debugf("[DP] Downloading replay for %d", d.MatchID)
r, err := http.Get(d.Url)
r, err := http.Get(d.URL)
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(r.Body)
if err != nil {
return nil, err
}
@@ -142,26 +142,13 @@ func (d *Demo) download() (io.Reader, error) {
return bzip2.NewReader(r.Body), nil
}
func (dp *DemoParser) getDBPlayer(demo *Demo, demoPlayer *common.Player) (*ent.MatchPlayer, error) {
tMatchPlayer, err := dp.db.MatchPlayer.Query().WithMatches(func(q *ent.MatchQuery) {
q.Where(match.ID(demo.MatchId))
}).WithPlayers(func(q *ent.PlayerQuery) {
q.Where(player.ID(demoPlayer.SteamID64))
}).Only(context.Background())
if err != nil {
return nil, err
}
return tMatchPlayer, nil
}
func (dp *DemoParser) MatchPlayerBySteamID(stats []*ent.MatchPlayer, steamId uint64) (*ent.MatchPlayer, error) {
func (dp *DemoParser) MatchPlayerBySteamID(stats []*ent.MatchPlayer, steamID uint64) (*ent.MatchPlayer, error) {
for _, tStats := range stats {
tPLayer, err := tStats.Edges.PlayersOrErr()
if err != nil {
return nil, fmt.Errorf("unable to get stats from statList: %w", err)
}
if tPLayer.ID == steamId {
if tPLayer.ID == steamID {
return tStats, nil
}
}
@@ -179,19 +166,14 @@ func setMatchPlayerColor(matchPlayer *ent.MatchPlayer, demoPlayer *common.Player
switch color {
case common.Yellow:
matchPlayer.Color = matchplayer.ColorYellow
break
case common.Green:
matchPlayer.Color = matchplayer.ColorGreen
break
case common.Purple:
matchPlayer.Color = matchplayer.ColorPurple
break
case common.Blue:
matchPlayer.Color = matchplayer.ColorBlue
break
case common.Orange:
matchPlayer.Color = matchplayer.ColorOrange
break
case common.Grey:
matchPlayer.Color = matchplayer.ColorGrey
}
@@ -200,7 +182,7 @@ func setMatchPlayerColor(matchPlayer *ent.MatchPlayer, demoPlayer *common.Player
func (dp *DemoParser) parseWorker() {
workloop:
for demo := range dp.demoQueue {
if demo.MatchId == 0 {
if demo.MatchID == 0 {
log.Warningf("[DP] can't parse match %s: no matchid found", demo.ShareCode)
dp.Done <- demo
continue
@@ -214,17 +196,17 @@ workloop:
return
}
tMatch, err := tx.Match.Get(context.Background(), demo.MatchId)
tMatch, err := tx.Match.Get(context.Background(), demo.MatchID)
if err != nil {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Unable to get match %d: %v", demo.MatchId, err)
log.Errorf("[DP] Unable to get match %d: %v", demo.MatchID, err)
dp.Done <- demo
continue
}
if tMatch.DemoParsed {
err = utils.Rollback(tx, err)
log.Infof("[DP] skipped already parsed %d", demo.MatchId)
_ = utils.Rollback(tx, err)
log.Infof("[DP] skipped already parsed %d", demo.MatchID)
dp.Done <- demo
continue
}
@@ -233,17 +215,17 @@ workloop:
fDemo, err := demo.download()
if err != nil {
if errors.Is(err, DemoNotFoundError{}) {
err = utils.Rollback(tx, err)
_ = utils.Rollback(tx, err)
if tMatch.Date.Before(time.Now().UTC().AddDate(0, 0, -30)) {
log.Infof("[DP] demo expired for match %d", tMatch.ID)
} else {
log.Infof("[DP] demo 404 not found for match %d. Retrying later.", demo.MatchId)
log.Infof("[DP] demo 404 not found for match %d. Retrying later.", demo.MatchID)
}
dp.Done <- demo
continue
} else {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Unable to download demo for %d: %v", demo.MatchId, err)
log.Errorf("[DP] Unable to download demo for %d: %v", demo.MatchID, err)
dp.Done <- demo
continue
}
@@ -253,7 +235,7 @@ workloop:
tStats, err := tMatch.QueryStats().WithPlayers().All(context.Background())
if err != nil {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Failed to find players for match %d: %v", demo.MatchId, err)
log.Errorf("[DP] Failed to find players for match %d: %v", demo.MatchID, err)
dp.Done <- demo
continue
}
@@ -274,7 +256,7 @@ workloop:
spays := make([]*Sprays, 0)
cfg := demoinfocs.DefaultParserConfig
if len(demo.DecryptionKey) == 16 {
if len(demo.DecryptionKey) == 16 { //nolint:gomnd
cfg.NetMessageDecryptionKey = demo.DecryptionKey
}
demoParser := demoinfocs.NewParserWithConfig(fDemo, cfg)
@@ -484,7 +466,7 @@ workloop:
Exec(context.Background())
if err != nil {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Unable to update match %d in database: %v", demo.MatchId, err)
log.Errorf("[DP] Unable to update match %d in database: %v", demo.MatchID, err)
dp.Done <- demo
continue
}
@@ -522,7 +504,13 @@ workloop:
}
for _, eqDmg := range eqMap[tMatchPlayer.PlayerStats] {
err = tx.Weapon.Create().SetStat(nMatchPLayer).SetDmg(eqDmg.Dmg).SetVictim(eqDmg.To).SetHitGroup(eqDmg.HitGroup).SetEqType(eqDmg.Eq).Exec(context.Background())
err = tx.Weapon.Create().
SetStat(nMatchPLayer).
SetDmg(eqDmg.Dmg).
SetVictim(eqDmg.To).
SetHitGroup(eqDmg.HitGroup).
SetEqType(eqDmg.Eq).
Exec(context.Background())
if err != nil {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Unable to create WeaponStat: %v", err)
@@ -532,7 +520,13 @@ workloop:
}
for _, eco := range ecoMap[tMatchPlayer.PlayerStats] {
err := tx.RoundStats.Create().SetMatchPlayer(nMatchPLayer).SetRound(uint(eco.Round)).SetBank(uint(eco.Bank)).SetEquipment(uint(eco.EqV)).SetSpent(uint(eco.Spent)).Exec(context.Background())
err := tx.RoundStats.Create().
SetMatchPlayer(nMatchPLayer).
SetRound(uint(eco.Round)).
SetBank(uint(eco.Bank)).
SetEquipment(uint(eco.EqV)).
SetSpent(uint(eco.Spent)).
Exec(context.Background())
if err != nil {
err = utils.Rollback(tx, err)
log.Errorf("[DP] Unable to create RoundStat: %v", err)
@@ -554,7 +548,11 @@ workloop:
continue workloop
}
err = tx.Spray.Create().SetMatchPlayers(nMatchPLayer).SetWeapon(spray.Weapon).SetSpray(sprayBuf.Bytes()).Exec(context.Background())
err = tx.Spray.Create().
SetMatchPlayers(nMatchPLayer).
SetWeapon(spray.Weapon).
SetSpray(sprayBuf.Bytes()).
Exec(context.Background())
if err != nil {
err = utils.Rollback(tx, err)
log.Warningf("[DP] Failure adding spray to database: %v", err)
@@ -567,7 +565,11 @@ workloop:
var bulk []*ent.MessagesCreate
for _, msg := range tMatchPlayer.Edges.Messages {
bulk = append(bulk, tx.Messages.Create().SetMessage(msg.Message).SetAllChat(msg.AllChat).SetTick(msg.Tick).SetMatchPlayer(tMatchPlayer))
bulk = append(bulk, tx.Messages.Create().
SetMessage(msg.Message).
SetAllChat(msg.AllChat).
SetTick(msg.Tick).
SetMatchPlayer(tMatchPlayer))
}
if len(bulk) > 0 {
err = tx.Messages.CreateBulk(bulk...).Exec(context.Background())
@@ -587,11 +589,11 @@ workloop:
continue
}
log.Infof("[DP] parsed match %d (took %s/%s)", demo.MatchId, downloadTime, time.Since(startTime))
log.Infof("[DP] parsed match %d (took %s/%s)", demo.MatchID, downloadTime, time.Since(startTime))
err = demoParser.Close()
if err != nil {
log.Errorf("[DP] Unable close demo file for match %d: %v", demo.MatchId, err)
log.Errorf("[DP] Unable close demo file for match %d: %v", demo.MatchID, err)
}
dp.Done <- demo
}

View File

@@ -10,9 +10,9 @@ import (
//goland:noinspection SpellCheckingInspection
var DICT = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789"
var sharecodeRexEx = regexp.MustCompile("^CSGO(?:-?\\w{5}){5}$")
var sharecodeRexEx = regexp.MustCompile(`^CSGO(?:-?\w{5}){5}$`)
func DecodeSharecode(code string) (uint64, uint64, uint16, error) {
func DecodeSharecode(code string) (matchID, outcomeID uint64, tokenID uint16, err error) {
if !sharecodeRexEx.MatchString(code) {
return 0, 0, 0, fmt.Errorf("not a CSGO sharecode: %s", code)
}
@@ -28,17 +28,17 @@ func DecodeSharecode(code string) (uint64, uint64, uint16, error) {
bigInt.Add(bigInt, big.NewInt(int64(strings.Index(DICT, c))))
}
if bigInt.BitLen() > 144 {
if bigInt.BitLen() > 144 { //nolint:gomnd
return 0, 0, 0, fmt.Errorf("invalid sharecode")
}
bytes := make([]byte, 18)
bytes := make([]byte, 18) //nolint:gomnd
bigInt.FillBytes(bytes)
matchId := binary.LittleEndian.Uint64(bytes[0:8])
outcomeId := binary.LittleEndian.Uint64(bytes[8:16])
tokenId := binary.LittleEndian.Uint16(bytes[16:18])
matchID = binary.LittleEndian.Uint64(bytes[0:8])
outcomeID = binary.LittleEndian.Uint64(bytes[8:16])
tokenID = binary.LittleEndian.Uint16(bytes[16:18])
return matchId, outcomeId, tokenId, nil
return
}
func ReverseString(numbers []string) []string {