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'. # 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]] [[projects]]
name = "github.com/go-sql-driver/mysql" name = "github.com/go-sql-driver/mysql"
packages = ["."] packages = ["."]
@ -16,8 +28,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/jmoiron/sqlx" name = "github.com/jmoiron/sqlx"
packages = [".","reflectx"] packages = [
revision = "3379e5993990b1f927fc8db926485e6f6becf2d2" ".",
"reflectx"
]
revision = "05cef0741ade10ca668982355b3f3f0bcf0ff0a8"
[[projects]] [[projects]]
name = "github.com/pressly/goose" name = "github.com/pressly/goose"
@ -34,8 +49,8 @@
[[projects]] [[projects]]
name = "github.com/sirupsen/logrus" name = "github.com/sirupsen/logrus"
packages = ["."] packages = ["."]
revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e" revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
version = "v1.0.3" version = "v1.0.4"
[[projects]] [[projects]]
name = "github.com/technoweenie/multipartstreamer" name = "github.com/technoweenie/multipartstreamer"
@ -47,35 +62,39 @@
branch = "master" branch = "master"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ssh/terminal"] packages = ["ssh/terminal"]
revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94" revision = "0efb9460aaf800c6376acf625be2853bceac2e06"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix","windows"] packages = [
revision = "665f6529cca930e27b831a0d1dafffbe1c172924" "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]] [[projects]]
branch = "v2" branch = "v2"
name = "gopkg.in/yaml.v2" name = "gopkg.in/yaml.v2"
packages = ["."] packages = ["."]
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f" revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
[[projects]]
branch = "master"
name = "lab.pztrn.name/golibs/flagger"
packages = ["."]
revision = "5cf6a6dd8fe8a4f37d1bb1e3deb5121c6e923668"
[[projects]]
branch = "master"
name = "lab.pztrn.name/golibs/mogrus"
packages = ["."]
revision = "a888e29e1fff06c2e6ebc607055a02de22a6ddae"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "e26a19cb9a3e96a96b1d51599ead99341edc2278667ddf8a7dac4cd197fbe09d" inputs-digest = "99811036e66782056cb66f37e4a2570ac81f0e3da2fe399c4d54d83acc5a368d"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -1,4 +1,3 @@
# Gopkg.toml example # Gopkg.toml example
# #
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
@ -17,16 +16,51 @@
# source = "github.com/myfork/project2" # source = "github.com/myfork/project2"
# #
# [[override]] # [[override]]
# name = "github.com/x/y" # name = "github.com/x/y"
# version = "2.4.0" # 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]] [[constraint]]
name = "github.com/go-telegram-bot-api/telegram-bot-api" 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]] [[constraint]]
branch = "v2" branch = "v2"
name = "gopkg.in/yaml.v2" name = "gopkg.in/yaml.v2"
[prune]
go-tests = true
unused-packages = true

View File

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

View File

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

View File

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

View File

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

View File

@ -4,11 +4,11 @@
package connections package connections
import ( import (
"bitbucket.org/pztrn/mogrus"
"git.wtfteam.pro/fat0troll/i2_bot/lib/config"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/go-telegram-bot-api/telegram-bot-api" "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"git.wtfteam.pro/fat0troll/i2_bot/lib/config"
"lab.pztrn.name/golibs/mogrus"
) )
// BotInit initializes connection to Telegram // 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"` Crystalls int `db:"crystalls"`
CreatedAt time.Time `db:"created_at"` 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" return "fail"
} }
pokememe, ok := p.GetPokememeByID(strconv.Itoa(pokememeNum)) err := c.DataCache.DeletePokememeByID(pokememeNum)
if !ok {
return "fail"
}
_, err := c.Db.NamedExec("DELETE FROM pokememes WHERE id=:id", &pokememe.Pokememe)
if err != nil { if err != nil {
c.Log.Error(err.Error()) c.Log.Error(err.Error())
return "fail" 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 := "Покемем удалён." message := "Покемем удалён."
msg := tgbotapi.NewMessage(update.Message.Chat.ID, message) msg := tgbotapi.NewMessage(update.Message.Chat.ID, message)

View File

@ -5,184 +5,50 @@ package pokedexer
import ( import (
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping" "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 // External functions
// GetPokememes returns all existing pokememes, known by bot func (p *Pokedexer) getBestPokememes(playerID int) (map[int]*dbmapping.PokememeFull, bool) {
func (p *Pokedexer) GetPokememes() ([]dbmapping.PokememeFull, bool) { pokememesArray := make(map[int]*dbmapping.PokememeFull)
pokememesArray := []dbmapping.PokememeFull{}
pokememes := []dbmapping.Pokememe{} playerRaw, err := c.DataCache.GetPlayerByID(playerID)
err := c.Db.Select(&pokememes, "SELECT * FROM pokememes ORDER BY grade asc, name asc")
if err != nil { if err != nil {
c.Log.Error(err) c.Log.Error(err.Error())
return pokememesArray, false return pokememesArray, false
} }
profileRaw, err := c.DataCache.GetProfileByPlayerID(playerRaw.ID)
pokememesArray, ok := p.formFullPokememes(pokememes) if err != nil {
return pokememesArray, ok c.Log.Error(err.Error())
} return pokememesArray, false
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
} }
if playerRaw.LeagueID == 0 { if playerRaw.LeagueID == 0 {
return pokememesArray, false return pokememesArray, false
} }
// TODO: make it more complicated allPokememes := c.DataCache.GetAllPokememes()
pokememes := []dbmapping.Pokememe{}
if profileRaw.LevelID < 4 { if profileRaw.LevelID < 4 {
err := c.Db.Select(&pokememes, c.Db.Rebind("SELECT * FROM pokememes WHERE grade = ? ORDER BY attack DESC"), profileRaw.LevelID+1) for i := range allPokememes {
if err != nil { if allPokememes[i].Pokememe.Grade == profileRaw.LevelID+1 {
c.Log.Error(err) pokememesArray[allPokememes[i].Pokememe.Attack] = allPokememes[i]
return pokememesArray, false }
} }
} else { } 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) for i := range allPokememes {
if err != nil { if allPokememes[i].Pokememe.Grade == profileRaw.LevelID+1 {
c.Log.Error(err) matchLeague := false
return pokememesArray, false for j := range allPokememes[i].Elements {
} if allPokememes[i].Elements[j].LeagueID == playerRaw.LeagueID {
} matchLeague = true
}
pokememesArray, ok = p.formFullPokememes(pokememes) }
return pokememesArray, ok if matchLeague {
} pokememesArray[allPokememes[i].Pokememe.Attack] = allPokememes[i]
// 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 k := range pokememesElements { return pokememesArray, true
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
} }

View File

@ -4,248 +4,143 @@
package pokedexer package pokedexer
import ( import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping" "git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time" // "time"
) )
// ParsePokememe parses pokememe, forwarded from PokememeBroBot, to database // ParsePokememe parses pokememe, forwarded from PokememeBroBot, to database
func (p *Pokedexer) ParsePokememe(update *tgbotapi.Update, playerRaw *dbmapping.Player) string { func (p *Pokedexer) ParsePokememe(update *tgbotapi.Update, playerRaw *dbmapping.Player) string {
text := update.Message.Text pokememeStringsArray := strings.Split(update.Message.Text, "\n")
var defendablePokememe = false
pokememeStringsArray := strings.Split(text, "\n")
pokememeRunesArray := make([][]rune, 0) pokememeRunesArray := make([][]rune, 0)
for i := range pokememeStringsArray { for i := range pokememeStringsArray {
pokememeRunesArray = append(pokememeRunesArray, []rune(pokememeStringsArray[i])) pokememeRunesArray = append(pokememeRunesArray, []rune(pokememeStringsArray[i]))
} }
if len(pokememeRunesArray) == 13 { pokememeData := make(map[string]string)
defendablePokememe = true 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 // Image
elements := []dbmapping.Element{} for _, entity := range *update.Message.Entities {
elementEmojis := make([]string, 0) if entity.Type == "text_link" && entity.URL != "" {
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][11])) pokememeData["image"] = entity.URL
if len(pokememeRunesArray[4]) > 12 { }
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][13]))
}
if len(pokememeRunesArray[4]) > 14 {
elementEmojis = append(elementEmojis, string(pokememeRunesArray[4][15]))
} }
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 { if err != nil {
c.Log.Error(err.Error()) c.Log.Error(err.Error())
p.pokememeAddFailureMessage(update) p.pokememeAddFailureMessage(update)
return "fail" return "fail"
} }
// Getting hit-points pokememeData["creator_id"] = strconv.Itoa(playerRaw.ID)
hitPointsRx := regexp.MustCompile("(\\d|\\.)+(K|M)?")
hitPoints := hitPointsRx.FindAllString(string(pokememeRunesArray[5]), -1) c.Log.Debugln("Pokememe data: ", pokememeData)
if len(hitPoints) != 3 { c.Log.Debugln("Elements: ", pokememeElements)
c.Log.Error("Can't parse hitpoints!") c.Log.Debugln("Locations: ", pokememeLocations)
c.Log.Debug(pokememeRunesArray[5])
if len(pokememeElements) == 0 {
p.pokememeAddFailureMessage(update) p.pokememeAddFailureMessage(update)
return "fail" return "fail"
} }
defence := "0" if len(pokememeLocations) == 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())
p.pokememeAddFailureMessage(update) p.pokememeAddFailureMessage(update)
return "fail" return "fail"
} }
// Getting new pokememe newPokememeID, err := c.DataCache.AddPokememe(pokememeData, pokememeLocations, pokememeElements)
err5 := c.Db.Get(&pokememe, c.Db.Rebind("SELECT * FROM pokememes WHERE grade='"+grade+"' AND name='"+name+"';")) if err != nil {
if err5 != nil { c.Log.Error(err.Error())
c.Log.Error("Pokememe isn't added!")
p.pokememeAddFailureMessage(update) p.pokememeAddFailureMessage(update)
return "fail" 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) p.pokememeAddSuccessMessage(update, newPokememeID)
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)
return "ok" return "ok"
} }

View File

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

View File

@ -4,8 +4,8 @@
package pokedexerinterface package pokedexerinterface
import ( import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping" "git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping"
"github.com/go-telegram-bot-api/telegram-bot-api"
) )
// PokedexerInterface implements Pokedexer for importing via appcontext. // PokedexerInterface implements Pokedexer for importing via appcontext.
@ -16,8 +16,5 @@ type PokedexerInterface interface {
PokememeInfo(update *tgbotapi.Update, playerRaw *dbmapping.Player) string PokememeInfo(update *tgbotapi.Update, playerRaw *dbmapping.Player) string
BestPokememesList(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 DeletePokememe(update *tgbotapi.Update) string
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,12 +27,18 @@ func (s *Statistics) SquadStatictics(squadID int) string {
for i := range squadMembers { for i := range squadMembers {
fullInfo := dbmapping.SquadPlayerFull{} fullInfo := dbmapping.SquadPlayerFull{}
playerRaw, _ := c.Users.GetPlayerByID(squadMembers[i].PlayerID) playerRaw, err := c.DataCache.GetPlayerByID(squadMembers[i].PlayerID)
profileRaw, _ := c.Users.GetProfile(playerRaw.ID) 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.Squad = squad
fullInfo.Player = playerRaw fullInfo.Player = *playerRaw
fullInfo.Profile = profileRaw fullInfo.Profile = *profileRaw
squadMembersWithInformation = append(squadMembersWithInformation, fullInfo) squadMembersWithInformation = append(squadMembersWithInformation, fullInfo)
} }

View File

@ -4,62 +4,10 @@
package users package users
import ( import (
"github.com/go-telegram-bot-api/telegram-bot-api"
"git.wtfteam.pro/fat0troll/i2_bot/lib/dbmapping" "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) // GetPrettyName returns "pretty" name of user (first_name + last name or username)
func (u *Users) GetPrettyName(user *tgbotapi.User) string { func (u *Users) GetPrettyName(user *tgbotapi.User) string {
userName := user.FirstName userName := user.FirstName

View File

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

View File

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

View File

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

View File

@ -15,9 +15,6 @@ type UsersInterface interface {
ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Player) string ParseProfile(update *tgbotapi.Update, playerRaw *dbmapping.Player) string
GetPrettyName(user *tgbotapi.User) 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 PlayerBetterThan(playerRaw *dbmapping.Player, powerLevel string) bool
FindByLevel(update *tgbotapi.Update) string FindByLevel(update *tgbotapi.Update) string

View File

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