package db import ( "context" "github.com/pkg/errors" "github.com/uptrace/bun" ) type Player struct { bun.BaseModel `bun:"table:players,alias:p"` ID int `bun:"id,pk,autoincrement" json:"id"` SlapID *uint32 `bun:"slap_id,unique" json:"slap_id"` DiscordID string `bun:"discord_id,unique,notnull" json:"discord_id"` UserID *int `bun:"user_id,unique" json:"user_id"` Name string `bun:"name,notnull" json:"name"` User *User `bun:"rel:belongs-to,join:user_id=id" json:"-"` } func (p *Player) DisplayName() string { if p.User != nil { return p.User.Username } return p.Name } // NewPlayer creates a new player in the database. If there is an existing user with the same // discordID, it will automatically link that user to the player func NewPlayer(ctx context.Context, tx bun.Tx, name, discordID string, audit *AuditMeta) (*Player, error) { player := &Player{DiscordID: discordID, Name: name} user, err := GetUserByDiscordID(ctx, tx, discordID) if err != nil && !IsBadRequest(err) { return nil, errors.Wrap(err, "GetUserByDiscordID") } if user != nil { player.UserID = &user.ID player.Name = user.Username } err = Insert(tx, player). WithAudit(audit, nil).Exec(ctx) if err != nil { return nil, errors.Wrap(err, "Insert") } return player, nil } func NewPlayerFromLog(ctx context.Context, tx bun.Tx, name string, slapID uint32, audit *AuditMeta) (*Player, error) { player := &Player{Name: name, SlapID: &slapID} err := Insert(tx, player). WithAudit(audit, nil).Exec(ctx) if err != nil { return nil, errors.Wrap(err, "Insert") } return player, nil } // ConnectPlayer links the user to an existing player, or creates a new player to link if not found // Populates User.Player on success func (u *User) ConnectPlayer(ctx context.Context, tx bun.Tx, audit *AuditMeta) error { player, err := GetByField[Player](tx, "p.discord_id", u.DiscordID). Relation("User").Get(ctx) if err != nil { if !IsBadRequest(err) { // Unexpected error occured return errors.Wrap(err, "GetByField") } // Player doesn't exist, create a new one player, err = NewPlayer(ctx, tx, u.Username, u.DiscordID, audit) if err != nil { return errors.Wrap(err, "NewPlayer") } // New player should automatically get linked to the user u.Player = player return nil } // Player was found if player.UserID != nil { if player.UserID == &u.ID { return nil } return errors.New("player with that discord_id already linked to a user") } player.UserID = &u.ID err = UpdateByID(tx, player.ID, player).Column("user_id").Exec(ctx) if err != nil { return errors.Wrap(err, "UpdateByID") } u.Player = player return nil } func GetPlayer(ctx context.Context, tx bun.Tx, playerID int) (*Player, error) { return GetByID[Player](tx, playerID).Relation("User").Get(ctx) } func UpdatePlayerSlapID(ctx context.Context, tx bun.Tx, playerID int, slapID uint32, audit *AuditMeta) error { player, err := GetPlayer(ctx, tx, playerID) if err != nil { return errors.Wrap(err, "GetPlayer") } player.SlapID = &slapID err = UpdateByID(tx, player.ID, player).Column("slap_id"). WithAudit(audit, nil).Exec(ctx) if err != nil { return errors.Wrap(err, "UpdateByID") } return nil } func GetPlayersNotOnTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID int) ([]*Player, error) { players, err := GetList[Player](tx).Relation("User"). Join("LEFT JOIN team_rosters tr ON tr.player_id = p.id"). Where("NOT (tr.season_id = ? and tr.league_id = ?) OR (tr.season_id IS NULL and tr.league_id IS NULL)", seasonID, leagueID). Order("p.name ASC"). GetAll(ctx) if err != nil { return nil, errors.Wrap(err, "GetList") } return players, nil }