diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 330acf2..a70192f 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -42,6 +42,7 @@ var ( {Name: "steam_updated", Type: field.TypeTime}, {Name: "sharecode_updated", Type: field.TypeTime, Nullable: true}, {Name: "auth_code", Type: field.TypeString, Nullable: true}, + {Name: "profile_created", Type: field.TypeTime, Nullable: true}, } // PlayersTable holds the schema information for the "players" table. PlayersTable = &schema.Table{ diff --git a/ent/mutation.go b/ent/mutation.go index 27047c3..33f314e 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -1320,6 +1320,7 @@ type PlayerMutation struct { steam_updated *time.Time sharecode_updated *time.Time auth_code *string + profile_created *time.Time clearedFields map[string]struct{} stats map[int]struct{} removedstats map[int]struct{} @@ -1902,6 +1903,55 @@ func (m *PlayerMutation) ResetAuthCode() { delete(m.clearedFields, player.FieldAuthCode) } +// SetProfileCreated sets the "profile_created" field. +func (m *PlayerMutation) SetProfileCreated(t time.Time) { + m.profile_created = &t +} + +// ProfileCreated returns the value of the "profile_created" field in the mutation. +func (m *PlayerMutation) ProfileCreated() (r time.Time, exists bool) { + v := m.profile_created + if v == nil { + return + } + return *v, true +} + +// OldProfileCreated returns the old "profile_created" field's value of the Player entity. +// If the Player object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlayerMutation) OldProfileCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldProfileCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldProfileCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProfileCreated: %w", err) + } + return oldValue.ProfileCreated, nil +} + +// ClearProfileCreated clears the value of the "profile_created" field. +func (m *PlayerMutation) ClearProfileCreated() { + m.profile_created = nil + m.clearedFields[player.FieldProfileCreated] = struct{}{} +} + +// ProfileCreatedCleared returns if the "profile_created" field was cleared in this mutation. +func (m *PlayerMutation) ProfileCreatedCleared() bool { + _, ok := m.clearedFields[player.FieldProfileCreated] + return ok +} + +// ResetProfileCreated resets all changes to the "profile_created" field. +func (m *PlayerMutation) ResetProfileCreated() { + m.profile_created = nil + delete(m.clearedFields, player.FieldProfileCreated) +} + // AddStatIDs adds the "stats" edge to the Stats entity by ids. func (m *PlayerMutation) AddStatIDs(ids ...int) { if m.stats == nil { @@ -2029,7 +2079,7 @@ func (m *PlayerMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *PlayerMutation) Fields() []string { - fields := make([]string, 0, 10) + fields := make([]string, 0, 11) if m.name != nil { fields = append(fields, player.FieldName) } @@ -2060,6 +2110,9 @@ func (m *PlayerMutation) Fields() []string { if m.auth_code != nil { fields = append(fields, player.FieldAuthCode) } + if m.profile_created != nil { + fields = append(fields, player.FieldProfileCreated) + } return fields } @@ -2088,6 +2141,8 @@ func (m *PlayerMutation) Field(name string) (ent.Value, bool) { return m.SharecodeUpdated() case player.FieldAuthCode: return m.AuthCode() + case player.FieldProfileCreated: + return m.ProfileCreated() } return nil, false } @@ -2117,6 +2172,8 @@ func (m *PlayerMutation) OldField(ctx context.Context, name string) (ent.Value, return m.OldSharecodeUpdated(ctx) case player.FieldAuthCode: return m.OldAuthCode(ctx) + case player.FieldProfileCreated: + return m.OldProfileCreated(ctx) } return nil, fmt.Errorf("unknown Player field %s", name) } @@ -2196,6 +2253,13 @@ func (m *PlayerMutation) SetField(name string, value ent.Value) error { } m.SetAuthCode(v) return nil + case player.FieldProfileCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProfileCreated(v) + return nil } return fmt.Errorf("unknown Player field %s", name) } @@ -2265,6 +2329,9 @@ func (m *PlayerMutation) ClearedFields() []string { if m.FieldCleared(player.FieldAuthCode) { fields = append(fields, player.FieldAuthCode) } + if m.FieldCleared(player.FieldProfileCreated) { + fields = append(fields, player.FieldProfileCreated) + } return fields } @@ -2303,6 +2370,9 @@ func (m *PlayerMutation) ClearField(name string) error { case player.FieldAuthCode: m.ClearAuthCode() return nil + case player.FieldProfileCreated: + m.ClearProfileCreated() + return nil } return fmt.Errorf("unknown Player nullable field %s", name) } @@ -2341,6 +2411,9 @@ func (m *PlayerMutation) ResetField(name string) error { case player.FieldAuthCode: m.ResetAuthCode() return nil + case player.FieldProfileCreated: + m.ResetProfileCreated() + return nil } return fmt.Errorf("unknown Player field %s", name) } diff --git a/ent/player.go b/ent/player.go index c5fa84e..6fd9e90 100644 --- a/ent/player.go +++ b/ent/player.go @@ -36,6 +36,8 @@ type Player struct { SharecodeUpdated time.Time `json:"-"` // AuthCode holds the value of the "auth_code" field. AuthCode string `json:"-"` + // ProfileCreated holds the value of the "profile_created" field. + ProfileCreated time.Time `json:"profile_created,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the PlayerQuery when eager-loading is set. Edges PlayerEdges `json:"edges"` @@ -81,7 +83,7 @@ func (*Player) scanValues(columns []string) ([]interface{}, error) { values[i] = new(sql.NullInt64) case player.FieldName, player.FieldAvatarURL, player.FieldVanityURL, player.FieldVanityURLReal, player.FieldAuthCode: values[i] = new(sql.NullString) - case player.FieldVacDate, player.FieldSteamUpdated, player.FieldSharecodeUpdated: + case player.FieldVacDate, player.FieldSteamUpdated, player.FieldSharecodeUpdated, player.FieldProfileCreated: values[i] = new(sql.NullTime) default: return nil, fmt.Errorf("unexpected column %q for type Player", columns[i]) @@ -164,6 +166,12 @@ func (pl *Player) assignValues(columns []string, values []interface{}) error { } else if value.Valid { pl.AuthCode = value.String } + case player.FieldProfileCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field profile_created", values[i]) + } else if value.Valid { + pl.ProfileCreated = value.Time + } } } return nil @@ -221,6 +229,8 @@ func (pl *Player) String() string { builder.WriteString(", sharecode_updated=") builder.WriteString(pl.SharecodeUpdated.Format(time.ANSIC)) builder.WriteString(", auth_code=") + builder.WriteString(", profile_created=") + builder.WriteString(pl.ProfileCreated.Format(time.ANSIC)) builder.WriteByte(')') return builder.String() } diff --git a/ent/player/player.go b/ent/player/player.go index 0c2354a..a6f8336 100644 --- a/ent/player/player.go +++ b/ent/player/player.go @@ -31,6 +31,8 @@ const ( FieldSharecodeUpdated = "sharecode_updated" // FieldAuthCode holds the string denoting the auth_code field in the database. FieldAuthCode = "auth_code" + // FieldProfileCreated holds the string denoting the profile_created field in the database. + FieldProfileCreated = "profile_created" // EdgeStats holds the string denoting the stats edge name in mutations. EdgeStats = "stats" // EdgeMatches holds the string denoting the matches edge name in mutations. @@ -64,6 +66,7 @@ var Columns = []string{ FieldSteamUpdated, FieldSharecodeUpdated, FieldAuthCode, + FieldProfileCreated, } var ( diff --git a/ent/player/where.go b/ent/player/where.go index 57e33f7..45b59ed 100644 --- a/ent/player/where.go +++ b/ent/player/where.go @@ -163,6 +163,13 @@ func AuthCode(v string) predicate.Player { }) } +// ProfileCreated applies equality check predicate on the "profile_created" field. It's identical to ProfileCreatedEQ. +func ProfileCreated(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldProfileCreated), v)) + }) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Player { return predicate.Player(func(s *sql.Selector) { @@ -1148,6 +1155,96 @@ func AuthCodeContainsFold(v string) predicate.Player { }) } +// ProfileCreatedEQ applies the EQ predicate on the "profile_created" field. +func ProfileCreatedEQ(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedNEQ applies the NEQ predicate on the "profile_created" field. +func ProfileCreatedNEQ(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedIn applies the In predicate on the "profile_created" field. +func ProfileCreatedIn(vs ...time.Time) predicate.Player { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Player(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldProfileCreated), v...)) + }) +} + +// ProfileCreatedNotIn applies the NotIn predicate on the "profile_created" field. +func ProfileCreatedNotIn(vs ...time.Time) predicate.Player { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Player(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldProfileCreated), v...)) + }) +} + +// ProfileCreatedGT applies the GT predicate on the "profile_created" field. +func ProfileCreatedGT(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedGTE applies the GTE predicate on the "profile_created" field. +func ProfileCreatedGTE(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedLT applies the LT predicate on the "profile_created" field. +func ProfileCreatedLT(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedLTE applies the LTE predicate on the "profile_created" field. +func ProfileCreatedLTE(v time.Time) predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldProfileCreated), v)) + }) +} + +// ProfileCreatedIsNil applies the IsNil predicate on the "profile_created" field. +func ProfileCreatedIsNil() predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldProfileCreated))) + }) +} + +// ProfileCreatedNotNil applies the NotNil predicate on the "profile_created" field. +func ProfileCreatedNotNil() predicate.Player { + return predicate.Player(func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldProfileCreated))) + }) +} + // HasStats applies the HasEdge predicate on the "stats" edge. func HasStats() predicate.Player { return predicate.Player(func(s *sql.Selector) { diff --git a/ent/player_create.go b/ent/player_create.go index 490aac5..0239bf8 100644 --- a/ent/player_create.go +++ b/ent/player_create.go @@ -162,6 +162,20 @@ func (pc *PlayerCreate) SetNillableAuthCode(s *string) *PlayerCreate { return pc } +// SetProfileCreated sets the "profile_created" field. +func (pc *PlayerCreate) SetProfileCreated(t time.Time) *PlayerCreate { + pc.mutation.SetProfileCreated(t) + return pc +} + +// SetNillableProfileCreated sets the "profile_created" field if the given value is not nil. +func (pc *PlayerCreate) SetNillableProfileCreated(t *time.Time) *PlayerCreate { + if t != nil { + pc.SetProfileCreated(*t) + } + return pc +} + // SetID sets the "id" field. func (pc *PlayerCreate) SetID(u uint64) *PlayerCreate { pc.mutation.SetID(u) @@ -400,6 +414,14 @@ func (pc *PlayerCreate) createSpec() (*Player, *sqlgraph.CreateSpec) { }) _node.AuthCode = value } + if value, ok := pc.mutation.ProfileCreated(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: player.FieldProfileCreated, + }) + _node.ProfileCreated = value + } if nodes := pc.mutation.StatsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/ent/player_update.go b/ent/player_update.go index de39def..afa4597 100644 --- a/ent/player_update.go +++ b/ent/player_update.go @@ -224,6 +224,26 @@ func (pu *PlayerUpdate) ClearAuthCode() *PlayerUpdate { return pu } +// SetProfileCreated sets the "profile_created" field. +func (pu *PlayerUpdate) SetProfileCreated(t time.Time) *PlayerUpdate { + pu.mutation.SetProfileCreated(t) + return pu +} + +// SetNillableProfileCreated sets the "profile_created" field if the given value is not nil. +func (pu *PlayerUpdate) SetNillableProfileCreated(t *time.Time) *PlayerUpdate { + if t != nil { + pu.SetProfileCreated(*t) + } + return pu +} + +// ClearProfileCreated clears the value of the "profile_created" field. +func (pu *PlayerUpdate) ClearProfileCreated() *PlayerUpdate { + pu.mutation.ClearProfileCreated() + return pu +} + // AddStatIDs adds the "stats" edge to the Stats entity by IDs. func (pu *PlayerUpdate) AddStatIDs(ids ...int) *PlayerUpdate { pu.mutation.AddStatIDs(ids...) @@ -498,6 +518,19 @@ func (pu *PlayerUpdate) sqlSave(ctx context.Context) (n int, err error) { Column: player.FieldAuthCode, }) } + if value, ok := pu.mutation.ProfileCreated(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: player.FieldProfileCreated, + }) + } + if pu.mutation.ProfileCreatedCleared() { + _spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Column: player.FieldProfileCreated, + }) + } if pu.mutation.StatsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -820,6 +853,26 @@ func (puo *PlayerUpdateOne) ClearAuthCode() *PlayerUpdateOne { return puo } +// SetProfileCreated sets the "profile_created" field. +func (puo *PlayerUpdateOne) SetProfileCreated(t time.Time) *PlayerUpdateOne { + puo.mutation.SetProfileCreated(t) + return puo +} + +// SetNillableProfileCreated sets the "profile_created" field if the given value is not nil. +func (puo *PlayerUpdateOne) SetNillableProfileCreated(t *time.Time) *PlayerUpdateOne { + if t != nil { + puo.SetProfileCreated(*t) + } + return puo +} + +// ClearProfileCreated clears the value of the "profile_created" field. +func (puo *PlayerUpdateOne) ClearProfileCreated() *PlayerUpdateOne { + puo.mutation.ClearProfileCreated() + return puo +} + // AddStatIDs adds the "stats" edge to the Stats entity by IDs. func (puo *PlayerUpdateOne) AddStatIDs(ids ...int) *PlayerUpdateOne { puo.mutation.AddStatIDs(ids...) @@ -1118,6 +1171,19 @@ func (puo *PlayerUpdateOne) sqlSave(ctx context.Context) (_node *Player, err err Column: player.FieldAuthCode, }) } + if value, ok := puo.mutation.ProfileCreated(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: player.FieldProfileCreated, + }) + } + if puo.mutation.ProfileCreatedCleared() { + _spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Column: player.FieldProfileCreated, + }) + } if puo.mutation.StatsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/ent/schema/player.go b/ent/schema/player.go index f256fdd..c5af4cf 100644 --- a/ent/schema/player.go +++ b/ent/schema/player.go @@ -28,6 +28,7 @@ func (Player) Fields() []ent.Field { }).StructTag(`json:"-"`), field.Time("sharecode_updated").Optional().StructTag(`json:"-"`), field.String("auth_code").Optional().Sensitive(), + field.Time("profile_created").Optional(), } } diff --git a/go.mod b/go.mod index 2cc3de4..9f270b8 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/go-redis/redis/v8 v8.11.4 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 + github.com/lib/pq v1.10.2 github.com/markus-wa/demoinfocs-golang/v2 v2.10.1 github.com/mattn/go-sqlite3 v1.14.8 github.com/sirupsen/logrus v1.8.1 diff --git a/go.sum b/go.sum index 650464e..b24a2f5 100644 --- a/go.sum +++ b/go.sum @@ -66,7 +66,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -94,7 +93,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -246,6 +244,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= @@ -460,7 +459,6 @@ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxT golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee h1:qlrAyYdKz4o7rWVUjiKqQJMa4PEpd55fqBU8jpsl4Iw= golang.org/x/exp v0.0.0-20210916165020-5cb4fee858ee/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= golang.org/x/exp v0.0.0-20211012155715-ffe10e552389 h1:qFfBYVpJAdBCk6Nmd7ZbcyhGmKmv8fps+OyoOfpjvu8= golang.org/x/exp v0.0.0-20211012155715-ffe10e552389/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA= @@ -553,7 +551,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -672,7 +669,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/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= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index c8d289d..77db50f 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/go-redis/redis/v8" "github.com/gorilla/handlers" "github.com/gorilla/mux" + _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" log "github.com/sirupsen/logrus" "github.com/wercker/journalhook" @@ -45,43 +46,6 @@ var ( journalLogFlag = flag.Bool("journal", false, "Log to systemd journal instead of stdout") ) -type PlayerResponse struct { - SteamID64 uint64 `json:"steamid64,string"` - Name string `json:"name"` - Avatar string `json:"avatar"` - VAC bool `json:"vac"` - VACDate time.Time `json:"vac_date,omitempty"` - Tracked bool `json:"tracked"` - VanityURL string `json:"vanity_url,omitempty"` - MatchStats utils.MatchStats `json:"match_stats,omitempty"` - Matches []*MatchResponse `json:"matches,omitempty"` -} - -type EqResponse struct { - Victim uint64 `json:"victim"` - Type int `json:"type"` - HitGroup int `json:"hit_group"` - Dmg uint `json:"dmg"` -} - -type WeaponResponse struct { - Player *PlayerResponse `json:"player"` - Eq []*EqResponse `json:"eq,omitempty"` -} - -type MatchResponse struct { - MatchId uint64 `json:"match_id,string"` - ShareCode string `json:"share_code,omitempty"` - Map string `json:"map"` - Date time.Time `json:"date"` - Score [2]int `json:"score"` - Duration int `json:"duration"` - MatchResult int `json:"match_result"` - MaxRounds int `json:"max_rounds,omitempty"` - Parsed bool `json:"parsed"` - Stats interface{} `json:"stats,omitempty"` -} - func housekeeping() { for { if !firstHK { @@ -100,9 +64,7 @@ func housekeeping() { continue } - for _, tPlayer := range tPlayerNeedSteamUpdate { - _, err = utils.UpdatePlayerFromSteam(tPlayer, conf.Steam.APIKey, db.Lock, rL) - } + _, err = utils.UpdatePlayerFromSteam(tPlayerNeedSteamUpdate, db.Client, conf.Steam.APIKey, db.Lock, rL) // getting new sharecodes if !demoLoader.GCReady { @@ -171,14 +133,14 @@ func getPlayer(w http.ResponseWriter, r *http.Request) { return } - response := PlayerResponse{ + response := utils.PlayerResponse{ SteamID64: tPlayer.ID, Name: tPlayer.Name, Avatar: tPlayer.AvatarURL, VAC: tPlayer.Vac, VanityURL: tPlayer.VanityURLReal, Tracked: tPlayer.AuthCode != "", - Matches: []*MatchResponse{}, + Matches: []*utils.MatchResponse{}, } if !tPlayer.VacDate.IsZero() { @@ -232,7 +194,7 @@ func getPlayer(w http.ResponseWriter, r *http.Request) { } for _, iMatch := range tMatches { - mResponse := &MatchResponse{ + mResponse := &utils.MatchResponse{ MatchId: iMatch.ID, Map: iMatch.Map, Date: iMatch.Date, @@ -386,7 +348,7 @@ func getMatchWeapons(w http.ResponseWriter, r *http.Request) { return } - mResponse := make([]*WeaponResponse, 0) + mResponse := make([]*utils.WeaponResponse, 0) db.Lock.RLock() tStats, err := db.Client.Stats.Query().Where(stats.HasMatchesWith(match.ID(matchId))).All(context.Background()) @@ -406,12 +368,12 @@ func getMatchWeapons(w http.ResponseWriter, r *http.Request) { continue } - mWr := &WeaponResponse{ - Player: &PlayerResponse{SteamID64: stat.PlayerStats}, + mWr := &utils.WeaponResponse{ + Player: &utils.PlayerResponse{SteamID64: stat.PlayerStats}, } for _, wr := range mWs { - mWr.Eq = append(mWr.Eq, &EqResponse{ + mWr.Eq = append(mWr.Eq, &utils.EqResponse{ Victim: wr.Victim, Type: wr.EqType, HitGroup: wr.HitGroup, @@ -453,7 +415,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) { return } - mResponse := &MatchResponse{ + mResponse := &utils.MatchResponse{ MatchId: tMatch.ID, ShareCode: tMatch.ShareCode, Map: tMatch.Map, @@ -479,7 +441,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) { for _, iStats := range tStats { sResponse := &utils.StatsResponse{ - Player: PlayerResponse{ + Player: utils.PlayerResponse{ SteamID64: iStats.Edges.Players.ID, Name: iStats.Edges.Players.Name, Avatar: iStats.Edges.Players.AvatarURL, @@ -545,7 +507,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) { if !iStats.Edges.Players.VacDate.IsZero() { switch s := sResponse.Player.(type) { - case PlayerResponse: + case utils.PlayerResponse: s.VACDate = iStats.Edges.Players.VacDate } } diff --git a/utils/utils.go b/utils/utils.go index 68d79d4..ffcd37e 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -7,7 +7,6 @@ import ( "csgowtfd/ent/player" "csgowtfd/ent/stats" "encoding/json" - "encoding/xml" "entgo.io/ent/dialect/sql" "fmt" "github.com/Philipp15b/go-steamapi" @@ -16,6 +15,7 @@ import ( "io" "io/ioutil" "net/http" + "path" "regexp" "strconv" "strings" @@ -150,6 +150,43 @@ type StatsResponse struct { KAST int `json:"kast,omitempty"` } +type PlayerResponse struct { + SteamID64 uint64 `json:"steamid64,string"` + Name string `json:"name"` + Avatar string `json:"avatar"` + VAC bool `json:"vac"` + VACDate time.Time `json:"vac_date,omitempty"` + Tracked bool `json:"tracked"` + VanityURL string `json:"vanity_url,omitempty"` + MatchStats MatchStats `json:"match_stats,omitempty"` + Matches []*MatchResponse `json:"matches,omitempty"` +} + +type EqResponse struct { + Victim uint64 `json:"victim"` + Type int `json:"type"` + HitGroup int `json:"hit_group"` + Dmg uint `json:"dmg"` +} + +type WeaponResponse struct { + Player *PlayerResponse `json:"player"` + Eq []*EqResponse `json:"eq,omitempty"` +} + +type MatchResponse struct { + MatchId uint64 `json:"match_id,string"` + ShareCode string `json:"share_code,omitempty"` + Map string `json:"map"` + Date time.Time `json:"date"` + Score [2]int `json:"score"` + Duration int `json:"duration"` + MatchResult int `json:"match_result"` + MaxRounds int `json:"max_rounds,omitempty"` + Parsed bool `json:"parsed"` + Stats interface{} `json:"stats,omitempty"` +} + const ( steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1" steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1" @@ -329,13 +366,13 @@ func GetPlayer(db *DBWithLock, id interface{}, apiKey string, rl ratelimit.Limit return GetPlayerFromSteamID64(db, steamID64, apiKey, rl) } - return GetPlayerFromVanityURL(db, e) + return GetPlayerFromVanityURL(db, e, apiKey, rl) default: return nil, fmt.Errorf("invalid arguments") } } -func GetPlayerFromVanityURL(db *DBWithLock, id string) (*ent.Player, error) { +func GetPlayerFromVanityURL(db *DBWithLock, id string, apiKey string, rl ratelimit.Limiter) (*ent.Player, error) { if id == "" { return nil, fmt.Errorf("invalid arguments") } @@ -346,28 +383,28 @@ func GetPlayerFromVanityURL(db *DBWithLock, id string) (*ent.Player, error) { if err == nil { return tPlayer, nil } else { - profile, err := SteamProfile2XML(id, 0) + rl.Take() + resp, err := steamapi.ResolveVanityURL(id, apiKey) if err != nil { return nil, err } - if profile.Error != "" { - return nil, fmt.Errorf("profile not found") + if resp.Success != 1 { + return nil, fmt.Errorf("vanity url not found") } - db.Lock.Lock() - nPlayer, err := db.Client.Player.Create().SetID(profile.SteamID64).SetVanityURL(strings.ToLower(profile.VanityURL)).SetVac(profile.VacBanned).SetAvatarURL(profile.AvatarURL).SetName(profile.ProfileName).Save(context.Background()) - db.Lock.Unlock() + nPlayer, err := GetPlayerFromSteamID64(db, resp.SteamID, apiKey, rl) 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.ID(steamID)).Only(context.Background()) + tPlayer, err := db.Client.Player.Get(context.Background(), steamID) db.Lock.RUnlock() if err == nil { return tPlayer, nil @@ -379,75 +416,78 @@ func GetPlayerFromSteamID64(db *DBWithLock, steamID uint64, apiKey string, rl ra return nil, err } - nPlayer, err = UpdatePlayerFromSteam(nPlayer, apiKey, db.Lock, rl) + uPlayer, err := UpdatePlayerFromSteam([]*ent.Player{nPlayer}, db.Client, apiKey, db.Lock, rl) if err != nil { return nil, err } - return nPlayer, nil + + if len(uPlayer) > 0 { + return uPlayer[0], nil + } else { + return nil, nil + } } } -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)) - } - if err != nil { - return nil, err - } - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(r.Body) +func UpdatePlayerFromSteam(players []*ent.Player, db *ent.Client, apiKey string, lock *sync.RWMutex, rl ratelimit.Limiter) ([]*ent.Player, error) { + var idsToUpdate []uint64 - 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.ID) - 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 + for _, updatePlayer := range players { + idsToUpdate = append(idsToUpdate, updatePlayer.ID) } rl.Take() - bans, err := steamapi.GetPlayerBans([]uint64{profile.SteamID64}, apiKey) + playerSum, err := steamapi.GetPlayerSummaries(idsToUpdate, 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)) + var nPlayers []*ent.Player + for _, pS := range playerSum { + // TODO: what happens if a player deleted their profile? + + // check for vanityURL + if SteamId64RegEx.MatchString(path.Base(pS.ProfileURL)) { + pS.ProfileURL = "" + } else { + pS.ProfileURL = path.Base(pS.ProfileURL) + } lock.Lock() - err := tPlayer.Update().SetVacCount(int(bans[0].NumberOfVACBans)).SetVacDate(banDate).Exec(context.Background()) + tPlayer, err := db.Player.UpdateOneID(pS.SteamID). + SetName(pS.PersonaName). + SetAvatarURL(pS.LargeAvatarURL). + SetSteamUpdated(time.Now().UTC()). + SetVanityURL(strings.ToLower(pS.ProfileURL)). + SetVanityURLReal(pS.ProfileURL). + SetSteamUpdated(time.Now().UTC()). + SetProfileCreated(time.Unix(pS.TimeCreated, 0).UTC()). + Save(context.Background()) lock.Unlock() if err != nil { return nil, err } + nPlayers = append(nPlayers, tPlayer) } - return tPlayer, nil + rl.Take() + bans, err := steamapi.GetPlayerBans(idsToUpdate, apiKey) + if err != nil { + return nil, err + } + + for _, ban := range bans { + if ban.NumberOfVACBans > 0 { + banDate := time.Now().UTC().AddDate(0, 0, -1*int(bans[0].DaysSinceLastBan)) + + lock.Lock() + err := db.Player.UpdateOneID(ban.SteamID).SetVacCount(int(ban.NumberOfVACBans)).SetVacDate(banDate).Exec(context.Background()) + lock.Unlock() + if err != nil { + return nil, err + } + } + } + + return nPlayers, nil }