Archived
1

DataCache and changes for game update

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.
This commit is contained in:
Vladimir Hodakov 2018-01-29 23:50:25 +04:00
parent 074fc4a1e3
commit b8226d8aa8
36 changed files with 1294 additions and 700 deletions

61
Gopkg.lock generated
View File

@ -1,6 +1,18 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "bitbucket.org/pztrn/flagger"
packages = ["."]
revision = "c9b4726e1787adb01126cdf9a7932e2bccbb51a7"
[[projects]]
branch = "master"
name = "bitbucket.org/pztrn/mogrus"
packages = ["."]
revision = "2e3a9d38c8e4b0036f714f88ef0fe9fc14b4c6b8"
[[projects]]
name = "github.com/go-sql-driver/mysql"
packages = ["."]
@ -16,8 +28,11 @@
[[projects]]
branch = "master"
name = "github.com/jmoiron/sqlx"
packages = [".","reflectx"]
revision = "3379e5993990b1f927fc8db926485e6f6becf2d2"
packages = [
".",
"reflectx"
]
revision = "05cef0741ade10ca668982355b3f3f0bcf0ff0a8"
[[projects]]
name = "github.com/pressly/goose"
@ -34,8 +49,8 @@
[[projects]]
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e"
version = "v1.0.3"
revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
version = "v1.0.4"
[[projects]]
name = "github.com/technoweenie/multipartstreamer"
@ -47,35 +62,39 @@
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94"
revision = "0efb9460aaf800c6376acf625be2853bceac2e06"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
revision = "665f6529cca930e27b831a0d1dafffbe1c172924"
packages = [
"unix",
"windows"
]
revision = "ff2a66f350cefa5c93a634eadb5d25bb60c85a9c"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm"
]
revision = "e19ae1496984b1c655b8044a65c0300a3c878dd3"
[[projects]]
branch = "v2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
[[projects]]
branch = "master"
name = "lab.pztrn.name/golibs/flagger"
packages = ["."]
revision = "5cf6a6dd8fe8a4f37d1bb1e3deb5121c6e923668"
[[projects]]
branch = "master"
name = "lab.pztrn.name/golibs/mogrus"
packages = ["."]
revision = "a888e29e1fff06c2e6ebc607055a02de22a6ddae"
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e26a19cb9a3e96a96b1d51599ead99341edc2278667ddf8a7dac4cd197fbe09d"
inputs-digest = "99811036e66782056cb66f37e4a2570ac81f0e3da2fe399c4d54d83acc5a368d"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,4 +1,3 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
@ -17,16 +16,51 @@
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
required = ["github.com/go-sql-driver/mysql", "github.com/jmoiron/sqlx", "github.com/pressly/goose"]
[[constraint]]
branch = "master"
name = "bitbucket.org/pztrn/flagger"
[[constraint]]
branch = "master"
name = "bitbucket.org/pztrn/mogrus"
[[constraint]]
name = "github.com/go-sql-driver/mysql"
version = "1.3.0"
[[constraint]]
name = "github.com/go-telegram-bot-api/telegram-bot-api"
version = "4.6.0"
version = "4.6.1"
[[constraint]]
branch = "master"
name = "github.com/jmoiron/sqlx"
[[constraint]]
name = "github.com/pressly/goose"
version = "2.1.0"
[[constraint]]
name = "github.com/robfig/cron"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "golang.org/x/text"
[[constraint]]
branch = "v2"
name = "gopkg.in/yaml.v2"
[prune]
go-tests = true
unused-packages = true

View File

@ -4,10 +4,10 @@
package main
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/appcontext"
"git.wtfteam.pro/fat0troll/i2_bot/lib/broadcaster"
"git.wtfteam.pro/fat0troll/i2_bot/lib/chatter"
"git.wtfteam.pro/fat0troll/i2_bot/lib/datacache"
"git.wtfteam.pro/fat0troll/i2_bot/lib/forwarder"
"git.wtfteam.pro/fat0troll/i2_bot/lib/migrations"
"git.wtfteam.pro/fat0troll/i2_bot/lib/orders"
@ -20,6 +20,7 @@ import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/talkers"
"git.wtfteam.pro/fat0troll/i2_bot/lib/users"
"git.wtfteam.pro/fat0troll/i2_bot/lib/welcomer"
"github.com/go-telegram-bot-api/telegram-bot-api"
"time"
)
@ -34,6 +35,7 @@ func main() {
router.New(c)
migrations.New(c)
c.RunDatabaseMigrations()
datacache.New(c)
forwarder.New(c)
pokedexer.New(c)
pinner.New(c)

View File

@ -4,13 +4,13 @@
package appcontext
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/jmoiron/sqlx"
"github.com/robfig/cron"
"bitbucket.org/pztrn/flagger"
"bitbucket.org/pztrn/mogrus"
"git.wtfteam.pro/fat0troll/i2_bot/lib/broadcaster/broadcasterinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/chatter/chatterinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/config"
"git.wtfteam.pro/fat0troll/i2_bot/lib/connections"
"git.wtfteam.pro/fat0troll/i2_bot/lib/datacache/datacacheinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/forwarder/forwarderinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/migrations/migrationsinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/orders/ordersinterface"
@ -23,8 +23,9 @@ import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/talkers/talkersinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/users/usersinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/welcomer/welcomerinterface"
"lab.pztrn.name/golibs/flagger"
"lab.pztrn.name/golibs/mogrus"
"github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/jmoiron/sqlx"
"github.com/robfig/cron"
"os"
)
@ -35,6 +36,7 @@ type Context struct {
Cron *cron.Cron
Log *mogrus.LoggerHandler
Bot *tgbotapi.BotAPI
DataCache datacacheinterface.DataCacheInterface
Forwarder forwarderinterface.ForwarderInterface
Migrations migrationsinterface.MigrationsInterface
Router routerinterface.RouterInterface
@ -157,6 +159,12 @@ func (c *Context) RegisterChatterInterface(ci chatterinterface.ChatterInterface)
c.Chatter.Init()
}
// RegisterDataCacheInterface registers datacache interface in application
func (c *Context) RegisterDataCacheInterface(di datacacheinterface.DataCacheInterface) {
c.DataCache = di
c.DataCache.Init()
}
// RegisterSquaderInterface registers squader interface in application
func (c *Context) RegisterSquaderInterface(si squaderinterface.SquaderInterface) {
c.Squader = si

View File

@ -4,8 +4,8 @@
package chatter
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"strconv"
"strings"
)
@ -31,12 +31,13 @@ func (ct *Chatter) userPrivilegesCheck(update *tgbotapi.Update, user *tgbotapi.U
}
}
playerRaw, ok := c.Users.GetOrCreatePlayer(user.ID)
if !ok {
playerRaw, err := c.DataCache.GetPlayerByTelegramID(user.ID)
if err != nil {
c.Log.Error(err.Error())
return false
}
if c.Users.PlayerBetterThan(&playerRaw, "admin") {
if c.Users.PlayerBetterThan(playerRaw, "admin") {
return true
}
@ -51,7 +52,7 @@ func (ct *Chatter) userPrivilegesCheck(update *tgbotapi.Update, user *tgbotapi.U
return true
}
default:
availableChatsForUser, _ := c.Squader.GetAvailableSquadChatsForUser(&playerRaw)
availableChatsForUser, _ := c.Squader.GetAvailableSquadChatsForUser(playerRaw)
for i := range availableChatsForUser {
if update.Message.Chat.ID == availableChatsForUser[i].TelegramID {
return true

View File

@ -4,9 +4,9 @@
package config
import (
"bitbucket.org/pztrn/mogrus"
"gopkg.in/yaml.v2"
"io/ioutil"
"lab.pztrn.name/golibs/mogrus"
"path/filepath"
)

View File

@ -4,11 +4,11 @@
package connections
import (
"bitbucket.org/pztrn/mogrus"
"git.wtfteam.pro/fat0troll/i2_bot/lib/config"
_ "github.com/go-sql-driver/mysql"
"github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/jmoiron/sqlx"
"git.wtfteam.pro/fat0troll/i2_bot/lib/config"
"lab.pztrn.name/golibs/mogrus"
)
// BotInit initializes connection to Telegram

View File

@ -0,0 +1,34 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacacheinterface
import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
)
// DataCacheInterface implements DataCache for importing via appcontext.
type DataCacheInterface interface {
Init()
AddPlayer(player *dbmapping.Player) (int, error)
GetOrCreatePlayerByTelegramID(telegramID int) (*dbmapping.Player, error)
GetPlayerByID(playerID int) (*dbmapping.Player, error)
GetPlayerByTelegramID(telegramID int) (*dbmapping.Player, error)
UpdatePlayerFields(player *dbmapping.Player) (*dbmapping.Player, error)
UpdatePlayerTimestamp(playerID int) error
AddProfile(profile *dbmapping.Profile) (int, error)
GetPlayersWithCurrentProfiles() map[int]*dbmapping.PlayerProfile
GetProfileByID(profileID int) (*dbmapping.Profile, error)
GetProfileByPlayerID(playerID int) (*dbmapping.Profile, error)
AddPokememe(pokememeData map[string]string, pokememeLocations map[string]string, pokememeElements map[string]string) (int, error)
GetAllPokememes() map[int]*dbmapping.PokememeFull
GetPokememeByID(pokememeID int) (*dbmapping.PokememeFull, error)
GetPokememeByName(name string) (*dbmapping.PokememeFull, error)
DeletePokememeByID(pokememeID int) error
GetLeagueBySymbol(symbol string) (*dbmapping.League, error)
GetWeaponTypeByName(name string) (*dbmapping.Weapon, error)
}

45
lib/datacache/elements.go Normal file
View File

@ -0,0 +1,45 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
func (dc *DataCache) initElements() {
c.Log.Info("Initializing Elements storage...")
dc.elements = make(map[int]*dbmapping.Element)
}
func (dc *DataCache) loadElements() {
c.Log.Info("Load current Elements data from database to DataCache...")
elements := []dbmapping.Element{}
err := c.Db.Select(&elements, "SELECT * FROM elements")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
dc.elementsMutex.Lock()
for i := range elements {
dc.elements[elements[i].ID] = &elements[i]
}
c.Log.Info("Loaded elements in DataCache: " + strconv.Itoa(len(dc.elements)))
dc.elementsMutex.Unlock()
}
func (dc *DataCache) findElementIDBySymbol(symbol string) (int, error) {
dc.elementsMutex.Lock()
for i := range dc.elements {
if dc.elements[i].Symbol == symbol {
dc.elementsMutex.Unlock()
return i, nil
}
}
dc.elementsMutex.Unlock()
return 0, errors.New("There is no element with symbol = " + symbol)
}

76
lib/datacache/exported.go Normal file
View File

@ -0,0 +1,76 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/appcontext"
"git.wtfteam.pro/fat0troll/i2_bot/lib/datacache/datacacheinterface"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"sync"
)
var (
c *appcontext.Context
)
// DataCache is a function-handling struct for package datacache.
// Also, it's a data cache: it handles all data, powered by DataCache functions.
type DataCache struct {
// Players — users of bot
players map[int]*dbmapping.Player
playersMutex sync.Mutex
// Profiles - game profiles, no matter, actual or not
profiles map[int]*dbmapping.Profile
profilesMutex sync.Mutex
// Current profiles - actual profiles for players, mostly used by bot
// Note: int in this array for player ID, not for profile ID
currentProfiles map[int]*dbmapping.Profile
currentProfilesMutex sync.Mutex
// Pokememes
pokememes map[int]*dbmapping.Pokememe
pokememesMutex sync.Mutex
// Pokememes with all supported data
fullPokememes map[int]*dbmapping.PokememeFull
fullPokememesMutex sync.Mutex
// Elements
elements map[int]*dbmapping.Element
elementsMutex sync.Mutex
// Leagues
leagues map[int]*dbmapping.League
leaguesMutex sync.Mutex
// Locations
locations map[int]*dbmapping.Location
locationsMutex sync.Mutex
// Weapons
weapons map[int]*dbmapping.Weapon
weaponsMutex sync.Mutex
}
// New is an initialization function for appcontext
func New(ac *appcontext.Context) {
c = ac
dc := &DataCache{}
c.RegisterDataCacheInterface(datacacheinterface.DataCacheInterface(dc))
}
// Init is a initialization function for package
func (dc *DataCache) Init() {
c.Log.Info("Initializing DataCache...")
dc.initElements()
dc.loadElements()
dc.initLeagues()
dc.loadLeagues()
dc.initLocations()
dc.loadLocations()
dc.initWeapons()
dc.loadWeapons()
dc.initPokememes()
dc.loadPokememes()
dc.initPlayers()
dc.loadPlayers()
dc.initProfiles()
dc.loadProfiles()
}

48
lib/datacache/leagues.go Normal file
View File

@ -0,0 +1,48 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
func (dc *DataCache) initLeagues() {
c.Log.Info("Initializing Leagues storage...")
dc.leagues = make(map[int]*dbmapping.League)
}
func (dc *DataCache) loadLeagues() {
c.Log.Info("Load current Leagues data from database to DataCache...")
leagues := []dbmapping.League{}
err := c.Db.Select(&leagues, "SELECT * FROM leagues")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
dc.leaguesMutex.Lock()
for i := range leagues {
dc.leagues[leagues[i].ID] = &leagues[i]
}
c.Log.Info("Loaded leagues in DataCache: " + strconv.Itoa(len(dc.leagues)))
dc.leaguesMutex.Unlock()
}
// External functions
// GetLeagueBySymbol returns league from datacache by emoji
func (dc *DataCache) GetLeagueBySymbol(symbol string) (*dbmapping.League, error) {
dc.leaguesMutex.Lock()
for i := range dc.leagues {
if dc.leagues[i].Symbol == symbol {
dc.leaguesMutex.Unlock()
return dc.leagues[i], nil
}
}
dc.leaguesMutex.Unlock()
return nil, errors.New("There is no league with symbol = " + symbol)
}

View File

@ -0,0 +1,45 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
func (dc *DataCache) initLocations() {
c.Log.Info("Initializing Locations storage...")
dc.locations = make(map[int]*dbmapping.Location)
}
func (dc *DataCache) loadLocations() {
c.Log.Info("Load current Locations data from database to DataCache...")
locations := []dbmapping.Location{}
err := c.Db.Select(&locations, "SELECT * FROM locations")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
dc.locationsMutex.Lock()
for i := range locations {
dc.locations[locations[i].ID] = &locations[i]
}
c.Log.Info("Loaded locations in DataCache: " + strconv.Itoa(len(dc.locations)))
dc.locationsMutex.Unlock()
}
func (dc *DataCache) findLocationIDByName(name string) (int, error) {
dc.locationsMutex.Lock()
for i := range dc.locations {
if dc.locations[i].Name == name {
dc.locationsMutex.Unlock()
return i, nil
}
}
dc.locationsMutex.Unlock()
return 0, errors.New("There is no location with name = " + name)
}

157
lib/datacache/players.go Normal file
View File

@ -0,0 +1,157 @@
// 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))
}

297
lib/datacache/pokememes.go Normal file
View File

@ -0,0 +1,297 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"sort"
"strconv"
"strings"
"time"
)
func (dc *DataCache) initPokememes() {
c.Log.Info("Initializing Pokememes storage...")
dc.pokememes = make(map[int]*dbmapping.Pokememe)
dc.fullPokememes = make(map[int]*dbmapping.PokememeFull)
}
func (dc *DataCache) loadPokememes() {
c.Log.Info("Load current Pokememes data from database to DataCache...")
pokememes := []dbmapping.Pokememe{}
err := c.Db.Select(&pokememes, "SELECT * FROM pokememes")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
pokememesElements := []dbmapping.PokememeElement{}
err = c.Db.Select(&pokememesElements, "SELECT * FROM pokememes_elements")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err)
}
pokememesLocations := []dbmapping.PokememeLocation{}
err = c.Db.Select(&pokememesLocations, "SELECT * FROM pokememes_locations")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err)
}
dc.pokememesMutex.Lock()
dc.fullPokememesMutex.Lock()
for i := range pokememes {
dc.pokememes[pokememes[i].ID] = &pokememes[i]
// Filling fullPokememes
fullPokememe := dbmapping.PokememeFull{}
elementsListed := []dbmapping.Element{}
locationsListed := []dbmapping.Location{}
for j := range pokememesLocations {
if pokememesLocations[j].PokememeID == pokememes[i].ID {
for l := range dc.locations {
if pokememesLocations[j].LocationID == dc.locations[l].ID {
locationsListed = append(locationsListed, *dc.locations[l])
}
}
}
}
for k := range pokememesElements {
if pokememesElements[k].PokememeID == pokememes[i].ID {
for e := range dc.elements {
if pokememesElements[k].ElementID == dc.elements[e].ID {
elementsListed = append(elementsListed, *dc.elements[e])
}
}
}
}
fullPokememe.Pokememe = pokememes[i]
fullPokememe.Elements = elementsListed
fullPokememe.Locations = locationsListed
dc.fullPokememes[pokememes[i].ID] = &fullPokememe
}
c.Log.Info("Loaded pokememes with all additional information in DataCache: " + strconv.Itoa(len(dc.fullPokememes)))
dc.pokememesMutex.Unlock()
dc.fullPokememesMutex.Unlock()
}
// External functions
// AddPokememe adds pokememe from parser
func (dc *DataCache) AddPokememe(pokememeData map[string]string, pokememeLocations map[string]string, pokememeElements map[string]string) (int, error) {
_, noerr := dc.GetPokememeByName(pokememeData["name"])
if noerr == nil {
return 0, errors.New("This pokememe already exists")
}
gradeInt := c.Statistics.GetPoints(pokememeData["grade"])
attackInt := c.Statistics.GetPoints(pokememeData["attack"])
hpInt := c.Statistics.GetPoints(pokememeData["hp"])
mpInt := c.Statistics.GetPoints(pokememeData["mp"])
defenceInt := attackInt
if pokememeData["defence"] != "" {
defenceInt = c.Statistics.GetPoints(pokememeData["defence"])
}
priceInt := c.Statistics.GetPoints(pokememeData["price"])
creatorID := c.Statistics.GetPoints(pokememeData["creator_id"])
if !(gradeInt != 0 && attackInt != 0 && hpInt != 0 && mpInt != 0 && defenceInt != 0 && priceInt != 0 && creatorID != 0) {
return 0, errors.New("Some of the required numerical values are empty")
}
pokememe := dbmapping.Pokememe{}
pokememe.Grade = gradeInt
pokememe.Name = pokememeData["name"]
pokememe.Description = pokememeData["description"]
pokememe.Attack = attackInt
pokememe.HP = hpInt
pokememe.MP = mpInt
pokememe.Defence = defenceInt
pokememe.Price = priceInt
if pokememeData["purchaseable"] == "true" {
pokememe.Purchaseable = true
} else {
pokememe.Purchaseable = false
}
pokememe.ImageURL = pokememeData["image"]
pokememe.PlayerID = creatorID
pokememe.CreatedAt = time.Now().UTC()
locations := []dbmapping.Location{}
elements := []dbmapping.Element{}
for i := range pokememeLocations {
locationID, err := dc.findLocationIDByName(pokememeLocations[i])
if err != nil {
return 0, err
}
locations = append(locations, *dc.locations[locationID])
}
for i := range pokememeElements {
elementID, err := dc.findElementIDBySymbol(pokememeElements[i])
if err != nil {
return 0, err
}
elements = append(elements, *dc.elements[elementID])
}
// All objects are prepared, let's fill database with it!
c.Log.Debug("Filling pokememe...")
_, err := c.Db.NamedExec("INSERT INTO pokememes VALUES(NULL, :grade, :name, :description, :attack, :hp, :mp, :defence, :price, :purchaseable, :image_url, :player_id, :created_at)", &pokememe)
if err != nil {
return 0, err
}
c.Log.Debug("Finding newly added pokememe...")
insertedPokememe := dbmapping.Pokememe{}
err = c.Db.Get(&insertedPokememe, c.Db.Rebind("SELECT * FROM pokememes WHERE grade=? AND name=?"), pokememe.Grade, pokememe.Name)
if err != nil {
return 0, err
}
// Now we creating locations and elements links
locationsAndElementsFilledSuccessfully := true
c.Log.Debug("Filling locations...")
for i := range locations {
link := dbmapping.PokememeLocation{}
link.PokememeID = insertedPokememe.ID
link.LocationID = locations[i].ID
link.CreatedAt = time.Now().UTC()
_, err := c.Db.NamedExec("INSERT INTO pokememes_locations VALUES(NULL, :pokememe_id, :location_id, :created_at)", &link)
if err != nil {
c.Log.Error(err.Error())
locationsAndElementsFilledSuccessfully = false
}
}
c.Log.Debug("Filling elements...")
for i := range elements {
link := dbmapping.PokememeElement{}
link.PokememeID = insertedPokememe.ID
link.ElementID = elements[i].ID
link.CreatedAt = time.Now().UTC()
_, err := c.Db.NamedExec("INSERT INTO pokememes_elements VALUES(NULL, :pokememe_id, :element_id, :created_at)", &link)
if err != nil {
c.Log.Error(err.Error())
locationsAndElementsFilledSuccessfully = false
}
}
if !locationsAndElementsFilledSuccessfully {
c.Log.Debug("All fucked up, removing what we have already added...")
// There is something fucked up. In normal state we're should never reach this code
_, err = c.Db.NamedExec("DELETE FROM pokememes_locations WHERE pokememe_id=:id", &insertedPokememe)
if err != nil {
c.Log.Error(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM pokememes_elements WHERE pokememe_id=:id", &insertedPokememe)
if err != nil {
c.Log.Error(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM pokememes where id=:id", &insertedPokememe)
if err != nil {
c.Log.Error(err.Error())
}
return 0, errors.New("Failed to add pokememe to database")
}
fullPokememe := dbmapping.PokememeFull{}
fullPokememe.Pokememe = insertedPokememe
fullPokememe.Locations = locations
fullPokememe.Elements = elements
// Filling data cache
dc.pokememesMutex.Lock()
dc.fullPokememesMutex.Lock()
dc.pokememes[insertedPokememe.ID] = &insertedPokememe
dc.fullPokememes[insertedPokememe.ID] = &fullPokememe
dc.pokememesMutex.Unlock()
dc.fullPokememesMutex.Unlock()
return insertedPokememe.ID, nil
}
// GetAllPokememes returns all pokememes
// Index in resulted map counts all pokememes ordered by grade and alphabetically
func (dc *DataCache) GetAllPokememes() map[int]*dbmapping.PokememeFull {
pokememes := make(map[int]*dbmapping.PokememeFull)
dc.fullPokememesMutex.Lock()
var keys []string
keysToIDs := make(map[string]int)
for i := range dc.fullPokememes {
keys = append(keys, strconv.Itoa(dc.fullPokememes[i].Pokememe.Grade)+"_"+dc.fullPokememes[i].Pokememe.Name)
keysToIDs[strconv.Itoa(dc.fullPokememes[i].Pokememe.Grade)+"_"+dc.fullPokememes[i].Pokememe.Name] = i
}
sort.Strings(keys)
idx := 0
for _, k := range keys {
pokememes[idx] = dc.fullPokememes[keysToIDs[k]]
idx++
}
dc.fullPokememesMutex.Unlock()
return pokememes
}
// GetPokememeByID returns pokememe with additional information by ID
func (dc *DataCache) GetPokememeByID(pokememeID int) (*dbmapping.PokememeFull, error) {
dc.fullPokememesMutex.Lock()
if dc.fullPokememes[pokememeID] != nil {
dc.fullPokememesMutex.Unlock()
return dc.fullPokememes[pokememeID], nil
}
dc.fullPokememesMutex.Unlock()
return nil, errors.New("There is no pokememe with ID = " + strconv.Itoa(pokememeID))
}
// GetPokememeByName returns pokememe from datacache by name
func (dc *DataCache) GetPokememeByName(name string) (*dbmapping.PokememeFull, error) {
dc.fullPokememesMutex.Lock()
for i := range dc.fullPokememes {
if strings.HasPrefix(dc.fullPokememes[i].Pokememe.Name, name) {
dc.fullPokememesMutex.Unlock()
return dc.fullPokememes[i], nil
}
}
dc.fullPokememesMutex.Unlock()
return nil, errors.New("There is no pokememe with name = " + name)
}
// DeletePokememeByID removes pokememe from database
func (dc *DataCache) DeletePokememeByID(pokememeID int) error {
pokememe, err := dc.GetPokememeByID(pokememeID)
if err != nil {
return err
}
_, err = c.Db.NamedExec("DELETE FROM pokememes_locations WHERE pokememe_id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Error(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM pokememes_elements WHERE pokememe_id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Error(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM pokememes where id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Error(err.Error())
}
dc.pokememesMutex.Lock()
dc.fullPokememesMutex.Lock()
delete(dc.pokememes, pokememe.Pokememe.ID)
delete(dc.fullPokememes, pokememe.Pokememe.ID)
dc.pokememesMutex.Unlock()
dc.fullPokememesMutex.Unlock()
return nil
}

118
lib/datacache/profiles.go Normal file
View File

@ -0,0 +1,118 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
func (dc *DataCache) initProfiles() {
c.Log.Info("Initializing Profiles storage...")
dc.profiles = make(map[int]*dbmapping.Profile)
dc.currentProfiles = make(map[int]*dbmapping.Profile)
}
func (dc *DataCache) loadProfiles() {
c.Log.Info("Load current Profiles data from database to DataCache...")
profiles := []dbmapping.Profile{}
err := c.Db.Select(&profiles, "SELECT * FROM profiles")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
dc.profilesMutex.Lock()
dc.currentProfilesMutex.Lock()
for i := range profiles {
dc.profiles[profiles[i].ID] = &profiles[i]
// Filling current profiles
if dc.currentProfiles[profiles[i].PlayerID] != nil {
if dc.currentProfiles[profiles[i].PlayerID].CreatedAt.Before(profiles[i].CreatedAt) {
dc.currentProfiles[profiles[i].PlayerID] = &profiles[i]
}
}
dc.currentProfiles[profiles[i].PlayerID] = &profiles[i]
}
c.Log.Info("Loaded profiles in DataCache: " + strconv.Itoa(len(dc.profiles)))
c.Log.Info("Loaded current profiles in DataCache: " + strconv.Itoa(len(dc.currentProfiles)))
dc.profilesMutex.Unlock()
dc.currentProfilesMutex.Unlock()
}
// External functions
// AddProfile creates new profile in database
func (dc *DataCache) AddProfile(profile *dbmapping.Profile) (int, error) {
_, err := c.Db.NamedExec("INSERT INTO `profiles` VALUES(NULL, :player_id, :nickname, :telegram_nickname, :level_id, :pokeballs, :wealth, :pokememes_wealth, :exp, :egg_exp, :power, :weapon_id, :crystalls, :created_at)", &profile)
if err != nil {
c.Log.Error(err.Error())
return 0, err
}
insertedProfile := dbmapping.Profile{}
err = c.Db.Get(&insertedProfile, c.Db.Rebind("SELECT * FROM profiles WHERE player_id=? AND created_at=?"), profile.PlayerID, profile.CreatedAt)
if err != nil {
c.Log.Error(err.Error())
return 0, err
}
dc.profilesMutex.Lock()
dc.currentProfilesMutex.Lock()
dc.profiles[insertedProfile.ID] = &insertedProfile
dc.currentProfiles[insertedProfile.PlayerID] = &insertedProfile
dc.currentProfilesMutex.Unlock()
dc.profilesMutex.Unlock()
return insertedProfile.ID, nil
}
// GetPlayersWithCurrentProfiles returns users with profiles, if exist
func (dc *DataCache) GetPlayersWithCurrentProfiles() map[int]*dbmapping.PlayerProfile {
dc.currentProfilesMutex.Lock()
dc.playersMutex.Lock()
users := make(map[int]*dbmapping.PlayerProfile)
for i := range dc.players {
user := dbmapping.PlayerProfile{}
user.Player = *dc.players[i]
if dc.players[i].LeagueID != 0 {
user.League = *dc.leagues[dc.players[i].LeagueID]
}
if dc.currentProfiles[dc.players[i].ID] != nil {
user.HaveProfile = true
user.Profile = *dc.currentProfiles[dc.players[i].ID]
} else {
user.HaveProfile = false
}
users[dc.players[i].ID] = &user
}
dc.currentProfilesMutex.Unlock()
dc.playersMutex.Unlock()
return users
}
// GetProfileByID returns profile from datacache by ID
func (dc *DataCache) GetProfileByID(profileID int) (*dbmapping.Profile, error) {
if dc.profiles[profileID] != nil {
return dc.profiles[profileID], nil
}
return nil, errors.New("There is no profile with ID = " + strconv.Itoa(profileID))
}
// GetProfileByPlayerID returns current profile for player
func (dc *DataCache) GetProfileByPlayerID(playerID int) (*dbmapping.Profile, error) {
if dc.currentProfiles[playerID] != nil {
return dc.currentProfiles[playerID], nil
}
return nil, errors.New("There is no profile for user with ID = " + strconv.Itoa(playerID))
}

48
lib/datacache/weapons.go Normal file
View File

@ -0,0 +1,48 @@
// i2_bot Instinct PokememBro Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package datacache
import (
"errors"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
func (dc *DataCache) initWeapons() {
c.Log.Info("Initializing Weapons storage...")
dc.weapons = make(map[int]*dbmapping.Weapon)
}
func (dc *DataCache) loadWeapons() {
c.Log.Info("Load current Weapons data from database to DataCache...")
weapons := []dbmapping.Weapon{}
err := c.Db.Select(&weapons, "SELECT * FROM weapons")
if err != nil {
// This is critical error and we need to stop immediately!
c.Log.Fatal(err.Error())
}
dc.weaponsMutex.Lock()
for i := range weapons {
dc.weapons[weapons[i].ID] = &weapons[i]
}
c.Log.Info("Loaded weapon types in DataCache: " + strconv.Itoa(len(dc.weapons)))
dc.weaponsMutex.Unlock()
}
// External functions
// GetWeaponTypeByName returns weapon type from datacache by weapon name
func (dc *DataCache) GetWeaponTypeByName(name string) (*dbmapping.Weapon, error) {
dc.weaponsMutex.Lock()
for i := range dc.weapons {
if dc.weapons[i].Name == name {
dc.weaponsMutex.Unlock()
return dc.weapons[i], nil
}
}
dc.weaponsMutex.Unlock()
return nil, errors.New("There is no weapon type with name = " + name)
}

View File

@ -24,24 +24,3 @@ type Profile struct {
Crystalls int `db:"crystalls"`
CreatedAt time.Time `db:"created_at"`
}
// ProfileWithAddons is a struct, which represents `profiles` table item in database and some good external fields.
type ProfileWithAddons struct {
ID int `db:"id"`
PlayerID int `db:"player_id"`
TelegramID int `db:"telegram_id"`
LeagueID int `db:"league_id"`
LeagueSymbol string `db:"league_symbol"`
Nickname string `db:"nickname"`
TelegramNickname string `db:"telegram_nickname"`
LevelID int `db:"level_id"`
Pokeballs int `db:"pokeballs"`
Wealth int `db:"wealth"`
PokememesWealth int `db:"pokememes_wealth"`
Exp int `db:"exp"`
EggExp int `db:"egg_exp"`
Power int `db:"power"`
WeaponID int `db:"weapon_id"`
Crystalls int `db:"crystalls"`
CreatedAt time.Time `db:"created_at"`
}

View File

@ -15,32 +15,12 @@ func (p *Pokedexer) DeletePokememe(update *tgbotapi.Update) string {
return "fail"
}
pokememe, ok := p.GetPokememeByID(strconv.Itoa(pokememeNum))
if !ok {
return "fail"
}
_, err := c.Db.NamedExec("DELETE FROM pokememes WHERE id=:id", &pokememe.Pokememe)
err := c.DataCache.DeletePokememeByID(pokememeNum)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}
_, err = c.Db.NamedExec("DELETE FROM pokememes_elements WHERE pokememe_id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Debug(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM pokememes_locations WHERE pokememe_id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Debug(err.Error())
}
_, err = c.Db.NamedExec("DELETE FROM profiles_pokememes WHERE pokememe_id=:id", &pokememe.Pokememe)
if err != nil {
c.Log.Debug(err.Error())
}
message := "Покемем удалён."
msg := tgbotapi.NewMessage(update.Message.Chat.ID, message)

View File

@ -5,184 +5,50 @@ package pokedexer
import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"strconv"
)
// Internal functions
func (p *Pokedexer) formFullPokememes(pokememes []dbmapping.Pokememe) ([]dbmapping.PokememeFull, bool) {
pokememesArray := []dbmapping.PokememeFull{}
elements := []dbmapping.Element{}
err := c.Db.Select(&elements, "SELECT * FROM elements")
if err != nil {
c.Log.Error(err)
return pokememesArray, false
}
locations := []dbmapping.Location{}
err = c.Db.Select(&locations, "SELECT * FROM locations")
if err != nil {
c.Log.Error(err)
return pokememesArray, false
}
pokememesElements := []dbmapping.PokememeElement{}
err = c.Db.Select(&pokememesElements, "SELECT * FROM pokememes_elements")
if err != nil {
c.Log.Error(err)
return pokememesArray, false
}
pokememesLocations := []dbmapping.PokememeLocation{}
err = c.Db.Select(&pokememesLocations, "SELECT * FROM pokememes_locations")
if err != nil {
c.Log.Error(err)
return pokememesArray, false
}
for i := range pokememes {
fullPokememe := dbmapping.PokememeFull{}
elementsListed := []dbmapping.Element{}
locationsListed := []dbmapping.Location{}
for j := range pokememesLocations {
if pokememesLocations[j].PokememeID == pokememes[i].ID {
for l := range locations {
if pokememesLocations[j].LocationID == locations[l].ID {
locationsListed = append(locationsListed, locations[l])
}
}
}
}
for k := range pokememesElements {
if pokememesElements[k].PokememeID == pokememes[i].ID {
for e := range elements {
if pokememesElements[k].ElementID == elements[e].ID {
elementsListed = append(elementsListed, elements[e])
}
}
}
}
fullPokememe.Pokememe = pokememes[i]
fullPokememe.Elements = elementsListed
fullPokememe.Locations = locationsListed
pokememesArray = append(pokememesArray, fullPokememe)
}
return pokememesArray, true
}
// External functions
// GetPokememes returns all existing pokememes, known by bot
func (p *Pokedexer) GetPokememes() ([]dbmapping.PokememeFull, bool) {
pokememesArray := []dbmapping.PokememeFull{}
pokememes := []dbmapping.Pokememe{}
err := c.Db.Select(&pokememes, "SELECT * FROM pokememes ORDER BY grade asc, name asc")
func (p *Pokedexer) getBestPokememes(playerID int) (map[int]*dbmapping.PokememeFull, bool) {
pokememesArray := make(map[int]*dbmapping.PokememeFull)
playerRaw, err := c.DataCache.GetPlayerByID(playerID)
if err != nil {
c.Log.Error(err)
c.Log.Error(err.Error())
return pokememesArray, false
}
pokememesArray, ok := p.formFullPokememes(pokememes)
return pokememesArray, ok
}
func (p *Pokedexer) getBestPokememes(playerID int) ([]dbmapping.PokememeFull, bool) {
pokememesArray := []dbmapping.PokememeFull{}
playerRaw, ok := c.Users.GetPlayerByID(playerID)
if !ok {
return pokememesArray, ok
}
profileRaw, ok := c.Users.GetProfile(playerID)
if !ok {
return pokememesArray, ok
profileRaw, err := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
return pokememesArray, false
}
if playerRaw.LeagueID == 0 {
return pokememesArray, false
}
// TODO: make it more complicated
pokememes := []dbmapping.Pokememe{}
allPokememes := c.DataCache.GetAllPokememes()
if profileRaw.LevelID < 4 {
err := c.Db.Select(&pokememes, c.Db.Rebind("SELECT * FROM pokememes WHERE grade = ? ORDER BY attack DESC"), profileRaw.LevelID+1)
if err != nil {
c.Log.Error(err)
return pokememesArray, false
for i := range allPokememes {
if allPokememes[i].Pokememe.Grade == profileRaw.LevelID+1 {
pokememesArray[allPokememes[i].Pokememe.Attack] = allPokememes[i]
}
}
} else {
err := c.Db.Select(&pokememes, c.Db.Rebind("SELECT p.* FROM pokememes p, pokememes_elements pe, elements e WHERE e.league_id = ? AND p.grade = ? AND pe.element_id = e.id AND pe.pokememe_id = p.id ORDER BY p.attack DESC"), playerRaw.LeagueID, profileRaw.LevelID+1)
if err != nil {
c.Log.Error(err)
return pokememesArray, false
}
}
pokememesArray, ok = p.formFullPokememes(pokememes)
return pokememesArray, ok
}
// GetPokememeByID returns single pokememe based on internal ID in database
func (p *Pokedexer) GetPokememeByID(pokememeID string) (dbmapping.PokememeFull, bool) {
fullPokememe := dbmapping.PokememeFull{}
pokememe := dbmapping.Pokememe{}
err := c.Db.Get(&pokememe, c.Db.Rebind("SELECT * FROM pokememes WHERE id=?"), pokememeID)
if err != nil {
c.Log.Error(err)
return fullPokememe, false
}
elements := []dbmapping.Element{}
err = c.Db.Select(&elements, "SELECT * FROM elements")
if err != nil {
c.Log.Error(err)
return fullPokememe, false
}
locations := []dbmapping.Location{}
err = c.Db.Select(&locations, "SELECT * FROM locations")
if err != nil {
c.Log.Error(err)
return fullPokememe, false
}
pokememesElements := []dbmapping.PokememeElement{}
err = c.Db.Select(&pokememesElements, "SELECT * FROM pokememes_elements WHERE pokememe_id='"+strconv.Itoa(pokememe.ID)+"'")
if err != nil {
c.Log.Error(err)
return fullPokememe, false
}
pokememesLocations := []dbmapping.PokememeLocation{}
err = c.Db.Select(&pokememesLocations, "SELECT * FROM pokememes_locations WHERE pokememe_id='"+strconv.Itoa(pokememe.ID)+"'")
if err != nil {
c.Log.Error(err)
return fullPokememe, false
}
elementsListed := []dbmapping.Element{}
locationsListed := []dbmapping.Location{}
for j := range pokememesLocations {
if pokememesLocations[j].PokememeID == pokememe.ID {
for l := range locations {
if pokememesLocations[j].LocationID == locations[l].ID {
locationsListed = append(locationsListed, locations[l])
for i := range allPokememes {
if allPokememes[i].Pokememe.Grade == profileRaw.LevelID+1 {
matchLeague := false
for j := range allPokememes[i].Elements {
if allPokememes[i].Elements[j].LeagueID == playerRaw.LeagueID {
matchLeague = true
}
}
if matchLeague {
pokememesArray[allPokememes[i].Pokememe.Attack] = allPokememes[i]
}
}
}
}
for k := range pokememesElements {
if pokememesElements[k].PokememeID == pokememe.ID {
for e := range elements {
if pokememesElements[k].ElementID == elements[e].ID {
elementsListed = append(elementsListed, elements[e])
}
}
}
}
fullPokememe.Pokememe = pokememe
fullPokememe.Elements = elementsListed
fullPokememe.Locations = locationsListed
return fullPokememe, true
return pokememesArray, true
}

View File

@ -4,248 +4,143 @@
package pokedexer
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"regexp"
"strconv"
"strings"
"time"
// "time"
)
// ParsePokememe parses pokememe, forwarded from PokememeBroBot, to database
func (p *Pokedexer) ParsePokememe(update *tgbotapi.Update, playerRaw *dbmapping.Player) string {
text := update.Message.Text
var defendablePokememe = false
pokememeStringsArray := strings.Split(text, "\n")
pokememeStringsArray := strings.Split(update.Message.Text, "\n")
pokememeRunesArray := make([][]rune, 0)
for i := range pokememeStringsArray {
pokememeRunesArray = append(pokememeRunesArray, []rune(pokememeStringsArray[i]))
}
if len(pokememeRunesArray) == 13 {
defendablePokememe = true
pokememeData := make(map[string]string)
pokememeLocations := make(map[string]string)
pokememeElements := make(map[string]string)
hitPointsRx := regexp.MustCompile("(\\d|\\.)+(K|M)?")
for i := range pokememeStringsArray {
c.Log.Debug("Processing string: " + pokememeStringsArray[i])
if strings.Contains(pokememeStringsArray[i], "⃣") {
// Strings with name and grade
pokememeData["grade"] = string(pokememeRunesArray[i][0])
pokememeData["name"] = string(pokememeRunesArray[i][3:])
}
if i == 1 {
pokememeData["description"] = string(pokememeRunesArray[i])
}
if strings.HasPrefix(pokememeStringsArray[i], "Обитает: ") {
// Elements
locationsString := strings.TrimPrefix(pokememeStringsArray[i], "Обитает: ")
locationsArray := strings.Split(locationsString, ", ")
for i := range locationsArray {
pokememeLocations[strconv.Itoa(i)] = locationsArray[i]
}
}
if strings.HasPrefix(pokememeStringsArray[i], "Элементы: ") {
// Elements
elementsString := strings.TrimPrefix(pokememeStringsArray[i], "Элементы: ")
elementsArray := strings.Split(elementsString, " ")
for i := range elementsArray {
pokememeElements[strconv.Itoa(i)] = elementsArray[i]
}
}
if strings.HasPrefix(pokememeStringsArray[i], "⚔Атака: ") {
// Attack, HP, MP
hitPoints := hitPointsRx.FindAllString(string(pokememeRunesArray[i]), -1)
if len(hitPoints) != 3 {
c.Log.Error("Can't parse hitpoints!")
c.Log.Debug("Points string was: " + string(pokememeRunesArray[i]))
p.pokememeAddFailureMessage(update)
return "fail"
}
pokememeData["attack"] = hitPoints[0]
pokememeData["hp"] = hitPoints[1]
pokememeData["mp"] = hitPoints[2]
}
if strings.HasPrefix(pokememeStringsArray[i], "🛡Защита ") {
// Defence for top-level pokememes
defence := hitPointsRx.FindAllString(string(pokememeRunesArray[i]), -1)
if len(defence) != 1 {
c.Log.Error("Can't parse defence!")
c.Log.Debug("Defence string was: " + string(pokememeRunesArray[i]))
p.pokememeAddFailureMessage(update)
return "fail"
}
pokememeData["defence"] = defence[0]
}
if strings.HasPrefix(pokememeStringsArray[i], "Стоимость :") {
// Price
price := hitPointsRx.FindAllString(string(pokememeRunesArray[i]), -1)
if len(price) != 1 {
c.Log.Error("Can't parse price!")
c.Log.Debug("Price string was: " + string(pokememeRunesArray[i]))
p.pokememeAddFailureMessage(update)
return "fail"
}
pokememeData["price"] = price[0]
}
if strings.HasPrefix(pokememeStringsArray[i], "Купить: ") {
// Purchaseability
pokememeData["purchaseable"] = "false"
if strings.Contains(pokememeStringsArray[i], "Можно") {
pokememeData["purchaseable"] = "true"
}
}
}
// Getting elements
elements := []dbmapping.Element{}
elementEmojis := make([]string, 0)
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][11]))
if len(pokememeRunesArray[4]) > 12 {
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][13]))
}
if len(pokememeRunesArray[4]) > 14 {
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][15]))
// Image
for _, entity := range *update.Message.Entities {
if entity.Type == "text_link" && entity.URL != "" {
pokememeData["image"] = entity.URL
}
}
err := c.Db.Select(&elements, "SELECT * FROM elements WHERE symbol IN ('"+strings.Join(elementEmojis, "', '")+"')")
// Checking grade to be integer
_, err := strconv.Atoi(pokememeData["grade"])
if err != nil {
c.Log.Error(err.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
// Getting hit-points
hitPointsRx := regexp.MustCompile("(\\d|\\.)+(K|M)?")
hitPoints := hitPointsRx.FindAllString(string(pokememeRunesArray[5]), -1)
if len(hitPoints) != 3 {
c.Log.Error("Can't parse hitpoints!")
c.Log.Debug(pokememeRunesArray[5])
pokememeData["creator_id"] = strconv.Itoa(playerRaw.ID)
c.Log.Debugln("Pokememe data: ", pokememeData)
c.Log.Debugln("Elements: ", pokememeElements)
c.Log.Debugln("Locations: ", pokememeLocations)
if len(pokememeElements) == 0 {
p.pokememeAddFailureMessage(update)
return "fail"
}
defence := "0"
price := "0"
locations := []dbmapping.Location{}
purchaseable := false
image := ""
if defendablePokememe {
// Actions for high-grade pokememes
defenceMatch := hitPointsRx.FindAllString(string(pokememeRunesArray[6]), -1)
if len(defenceMatch) < 1 {
c.Log.Error("Can't parse defence!")
c.Log.Debug(pokememeRunesArray[6])
p.pokememeAddFailureMessage(update)
return "fail"
}
defence = defenceMatch[0]
priceMatch := hitPointsRx.FindAllString(string(pokememeRunesArray[7]), -1)
if len(priceMatch) < 1 {
c.Log.Error("Can't parse price!")
c.Log.Debug(pokememeRunesArray[7])
p.pokememeAddFailureMessage(update)
return "fail"
}
price = priceMatch[0]
locationsPrepare := strings.Split(string(pokememeRunesArray[8]), ": ")
if len(locationsPrepare) < 2 {
c.Log.Error("Can't parse locations!")
c.Log.Debug(pokememeRunesArray[8])
p.pokememeAddFailureMessage(update)
return "fail"
}
locationsNames := strings.Split(locationsPrepare[1], ", ")
if len(locationsNames) < 1 {
c.Log.Error("Can't parse locations!")
c.Log.Debug(locationsPrepare)
p.pokememeAddFailureMessage(update)
return "fail"
}
err2 := c.Db.Select(&locations, "SELECT * FROM locations WHERE name IN ('"+strings.Join(locationsNames, "', '")+"')")
if err2 != nil {
c.Log.Error(err2.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
if strings.HasSuffix(string(pokememeRunesArray[9]), "Можно") {
purchaseable = true
}
image = strings.Replace(string(pokememeRunesArray[12]), " ", "", -1)
} else {
// Actions for low-grade pokememes
defence = hitPoints[0]
priceMatch := hitPointsRx.FindAllString(string(pokememeRunesArray[6]), -1)
if len(priceMatch) < 1 {
c.Log.Error("Can't parse price!")
c.Log.Debug(pokememeRunesArray[6])
p.pokememeAddFailureMessage(update)
return "fail"
}
price = priceMatch[0]
locationsPrepare := strings.Split(string(pokememeRunesArray[7]), ": ")
if len(locationsPrepare) < 2 {
c.Log.Error("Can't parse locations!")
c.Log.Debug(pokememeRunesArray[7])
p.pokememeAddFailureMessage(update)
return "fail"
}
locationsNames := strings.Split(locationsPrepare[1], ", ")
if len(locationsNames) < 1 {
c.Log.Error("Can't parse locations!")
c.Log.Debug(locationsPrepare)
p.pokememeAddFailureMessage(update)
return "fail"
}
err2 := c.Db.Select(&locations, "SELECT * FROM locations WHERE name IN ('"+strings.Join(locationsNames, "', '")+"')")
if err2 != nil {
c.Log.Error(err2.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
if strings.HasSuffix(string(pokememeRunesArray[8]), "Можно") {
purchaseable = true
}
image = strings.Replace(string(pokememeRunesArray[11]), " ", "", -1)
}
grade := string(pokememeRunesArray[0][0])
name := string(pokememeRunesArray[0][3:])
description := string(pokememeRunesArray[1])
c.Log.Debug("Pokememe grade: " + grade)
c.Log.Debug("Pokememe name: " + name)
c.Log.Debug("Pokememe description: " + description)
c.Log.Debug("Elements:")
for i := range elements {
c.Log.Debug(elements[i].Symbol + " " + elements[i].Name)
}
c.Log.Debug("Attack: " + hitPoints[0])
c.Log.Debug("HP: " + hitPoints[1])
c.Log.Debug("MP: " + hitPoints[2])
c.Log.Debug("Defence: " + defence)
c.Log.Debug("Price: " + price)
c.Log.Debug("Locations:")
for i := range locations {
c.Log.Debug(locations[i].Symbol + " " + locations[i].Name)
}
if purchaseable {
c.Log.Debug("Purchaseable")
} else {
c.Log.Debug("Non-purchaseable")
}
c.Log.Debug("Image: " + image)
// Building pokememe
pokememe := dbmapping.Pokememe{}
// Checking if pokememe exists in database
err3 := c.Db.Get(&pokememe, c.Db.Rebind("SELECT * FROM pokememes WHERE grade='"+grade+"' AND name='"+name+"';"))
if err3 != nil {
c.Log.Debug("Adding new pokememe...")
} else {
c.Log.Info("This pokememe already exist. Return specific error.")
p.pokememeAddDuplicateMessage(update)
return "dup"
}
gradeInt, _ := strconv.Atoi(grade)
attackInt := c.Statistics.GetPoints(hitPoints[0])
hpInt := c.Statistics.GetPoints(hitPoints[1])
mpInt := c.Statistics.GetPoints(hitPoints[2])
defenceInt := c.Statistics.GetPoints(defence)
priceInt := c.Statistics.GetPoints(price)
pokememe.Grade = gradeInt
pokememe.Name = name
pokememe.Description = description
pokememe.Attack = attackInt
pokememe.HP = hpInt
pokememe.MP = mpInt
pokememe.Defence = defenceInt
pokememe.Price = priceInt
if purchaseable {
pokememe.Purchaseable = true
} else {
pokememe.Purchaseable = false
}
pokememe.ImageURL = image
pokememe.PlayerID = playerRaw.ID
pokememe.CreatedAt = time.Now().UTC()
_, err4 := c.Db.NamedExec("INSERT INTO pokememes VALUES(NULL, :grade, :name, :description, :attack, :hp, :mp, :defence, :price, :purchaseable, :image_url, :player_id, :created_at)", &pokememe)
if err4 != nil {
c.Log.Error(err4.Error())
if len(pokememeLocations) == 0 {
p.pokememeAddFailureMessage(update)
return "fail"
}
// Getting new pokememe
err5 := c.Db.Get(&pokememe, c.Db.Rebind("SELECT * FROM pokememes WHERE grade='"+grade+"' AND name='"+name+"';"))
if err5 != nil {
c.Log.Error("Pokememe isn't added!")
newPokememeID, err := c.DataCache.AddPokememe(pokememeData, pokememeLocations, pokememeElements)
if err != nil {
c.Log.Error(err.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
for i := range elements {
link := dbmapping.PokememeElement{}
link.PokememeID = pokememe.ID
link.ElementID = elements[i].ID
link.CreatedAt = time.Now().UTC()
_, err6 := c.Db.NamedExec("INSERT INTO pokememes_elements VALUES(NULL, :pokememe_id, :element_id, :created_at)", &link)
if err6 != nil {
c.Log.Error(err6.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
}
for i := range locations {
link := dbmapping.PokememeLocation{}
link.PokememeID = pokememe.ID
link.LocationID = locations[i].ID
link.CreatedAt = time.Now().UTC()
_, err7 := c.Db.NamedExec("INSERT INTO pokememes_locations VALUES(NULL, :pokememe_id, :location_id, :created_at)", &link)
if err7 != nil {
c.Log.Error(err7.Error())
p.pokememeAddFailureMessage(update)
return "fail"
}
}
p.pokememeAddSuccessMessage(update)
p.pokememeAddSuccessMessage(update, newPokememeID)
return "ok"
}

View File

@ -4,15 +4,16 @@
package pokedexer
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"sort"
"strconv"
)
func (p *Pokedexer) pokememesListing(update *tgbotapi.Update, page int, pokememesArray []dbmapping.PokememeFull) {
func (p *Pokedexer) pokememesListing(update *tgbotapi.Update, page int, pokememesArray map[int]*dbmapping.PokememeFull) {
message := "*Известные боту покемемы*\n"
message += "Список отсортирован по грейду и алфавиту.\n"
message += "Покедекс: " + strconv.Itoa(len(pokememesArray)) + " / 249\n"
message += "Покедекс: " + strconv.Itoa(len(pokememesArray)) + " / 274\n"
message += "Отображаем покемемов с " + strconv.Itoa(((page-1)*50)+1) + " по " + strconv.Itoa(page*50) + "\n"
if len(pokememesArray) > page*50 {
message += "Переход на следующую страницу: /pokedeks" + strconv.Itoa(page+1)
@ -22,7 +23,13 @@ func (p *Pokedexer) pokememesListing(update *tgbotapi.Update, page int, pokememe
}
message += "\n\n"
var keys []int
for i := range pokememesArray {
keys = append(keys, i)
}
sort.Ints(keys)
for _, i := range keys {
if (i+1 > 50*(page-1)) && (i+1 < (50*page)+1) {
pk := pokememesArray[i].Pokememe
pkE := pokememesArray[i].Elements
@ -52,9 +59,10 @@ func (p *Pokedexer) pokememesListing(update *tgbotapi.Update, page int, pokememe
c.Bot.Send(msg)
}
func (p *Pokedexer) pokememeAddSuccessMessage(update *tgbotapi.Update) {
func (p *Pokedexer) pokememeAddSuccessMessage(update *tgbotapi.Update, newPokememeID int) {
message := "*Покемем успешно добавлен.*\n\n"
message += "Посмотреть всех известных боту покемемов можно командой /pokedeks"
message += "Посмотреть всех известных боту покемемов можно командой /pokedeks\n"
message += "Посмотреть свежедобавленного покемема можно командой /pk" + strconv.Itoa(newPokememeID)
msg := tgbotapi.NewMessage(update.Message.Chat.ID, message)
msg.ParseMode = "Markdown"

View File

@ -4,8 +4,8 @@
package pokedexerinterface
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
// PokedexerInterface implements Pokedexer for importing via appcontext.
@ -16,8 +16,5 @@ type PokedexerInterface interface {
PokememeInfo(update *tgbotapi.Update, playerRaw *dbmapping.Player) string
BestPokememesList(update *tgbotapi.Update, playerRaw *dbmapping.Player) string
GetPokememes() ([]dbmapping.PokememeFull, bool)
GetPokememeByID(pokememeID string) (dbmapping.PokememeFull, bool)
DeletePokememe(update *tgbotapi.Update) string
}

View File

@ -4,8 +4,9 @@
package pokedexer
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"sort"
"strconv"
"strings"
)
@ -18,8 +19,14 @@ func (p *Pokedexer) BestPokememesList(update *tgbotapi.Update, playerRaw *dbmapp
return "fail"
}
message := "*Лучшие покемемы для ловли*\n\n"
var attacks []int
for i := range pokememes {
attacks = append(attacks, i)
}
sort.Sort(sort.Reverse(sort.IntSlice(attacks)))
message := "*Лучшие покемемы для ловли*\n\n"
for _, i := range attacks {
pk := pokememes[i].Pokememe
pkL := pokememes[i].Locations
pkE := pokememes[i].Elements
@ -63,25 +70,24 @@ func (p *Pokedexer) PokememesList(update *tgbotapi.Update) {
if page == 0 {
page = 1
}
pokememesArray, ok := p.GetPokememes()
if !ok {
c.Talkers.BotError(update)
} else {
p.pokememesListing(update, page, pokememesArray)
}
pokememesArray := c.DataCache.GetAllPokememes()
p.pokememesListing(update, page, pokememesArray)
}
// PokememeInfo shows information about single pokememe based on internal ID
func (p *Pokedexer) PokememeInfo(update *tgbotapi.Update, playerRaw *dbmapping.Player) string {
pokememeNumber := strings.Replace(update.Message.Text, "/pk", "", 1)
var calculatePossibilites = true
profileRaw, ok := c.Users.GetProfile(playerRaw.ID)
if !ok {
profileRaw, err := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
calculatePossibilites = false
}
pokememe, ok := p.GetPokememeByID(pokememeNumber)
if !ok {
pokememeID, _ := strconv.Atoi(pokememeNumber)
pokememe, err := c.DataCache.GetPokememeByID(pokememeID)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}

View File

@ -10,8 +10,9 @@ import (
// RouteCallback routes inline requests to bot
func (r *Router) RouteCallback(update *tgbotapi.Update) string {
playerRaw, ok := c.Users.GetOrCreatePlayer(update.CallbackQuery.From.ID)
if !ok {
playerRaw, err := c.DataCache.GetOrCreatePlayerByTelegramID(update.CallbackQuery.From.ID)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}
@ -20,9 +21,9 @@ func (r *Router) RouteCallback(update *tgbotapi.Update) string {
switch {
case enableAlarmCallback.MatchString(update.CallbackQuery.Data):
return c.Reminder.CreateAlarmSetting(update, &playerRaw)
return c.Reminder.CreateAlarmSetting(update, playerRaw)
case disableAlarmCallback.MatchString(update.CallbackQuery.Data):
return c.Reminder.DestroyAlarmSetting(update, &playerRaw)
return c.Reminder.DestroyAlarmSetting(update, playerRaw)
}
return "ok"

View File

@ -11,8 +11,9 @@ import (
// RouteInline routes inline requests to bot
func (r *Router) RouteInline(update *tgbotapi.Update) string {
playerRaw, ok := c.Users.GetOrCreatePlayer(update.InlineQuery.From.ID)
if !ok {
playerRaw, err := c.DataCache.GetOrCreatePlayerByTelegramID(update.InlineQuery.From.ID)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}
@ -73,7 +74,7 @@ func (r *Router) RouteInline(update *tgbotapi.Update) string {
Results: results,
}
_, err := c.Bot.AnswerInlineQuery(inlineConf)
_, err = c.Bot.AnswerInlineQuery(inlineConf)
if err != nil {
c.Log.Error(err.Error())
}

View File

@ -9,8 +9,9 @@ import (
// RouteRequest decides, what to do with user input
func (r *Router) RouteRequest(update *tgbotapi.Update) string {
playerRaw, ok := c.Users.GetOrCreatePlayer(update.Message.From.ID)
if !ok {
playerRaw, err := c.DataCache.GetOrCreatePlayerByTelegramID(update.Message.From.ID)
if err != nil {
c.Log.Error(err.Error())
// Silently fail
return "fail"
}
@ -20,13 +21,10 @@ func (r *Router) RouteRequest(update *tgbotapi.Update) string {
return "fail"
}
c.Log.Debug("Received message from chat ")
c.Log.Debugln(chatRaw.TelegramID)
if update.Message.Chat.IsGroup() || update.Message.Chat.IsSuperGroup() {
return r.routeGroupRequest(update, &playerRaw, &chatRaw)
return r.routeGroupRequest(update, playerRaw, &chatRaw)
} else if update.Message.Chat.IsPrivate() {
return r.routePrivateRequest(update, &playerRaw, &chatRaw)
return r.routePrivateRequest(update, playerRaw, &chatRaw)
}
return "ok"

View File

@ -144,13 +144,13 @@ func (s *Squader) GetUserRolesInSquads(playerRaw *dbmapping.Player) ([]dbmapping
for i := range userRolesRaw {
userRoleFull := dbmapping.SquadPlayerFull{}
userRoleFull.Player = *playerRaw
userProfile, profileOk := c.Users.GetProfile(playerRaw.ID)
userRoleFull.Profile = userProfile
userProfile, profileError := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
userRoleFull.Profile = *userProfile
userRoleFull.UserRole = userRolesRaw[i].UserType
squad, squadOk := s.GetSquadByID(userRolesRaw[i].SquadID)
userRoleFull.Squad = squad
if profileOk && squadOk {
if profileError == nil && squadOk {
userRoles = append(userRoles, userRoleFull)
}
}

View File

@ -4,16 +4,17 @@
package squader
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
// CleanFlood will clean flood from squads
func (s *Squader) CleanFlood(update *tgbotapi.Update, chatRaw *dbmapping.Chat) string {
switch s.IsChatASquadEnabled(chatRaw) {
case "main":
talker, ok := c.Users.GetOrCreatePlayer(update.Message.From.ID)
if !ok {
talker, err := c.DataCache.GetPlayerByTelegramID(update.Message.From.ID)
if err != nil {
c.Log.Error(err.Error())
s.deleteFloodMessage(update)
return "fail"
}

View File

@ -4,8 +4,8 @@
package squader
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"regexp"
"strconv"
"strings"
@ -38,8 +38,8 @@ func (s *Squader) getPlayersForSquad(squadID int) ([]dbmapping.SquadPlayerFull,
for ii := range squadPlayers {
if squadPlayers[ii].PlayerID == playersRaw[i].ID {
playerWithProfile := dbmapping.SquadPlayerFull{}
profile, _ := c.Users.GetProfile(playersRaw[i].ID)
playerWithProfile.Profile = profile
profile, _ := c.DataCache.GetProfileByPlayerID(playersRaw[i].ID)
playerWithProfile.Profile = *profile
playerWithProfile.Player = playersRaw[i]
playerWithProfile.Squad = squad
playerWithProfile.UserRole = squadPlayers[ii].UserType
@ -114,8 +114,9 @@ func (s *Squader) createSquad(update *tgbotapi.Update, chatID int, floodChatID i
if err != nil {
c.Log.Debug(err)
playerRaw, ok := c.Users.GetOrCreatePlayer(update.Message.From.ID)
if !ok {
playerRaw, err := c.DataCache.GetPlayerByTelegramID(update.Message.From.ID)
if err != nil {
c.Log.Error(err.Error())
return squad, "fail"
}
@ -287,20 +288,22 @@ func (s *Squader) AddUserToSquad(update *tgbotapi.Update, adderRaw *dbmapping.Pl
return s.squadUserAdditionFailure(update)
}
playerRaw, ok := c.Users.GetPlayerByID(playerID)
if !ok {
playerRaw, err := c.DataCache.GetPlayerByID(playerID)
if err != nil {
c.Log.Error(err.Error())
return s.squadUserAdditionFailure(update)
}
squadRaw := dbmapping.Squad{}
err := c.Db.Get(&squadRaw, c.Db.Rebind("SELECT * FROM squads WHERE id=?"), squadID)
err = c.Db.Get(&squadRaw, c.Db.Rebind("SELECT * FROM squads WHERE id=?"), squadID)
if err != nil {
c.Log.Error(err.Error())
return s.squadUserAdditionFailure(update)
}
if !c.Users.PlayerBetterThan(&playerRaw, "admin") {
_, ok = c.Users.GetProfile(playerRaw.ID)
if !ok {
if !c.Users.PlayerBetterThan(playerRaw, "admin") {
_, err = c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
return s.squadUserAdditionFailure(update)
}
}
@ -315,7 +318,7 @@ func (s *Squader) AddUserToSquad(update *tgbotapi.Update, adderRaw *dbmapping.Pl
}
}
if !c.Users.PlayerBetterThan(&playerRaw, "admin") {
if !c.Users.PlayerBetterThan(playerRaw, "admin") {
if playerRaw.LeagueID != 1 {
return s.squadUserAdditionFailure(update)
}

View File

@ -27,12 +27,18 @@ func (s *Statistics) SquadStatictics(squadID int) string {
for i := range squadMembers {
fullInfo := dbmapping.SquadPlayerFull{}
playerRaw, _ := c.Users.GetPlayerByID(squadMembers[i].PlayerID)
profileRaw, _ := c.Users.GetProfile(playerRaw.ID)
playerRaw, err := c.DataCache.GetPlayerByID(squadMembers[i].PlayerID)
if err != nil {
c.Log.Error(err.Error())
}
profileRaw, err := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
}
fullInfo.Squad = squad
fullInfo.Player = playerRaw
fullInfo.Profile = profileRaw
fullInfo.Player = *playerRaw
fullInfo.Profile = *profileRaw
squadMembersWithInformation = append(squadMembersWithInformation, fullInfo)
}

View File

@ -4,62 +4,10 @@
package users
import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"time"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
// GetProfile returns last saved profile of player
func (u *Users) GetProfile(playerID int) (dbmapping.Profile, bool) {
profileRaw := dbmapping.Profile{}
err := c.Db.Get(&profileRaw, c.Db.Rebind("SELECT * FROM profiles WHERE player_id=? ORDER BY created_at DESC LIMIT 1"), playerID)
if err != nil {
c.Log.Error(err)
return profileRaw, false
}
return profileRaw, true
}
// GetPlayerByID returns dbmapping.Player instance with given ID.
func (u *Users) GetPlayerByID(playerID int) (dbmapping.Player, bool) {
playerRaw := dbmapping.Player{}
err := c.Db.Get(&playerRaw, c.Db.Rebind("SELECT * FROM players WHERE id=?"), playerID)
if err != nil {
c.Log.Error(err.Error())
return playerRaw, false
}
return playerRaw, true
}
// GetOrCreatePlayer seeks for player in database via Telegram ID.
// In case, when there is no player with such ID, new player will be created.
func (u *Users) GetOrCreatePlayer(telegramID int) (dbmapping.Player, bool) {
playerRaw := dbmapping.Player{}
err := c.Db.Get(&playerRaw, c.Db.Rebind("SELECT * FROM players WHERE telegram_id=? ORDER BY created_at desc LIMIT 1"), telegramID)
if err != nil {
c.Log.Error("Message user not found in database.")
c.Log.Error(err.Error())
// Create "nobody" user
playerRaw.TelegramID = telegramID
playerRaw.LeagueID = 0
playerRaw.Status = "nobody"
playerRaw.CreatedAt = time.Now().UTC()
playerRaw.UpdatedAt = time.Now().UTC()
_, err = c.Db.NamedExec("INSERT INTO players VALUES(NULL, :telegram_id, :league_id, :status, :created_at, :updated_at)", &playerRaw)
if err != nil {
c.Log.Error(err.Error())
return playerRaw, false
}
} else {
c.Log.Debug("Message user found in database.")
}
return playerRaw, true
}
// GetPrettyName returns "pretty" name of user (first_name + last name or username)
func (u *Users) GetPrettyName(user *tgbotapi.User) string {
userName := user.FirstName

View File

@ -15,15 +15,14 @@ import (
// Internal functions
func (u *Users) fillProfilePokememe(profileID int, meme string, attack string, rarity string) {
spkRaw := dbmapping.Pokememe{}
err := c.Db.Get(&spkRaw, c.Db.Rebind("SELECT * FROM pokememes WHERE name='"+meme+"';"))
spkRaw, err := c.DataCache.GetPokememeByName(meme)
if err != nil {
c.Log.Error(err.Error())
} else {
attackInt := c.Statistics.GetPoints(attack)
ppk := dbmapping.ProfilePokememe{}
ppk.ProfileID = profileID
ppk.PokememeID = spkRaw.ID
ppk.PokememeID = spkRaw.Pokememe.ID
ppk.PokememeAttack = attackInt
ppk.PokememeRarity = rarity
ppk.CreatedAt = time.Now().UTC()
@ -75,12 +74,13 @@ func (u *Users) ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Playe
currentString := string(profileRunesArray[i])
currentRunes := profileRunesArray[i]
if strings.HasPrefix(currentString, "🈸") || strings.HasPrefix(currentString, "🈳 ") || strings.HasPrefix(currentString, "🈵") {
err1 := c.Db.Get(&league, c.Db.Rebind("SELECT * FROM leagues WHERE symbol='"+string(currentRunes[0])+"'"))
if err1 != nil {
c.Log.Error(err1.Error())
leagueRaw, err := c.DataCache.GetLeagueBySymbol(string(currentRunes[0]))
if err != nil {
c.Log.Error(err.Error())
u.profileAddFailureMessage(update)
return "fail"
}
league = *leagueRaw
for j := range currentRunes {
if j > 1 {
nickname += string(currentRunes[j])
@ -209,10 +209,9 @@ func (u *Users) ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Playe
}
// Information is gathered, let's create profile in database!
weaponRaw := dbmapping.Weapon{}
err2 := c.Db.Get(&weaponRaw, c.Db.Rebind("SELECT * FROM weapons WHERE name='"+weapon+"'"))
if err2 != nil {
c.Log.Error(err2.Error())
weaponRaw, err := c.DataCache.GetWeaponTypeByName(weapon)
if err != nil {
c.Log.Error(err.Error())
}
if playerRaw.LeagueID == 0 {
@ -221,26 +220,17 @@ func (u *Users) ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Playe
if playerRaw.Status == "nobody" {
playerRaw.Status = "common"
}
_, err4 := c.Db.NamedExec("UPDATE `players` SET league_id=:league_id, status=:status WHERE id=:id", &playerRaw)
if err4 != nil {
c.Log.Error(err4.Error())
_, err = c.DataCache.UpdatePlayerFields(playerRaw)
if err != nil {
u.profileAddFailureMessage(update)
return "fail"
}
} else if playerRaw.LeagueID != league.ID {
// Duplicate profile: user changed league, beware!
// User changed league, beware!
playerRaw.LeagueID = league.ID
playerRaw.Status = "league_changed"
playerRaw.CreatedAt = time.Now().UTC()
_, err5 := c.Db.NamedExec("INSERT INTO players VALUES(NULL, :telegram_id, :league_id, :status, :created_at, :updated_at)", &playerRaw)
if err5 != nil {
c.Log.Error(err5.Error())
u.profileAddFailureMessage(update)
return "fail"
}
err6 := c.Db.Get(&playerRaw, c.Db.Rebind("SELECT * FROM players WHERE telegram_id='"+strconv.Itoa(playerRaw.TelegramID)+"' AND league_id='"+strconv.Itoa(league.ID)+"';"))
if err6 != nil {
c.Log.Error(err6.Error())
_, err = c.DataCache.UpdatePlayerFields(playerRaw)
if err != nil {
u.profileAddFailureMessage(update)
return "fail"
}
@ -261,25 +251,22 @@ func (u *Users) ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Playe
profileRaw.Crystalls = crystallsInt
profileRaw.CreatedAt = time.Now().UTC()
_, err3 := c.Db.NamedExec("INSERT INTO `profiles` VALUES(NULL, :player_id, :nickname, :telegram_nickname, :level_id, :pokeballs, :wealth, :pokememes_wealth, :exp, :egg_exp, :power, :weapon_id, :crystalls, :created_at)", &profileRaw)
if err3 != nil {
c.Log.Error(err3.Error())
newProfileID, err := c.DataCache.AddProfile(&profileRaw)
if err != nil {
c.Log.Error(err.Error())
u.profileAddFailureMessage(update)
return "fail"
}
err8 := c.Db.Get(&profileRaw, c.Db.Rebind("SELECT * FROM profiles WHERE player_id=? AND created_at=?"), profileRaw.PlayerID, profileRaw.CreatedAt)
if err8 != nil {
c.Log.Error(err8.Error())
c.Log.Error("Profile isn't added!")
_, err = c.DataCache.GetProfileByID(newProfileID)
if err != nil {
c.Log.Error(err.Error())
u.profileAddFailureMessage(update)
return "fail"
}
playerRaw.UpdatedAt = time.Now().UTC()
_, err7 := c.Db.NamedExec("UPDATE `players` SET updated_at=:updated_at WHERE id=:id", &playerRaw)
if err7 != nil {
c.Log.Error(err7.Error())
err = c.DataCache.UpdatePlayerTimestamp(playerRaw.ID)
if err != nil {
u.profileAddFailureMessage(update)
return "fail"
}
@ -304,11 +291,11 @@ func (u *Users) ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Playe
rarity = "super liber"
meme = strings.Replace(meme, "🔷", "", 1)
}
if strings.HasPrefix(meme, "❄") {
if strings.HasPrefix(meme, "❄") {
rarity = "new year"
meme = strings.Replace(meme, "❄", "", 1)
meme = strings.Replace(meme, "❄", "", 1)
}
u.fillProfilePokememe(profileRaw.ID, meme, attack, rarity)
u.fillProfilePokememe(newProfileID, meme, attack, rarity)
}
u.profileAddSuccessMessage(update, league.ID, profileRaw.LevelID)

View File

@ -29,12 +29,9 @@ func (u *Users) FindByLevel(update *tgbotapi.Update) string {
return "fail"
}
usersArray, ok := u.findUsersByLevel(levelID)
if !ok {
return "fail"
}
users := u.findUsersByLevel(levelID)
u.foundUsersMessage(update, usersArray)
u.foundUsersMessage(update, users)
return "ok"
}
@ -47,12 +44,9 @@ func (u *Users) FindByName(update *tgbotapi.Update) string {
return "fail"
}
usersArray, ok := u.findUserByName(commandArgs)
if !ok {
return "fail"
}
users := u.findUserByName(commandArgs)
u.foundUsersMessage(update, usersArray)
u.foundUsersMessage(update, users)
return "ok"
}
@ -66,20 +60,22 @@ func (u *Users) ForeignProfileMessage(update *tgbotapi.Update) string {
return "fail"
}
playerRaw, ok := u.GetPlayerByID(userID)
if !ok {
playerRaw, err := c.DataCache.GetPlayerByID(userID)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}
_, ok = u.GetProfile(playerRaw.ID)
if !ok {
_, err = c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
return c.Talkers.BotError(update)
}
return u.ProfileMessage(update, &playerRaw)
return u.ProfileMessage(update, playerRaw)
}
// ProfileAddEffectsMesage shows when user tries to post profile with effects enabled
// ProfileAddEffectsMessage shows when user tries to post profile with effects enabled
func (u *Users) ProfileAddEffectsMessage(update *tgbotapi.Update) string {
message := "*Наркоман, штоле?*\n\n"
message += "Бот не принимает профили во время активированных эффектов. Закончи свои дела и принеси чистый профиль через полчаса."
@ -94,12 +90,13 @@ func (u *Users) ProfileAddEffectsMessage(update *tgbotapi.Update) string {
// ProfileMessage shows current player's profile
func (u *Users) ProfileMessage(update *tgbotapi.Update, playerRaw *dbmapping.Player) string {
profileRaw, ok := u.GetProfile(playerRaw.ID)
if !ok {
profileRaw, err := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
if err != nil {
c.Log.Error(err.Error())
return c.Talkers.AnyMessageUnauthorized(update)
}
league := dbmapping.League{}
err := c.Db.Get(&league, c.Db.Rebind("SELECT * FROM leagues WHERE id=?"), playerRaw.LeagueID)
err = c.Db.Get(&league, c.Db.Rebind("SELECT * FROM leagues WHERE id=?"), playerRaw.LeagueID)
if err != nil {
c.Log.Error(err)
}
@ -210,11 +207,8 @@ func (u *Users) UsersList(update *tgbotapi.Update) string {
if page == 0 {
page = 1
}
usersArray, ok := u.getUsersWithProfiles()
if !ok {
return c.Talkers.BotError(update)
}
users := c.DataCache.GetPlayersWithCurrentProfiles()
u.usersList(update, page, usersArray)
u.usersList(update, page, users)
return "ok"
}

View File

@ -6,84 +6,70 @@ package users
import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"sort"
"strconv"
"strings"
"time"
)
// Internal functions for Users package
func (u *Users) getUsersWithProfiles() ([]dbmapping.PlayerProfile, bool) {
usersArray := []dbmapping.PlayerProfile{}
players := []dbmapping.Player{}
err := c.Db.Select(&players, "SELECT * FROM players")
if err != nil {
c.Log.Error(err)
return usersArray, false
}
func (u *Users) findUsersByLevel(levelID int) map[int]*dbmapping.PlayerProfile {
selectedUsers := make(map[int]*dbmapping.PlayerProfile)
allUsers := c.DataCache.GetPlayersWithCurrentProfiles()
for i := range players {
playerWithProfile := dbmapping.PlayerProfile{}
profile, ok := u.GetProfile(players[i].ID)
if !ok {
playerWithProfile.HaveProfile = false
} else {
playerWithProfile.HaveProfile = true
}
playerWithProfile.Profile = profile
playerWithProfile.Player = players[i]
league := dbmapping.League{}
if players[i].LeagueID != 0 {
err = c.Db.Get(&league, c.Db.Rebind("SELECT * FROM leagues WHERE id=?"), players[i].LeagueID)
if err != nil {
c.Log.Error(err.Error())
return usersArray, false
for i := range allUsers {
if allUsers[i].Profile.LevelID == levelID {
if allUsers[i].Player.UpdatedAt.After(time.Now().UTC().Add(-72 * time.Hour)) {
selectedUsers[i] = allUsers[i]
}
}
playerWithProfile.League = league
usersArray = append(usersArray, playerWithProfile)
}
return usersArray, true
return selectedUsers
}
func (u *Users) findUsersByLevel(levelID int) ([]dbmapping.ProfileWithAddons, bool) {
selectedUsers := []dbmapping.ProfileWithAddons{}
func (u *Users) findUserByName(pattern string) map[int]*dbmapping.PlayerProfile {
selectedUsers := make(map[int]*dbmapping.PlayerProfile)
allUsers := c.DataCache.GetPlayersWithCurrentProfiles()
err := c.Db.Select(&selectedUsers, c.Db.Rebind("SELECT p.*, l.symbol AS league_symbol, l.id AS league_id, pl.telegram_id FROM players pl, profiles p, leagues l WHERE pl.id = p.player_id AND l.id = pl.league_id AND p.created_at > NOW() - INTERVAL 72 HOUR AND p.level_id = ? GROUP BY player_id"), levelID)
if err != nil {
c.Log.Error(err.Error())
return selectedUsers, false
for i := range allUsers {
matchedPattern := false
if strings.Contains(strings.ToLower(allUsers[i].Profile.Nickname), strings.ToLower(pattern)) {
matchedPattern = true
}
if strings.Contains(strings.ToLower(allUsers[i].Profile.TelegramNickname), strings.ToLower(pattern)) {
matchedPattern = true
}
if matchedPattern {
selectedUsers[i] = allUsers[i]
}
}
return selectedUsers, true
return selectedUsers
}
func (u *Users) findUserByName(pattern string) ([]dbmapping.ProfileWithAddons, bool) {
selectedUsers := []dbmapping.ProfileWithAddons{}
err := c.Db.Select(&selectedUsers, c.Db.Rebind("SELECT * FROM (SELECT p.*, l.symbol AS league_symbol, l.id AS league_id, pl.telegram_id FROM players pl, profiles p, leagues l WHERE p.player_id = pl.id AND l.id = pl.league_id AND (p.nickname LIKE ? OR p.telegram_nickname LIKE ?) ORDER BY p.id DESC LIMIT 100000) AS find_users_table GROUP BY player_id"), "%"+pattern+"%", "%"+pattern+"%")
if err != nil {
c.Log.Error(err.Error())
return selectedUsers, false
func (u *Users) foundUsersMessage(update *tgbotapi.Update, users map[int]*dbmapping.PlayerProfile) {
var keys []int
for i := range users {
keys = append(keys, i)
}
sort.Ints(keys)
return selectedUsers, true
}
func (u *Users) foundUsersMessage(update *tgbotapi.Update, usersArray []dbmapping.ProfileWithAddons) {
message := "*Найденные игроки:*\n"
for i := range usersArray {
message += "#" + strconv.Itoa(usersArray[i].PlayerID)
message += " " + usersArray[i].LeagueSymbol
message += " " + usersArray[i].Nickname
if usersArray[i].TelegramNickname != "" {
message += " (@" + u.FormatUsername(usersArray[i].TelegramNickname) + ")"
for _, i := range keys {
message += "#" + strconv.Itoa(users[i].Player.ID)
if users[i].HaveProfile {
message += " " + users[i].League.Symbol
message += " " + users[i].Profile.Nickname
if users[i].Profile.TelegramNickname != "" {
message += " (@" + u.FormatUsername(users[i].Profile.TelegramNickname) + ")"
}
}
message += " /profile" + strconv.Itoa(usersArray[i].PlayerID) + "\n"
message += "Telegram ID: " + strconv.Itoa(usersArray[i].TelegramID) + "\n"
message += "Последнее обновление: " + usersArray[i].CreatedAt.Format("02.01.2006 15:04:05") + "\n"
message += " /profile" + strconv.Itoa(users[i].Player.ID) + "\n"
message += "Telegram ID: " + strconv.Itoa(users[i].Player.TelegramID) + "\n"
message += "Последнее обновление: " + users[i].Player.CreatedAt.Format("02.01.2006 15:04:05") + "\n"
if len(message) > 2000 {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, message)
@ -130,12 +116,12 @@ func (u *Users) profileAddFailureMessage(update *tgbotapi.Update) {
c.Bot.Send(msg)
}
func (u *Users) usersList(update *tgbotapi.Update, page int, usersArray []dbmapping.PlayerProfile) {
func (u *Users) usersList(update *tgbotapi.Update, page int, users map[int]*dbmapping.PlayerProfile) {
message := "*Зарегистрированные пользователи бота*\n"
message += "Список отсортирован по ID регистрации.\n"
message += "Количество зарегистрированных пользователей: " + strconv.Itoa(len(usersArray)) + "\n"
message += "Количество зарегистрированных пользователей: " + strconv.Itoa(len(users)) + "\n"
message += "Отображаем пользователей с " + strconv.Itoa(((page-1)*25)+1) + " по " + strconv.Itoa(page*25) + "\n"
if len(usersArray) > page*25 {
if len(users) > page*25 {
message += "Переход на следующую страницу: /users" + strconv.Itoa(page+1)
}
if page > 1 {
@ -143,30 +129,36 @@ func (u *Users) usersList(update *tgbotapi.Update, page int, usersArray []dbmapp
}
message += "\n\n"
for i := range usersArray {
var keys []int
for i := range users {
keys = append(keys, i)
}
sort.Ints(keys)
for _, i := range keys {
if (i+1 > 25*(page-1)) && (i+1 < (25*page)+1) {
message += "#" + strconv.Itoa(usersArray[i].Player.ID)
if usersArray[i].HaveProfile {
message += " " + usersArray[i].League.Symbol
message += " " + usersArray[i].Profile.Nickname
if usersArray[i].Profile.TelegramNickname != "" {
message += " (@" + u.FormatUsername(usersArray[i].Profile.TelegramNickname) + ")"
message += "#" + strconv.Itoa(users[i].Player.ID)
if users[i].HaveProfile {
message += " " + users[i].League.Symbol
message += " " + users[i].Profile.Nickname
if users[i].Profile.TelegramNickname != "" {
message += " (@" + u.FormatUsername(users[i].Profile.TelegramNickname) + ")"
}
message += " /profile" + strconv.Itoa(usersArray[i].Player.ID) + "\n"
message += "Telegram ID: " + strconv.Itoa(usersArray[i].Player.TelegramID) + "\n"
message += "Последнее обновление: " + usersArray[i].Profile.CreatedAt.Format("02.01.2006 15:04:05") + "\n"
message += " /profile" + strconv.Itoa(users[i].Player.ID) + "\n"
message += "Telegram ID: " + strconv.Itoa(users[i].Player.TelegramID) + "\n"
message += "Последнее обновление: " + users[i].Profile.CreatedAt.Format("02.01.2006 15:04:05") + "\n"
} else {
if usersArray[i].Player.Status == "special" {
if users[i].Player.Status == "special" {
message += " _суперюзер_\n"
} else {
message += " _без профиля_\n"
}
message += "Telegram ID: " + strconv.Itoa(usersArray[i].Player.TelegramID) + "\n"
message += "Telegram ID: " + strconv.Itoa(users[i].Player.TelegramID) + "\n"
}
}
}
if len(usersArray) > page*25 {
if len(users) > page*25 {
message += "\n"
message += "Переход на следующую страницу: /users" + strconv.Itoa(page+1)
}

View File

@ -15,9 +15,6 @@ type UsersInterface interface {
ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Player) string
GetPrettyName(user *tgbotapi.User) string
GetProfile(playerID int) (dbmapping.Profile, bool)
GetOrCreatePlayer(telegramID int) (dbmapping.Player, bool)
GetPlayerByID(playerID int) (dbmapping.Player, bool)
PlayerBetterThan(playerRaw *dbmapping.Player, powerLevel string) bool
FindByLevel(update *tgbotapi.Update) string

View File

@ -9,22 +9,25 @@ import (
)
func (w *Welcomer) groupWelcomeUser(update *tgbotapi.Update, newUser *tgbotapi.User) string {
playerRaw, ok := c.Users.GetOrCreatePlayer(newUser.ID)
if !ok {
playerRaw, err := c.DataCache.GetPlayerByTelegramID(newUser.ID)
if err != nil {
c.Log.Error(err.Error())
return "fail"
}
_, profileExist := c.Users.GetProfile(playerRaw.ID)
_, profileExist := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
message := "*Бот Инстинкта приветствует тебя, *"
message += c.Users.GetPrettyName(newUser)
message += "*!*\n\n"
if profileExist {
if profileExist == nil {
if playerRaw.LeagueID != 1 {
w.alertSpyUser(update, newUser)
}
} else {
c.Log.Info("Following profile error is OK.")
c.Log.Info(err.Error())
w.alertUserWithoutProfile(update, newUser)
}