Vladimir Hodakov
b8226d8aa8
Recent game update changed pokememes view in pokedeks, so we need to reflect it by updating parser. Introducing DataCache - a silver bullet for eliminating lags linked to database queries. Less queries, more in RAM, faster work. Needs testing in production environment.
158 lines
4.5 KiB
Go
158 lines
4.5 KiB
Go
// i2_bot – Instinct PokememBro Bot
|
||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||
|
||
package datacache
|
||
|
||
import (
|
||
"errors"
|
||
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
|
||
"strconv"
|
||
"time"
|
||
)
|
||
|
||
func (dc *DataCache) initPlayers() {
|
||
c.Log.Info("Initializing Players storage...")
|
||
dc.players = make(map[int]*dbmapping.Player)
|
||
}
|
||
|
||
func (dc *DataCache) loadPlayers() {
|
||
c.Log.Info("Load current Players data from database to DataCache...")
|
||
players := []dbmapping.Player{}
|
||
err := c.Db.Select(&players, "SELECT * FROM players")
|
||
if err != nil {
|
||
// This is critical error and we need to stop immediately!
|
||
c.Log.Fatal(err.Error())
|
||
}
|
||
|
||
dc.playersMutex.Lock()
|
||
for i := range players {
|
||
dc.players[players[i].ID] = &players[i]
|
||
}
|
||
c.Log.Info("Loaded players in DataCache: " + strconv.Itoa(len(dc.players)))
|
||
dc.playersMutex.Unlock()
|
||
}
|
||
|
||
// External functions
|
||
|
||
// AddPlayer creates new player in database
|
||
func (dc *DataCache) AddPlayer(player *dbmapping.Player) (int, error) {
|
||
c.Log.Info("DataCache: Creating new user...")
|
||
_, err := c.Db.NamedExec("INSERT INTO players VALUES(NULL, :telegram_id, :league_id, :status, :created_at, :updated_at)", &player)
|
||
if err != nil {
|
||
c.Log.Error(err.Error())
|
||
return 0, err
|
||
}
|
||
|
||
insertedPlayer := dbmapping.Player{}
|
||
err = c.Db.Get(&insertedPlayer, c.Db.Rebind("SELECT * FROM players WHERE telegram_id=:telegram_id, league_id=:league_id, status=:status, created_at=:created_at, updated_at=:updated_at"), &player)
|
||
if err != nil {
|
||
c.Log.Error(err.Error())
|
||
return 0, err
|
||
}
|
||
|
||
dc.playersMutex.Lock()
|
||
dc.players[insertedPlayer.ID] = &insertedPlayer
|
||
dc.playersMutex.Unlock()
|
||
|
||
return insertedPlayer.ID, nil
|
||
}
|
||
|
||
// GetOrCreatePlayerByTelegramID finds user by Telegram ID and creates one if not exist
|
||
func (dc *DataCache) GetOrCreatePlayerByTelegramID(telegramID int) (*dbmapping.Player, error) {
|
||
c.Log.Info("DataCache: Getting player with Telegram ID=", telegramID)
|
||
|
||
var player *dbmapping.Player
|
||
dc.playersMutex.Lock()
|
||
for i := range dc.players {
|
||
if dc.players[i].TelegramID == telegramID {
|
||
player = dc.players[i]
|
||
}
|
||
}
|
||
dc.playersMutex.Unlock()
|
||
|
||
if player == nil {
|
||
c.Log.Info("There is no such user, creating one...")
|
||
newPlayer := dbmapping.Player{}
|
||
newPlayer.TelegramID = telegramID
|
||
newPlayer.LeagueID = 0
|
||
newPlayer.Status = "nobody"
|
||
newPlayer.CreatedAt = time.Now().UTC()
|
||
newPlayer.UpdatedAt = time.Now().UTC()
|
||
|
||
newPlayerID, err := dc.AddPlayer(&newPlayer)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
player = dc.players[newPlayerID]
|
||
}
|
||
|
||
return player, nil
|
||
}
|
||
|
||
// GetPlayerByID returns player from datacache by ID
|
||
func (dc *DataCache) GetPlayerByID(playerID int) (*dbmapping.Player, error) {
|
||
c.Log.Info("DataCache: Getting player with ID=", playerID)
|
||
if dc.players[playerID] != nil {
|
||
return dc.players[playerID], nil
|
||
}
|
||
|
||
return nil, errors.New("There is no user with ID = " + strconv.Itoa(playerID))
|
||
}
|
||
|
||
// GetPlayerByTelegramID returns player with such Telegram ID
|
||
func (dc *DataCache) GetPlayerByTelegramID(telegramID int) (*dbmapping.Player, error) {
|
||
c.Log.Info("DataCache: Getting player with Telegram ID=", telegramID)
|
||
|
||
var player *dbmapping.Player
|
||
dc.playersMutex.Lock()
|
||
for i := range dc.players {
|
||
if dc.players[i].TelegramID == telegramID {
|
||
player = dc.players[i]
|
||
}
|
||
}
|
||
dc.playersMutex.Unlock()
|
||
|
||
if player != nil {
|
||
return player, nil
|
||
}
|
||
|
||
return nil, errors.New("There is no user with Telegram ID = " + strconv.Itoa(telegramID))
|
||
}
|
||
|
||
// UpdatePlayerFields writes new fields to player
|
||
func (dc *DataCache) UpdatePlayerFields(player *dbmapping.Player) (*dbmapping.Player, error) {
|
||
if dc.players[player.ID] != nil {
|
||
_, err := c.Db.NamedExec("UPDATE `players` SET league_id=:league_id, status=:status WHERE id=:id", player)
|
||
if err != nil {
|
||
c.Log.Error(err.Error())
|
||
return dc.players[player.ID], err
|
||
}
|
||
dc.playersMutex.Lock()
|
||
dc.players[player.ID].LeagueID = player.LeagueID
|
||
dc.players[player.ID].Status = player.Status
|
||
dc.playersMutex.Unlock()
|
||
return dc.players[player.ID], nil
|
||
}
|
||
|
||
return nil, errors.New("There is no user with ID = " + strconv.Itoa(player.ID))
|
||
}
|
||
|
||
// UpdatePlayerTimestamp writes current update time to player
|
||
func (dc *DataCache) UpdatePlayerTimestamp(playerID int) error {
|
||
if dc.players[playerID] != nil {
|
||
dc.playersMutex.Lock()
|
||
dc.players[playerID].UpdatedAt = time.Now().UTC()
|
||
_, err := c.Db.NamedExec("UPDATE `players` SET updated_at=:updated_at WHERE id=:id", dc.players[playerID])
|
||
if err != nil {
|
||
c.Log.Error(err.Error())
|
||
dc.playersMutex.Unlock()
|
||
return err
|
||
}
|
||
dc.playersMutex.Unlock()
|
||
return nil
|
||
}
|
||
|
||
return errors.New("There is no user with ID = " + strconv.Itoa(playerID))
|
||
}
|