snow/person/person.go

627 lines
16 KiB
Go
Raw Permalink Normal View History

2023-10-31 22:40:14 +00:00
package person
import (
"fmt"
"strings"
"time"
2024-01-29 23:46:22 +00:00
"github.com/ectrc/snow/aid"
2023-10-31 22:40:14 +00:00
"github.com/ectrc/snow/storage"
"github.com/google/uuid"
)
type Person struct {
ID string
2023-10-31 22:40:14 +00:00
DisplayName string
2024-02-04 15:21:16 +00:00
RefundTickets int
2024-02-09 21:51:26 +00:00
Permissions Permission
AthenaProfile *Profile
2023-10-31 22:40:14 +00:00
CommonCoreProfile *Profile
CommonPublicProfile *Profile
Profile0Profile *Profile
CollectionsProfile *Profile
2023-12-08 15:35:00 +00:00
CreativeProfile *Profile
CurrentSeasonStats *SeasonStats
AllSeasonsStats aid.GenericSyncMap[SeasonStats]
Discord *storage.DB_DiscordPerson
Receipts *ReceiptMutex
2024-02-10 01:55:56 +00:00
BanHistory aid.GenericSyncMap[storage.DB_BanStatus]
2024-01-30 16:34:17 +00:00
Relationships aid.GenericSyncMap[Relationship]
2024-02-11 19:09:23 +00:00
Parties aid.GenericSyncMap[Party]
2024-02-14 20:30:55 +00:00
Invites aid.GenericSyncMap[PartyInvite]
2024-02-17 14:40:17 +00:00
Intentions aid.GenericSyncMap[PartyIntention]
2023-10-31 22:40:14 +00:00
}
func NewPerson() *Person {
id := uuid.New().String()
2023-10-31 22:40:14 +00:00
return &Person{
ID: id,
2023-11-01 01:24:42 +00:00
DisplayName: uuid.New().String(),
2024-02-09 21:51:26 +00:00
Permissions: 0,
2024-02-04 15:21:16 +00:00
RefundTickets: 3,
2024-01-03 23:25:17 +00:00
AthenaProfile: NewProfile("athena"),
CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"),
Profile0Profile: NewProfile("profile0"),
CollectionsProfile: NewProfile("collections"),
CreativeProfile: NewProfile("creative"),
Receipts: NewReceiptMutex(id),
AllSeasonsStats: aid.GenericSyncMap[SeasonStats]{},
2024-02-12 20:29:02 +00:00
BanHistory: aid.GenericSyncMap[storage.DB_BanStatus]{},
Relationships: aid.GenericSyncMap[Relationship]{},
Parties: aid.GenericSyncMap[Party]{},
2024-02-14 20:30:55 +00:00
Invites: aid.GenericSyncMap[PartyInvite]{},
2024-02-17 14:40:17 +00:00
Intentions: aid.GenericSyncMap[PartyIntention]{},
2024-01-03 23:25:17 +00:00
}
}
func NewPersonWithCustomID(id string) *Person {
return &Person{
ID: id,
DisplayName: uuid.New().String(),
2024-02-09 21:51:26 +00:00
Permissions: 0,
2024-02-04 15:21:16 +00:00
RefundTickets: 3,
AthenaProfile: NewProfile("athena"),
CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"),
Profile0Profile: NewProfile("profile0"),
CollectionsProfile: NewProfile("collections"),
2023-12-08 15:35:00 +00:00
CreativeProfile: NewProfile("creative"),
Receipts: NewReceiptMutex(id),
AllSeasonsStats: aid.GenericSyncMap[SeasonStats]{},
2024-02-12 20:29:02 +00:00
BanHistory: aid.GenericSyncMap[storage.DB_BanStatus]{},
Relationships: aid.GenericSyncMap[Relationship]{},
Parties: aid.GenericSyncMap[Party]{},
2024-02-14 20:30:55 +00:00
Invites: aid.GenericSyncMap[PartyInvite]{},
2024-02-17 14:40:17 +00:00
Intentions: aid.GenericSyncMap[PartyIntention]{},
2023-10-31 22:40:14 +00:00
}
}
func Find(personId string) *Person {
if cache == nil {
cache = NewPersonsCacheMutex()
2023-10-31 22:40:14 +00:00
}
cachedPerson := cache.GetPerson(personId)
if cachedPerson != nil {
return cachedPerson
2023-10-31 22:40:14 +00:00
}
person := storage.Repo.GetPersonFromDB(personId)
if person == nil {
return nil
}
2024-01-29 23:46:22 +00:00
return findHelper(person, false, true)
}
func FindShallow(personId string) *Person {
if cache == nil {
cache = NewPersonsCacheMutex()
}
cachedPerson := cache.GetPerson(personId)
if cachedPerson != nil {
return cachedPerson
}
person := storage.Repo.GetPersonFromDB(personId)
if person == nil {
return nil
}
return findHelper(person, true, false)
}
func FindByDisplay(displayName string) *Person {
if cache == nil {
cache = NewPersonsCacheMutex()
}
cachedPerson := cache.GetPersonByDisplay(displayName)
if cachedPerson != nil {
return cachedPerson
}
person := storage.Repo.GetPersonByDisplayFromDB(displayName)
if person == nil {
return nil
}
2024-01-29 23:46:22 +00:00
return findHelper(person, false, true)
}
func FindByDisplayShallow(displayName string) *Person {
if cache == nil {
cache = NewPersonsCacheMutex()
}
cachedPerson := cache.GetPersonByDisplay(displayName)
if cachedPerson != nil {
return cachedPerson
}
person := storage.Repo.GetPersonByDisplayFromDB(displayName)
if person == nil {
return nil
}
return findHelper(person, true, false)
}
func FindByDiscord(discordId string) *Person {
if cache == nil {
cache = NewPersonsCacheMutex()
}
cachedPerson := cache.GetPersonByDiscordID(discordId)
if cachedPerson != nil {
return cachedPerson
}
person := storage.Repo.GetPersonByDiscordIDFromDB(discordId)
if person == nil {
return nil
}
2024-01-29 23:46:22 +00:00
return findHelper(person, false, true)
}
2024-01-29 23:46:22 +00:00
func findHelper(databasePerson *storage.DB_Person, shallow bool, save bool) *Person {
athenaProfile := NewProfile("athena")
commonCoreProfile := NewProfile("common_core")
commonPublicProfile := NewProfile("common_public")
profile0 := NewProfile("profile0")
collectionsProfile := NewProfile("collections")
2023-12-08 15:35:00 +00:00
creativeProfile := NewProfile("creative")
receipts := NewReceiptMutex(databasePerson.ID)
for _, profile := range databasePerson.Profiles {
if profile.Type == "athena" {
athenaProfile.ID = profile.ID
athenaProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_core" {
commonCoreProfile.ID = profile.ID
commonCoreProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "common_public" {
commonPublicProfile.ID = profile.ID
commonPublicProfile = FromDatabaseProfile(&profile)
}
if profile.Type == "profile0" {
profile0.ID = profile.ID
profile0 = FromDatabaseProfile(&profile)
}
if profile.Type == "collections" {
collectionsProfile.ID = profile.ID
collectionsProfile = FromDatabaseProfile(&profile)
}
2023-12-08 15:35:00 +00:00
if profile.Type == "creative" {
creativeProfile.ID = profile.ID
creativeProfile = FromDatabaseProfile(&profile)
}
}
for _, receipt := range databasePerson.Receipts {
receipts.AddReceipt(FromDatabaseReceipt(&receipt))
}
person := &Person{
ID: databasePerson.ID,
DisplayName: databasePerson.DisplayName,
2024-02-09 21:51:26 +00:00
Permissions: Permission(databasePerson.Permissions),
AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile,
Profile0Profile: profile0,
CollectionsProfile: collectionsProfile,
2023-12-08 15:35:00 +00:00
CreativeProfile: creativeProfile,
Discord: &databasePerson.Discord,
2024-02-04 15:21:16 +00:00
RefundTickets: databasePerson.RefundTickets,
Receipts: receipts,
AllSeasonsStats: aid.GenericSyncMap[SeasonStats]{},
BanHistory: aid.GenericSyncMap[storage.DB_BanStatus]{},
2024-01-30 16:34:17 +00:00
Relationships: aid.GenericSyncMap[Relationship]{},
2024-02-12 20:29:02 +00:00
Parties: aid.GenericSyncMap[Party]{},
2024-02-14 20:30:55 +00:00
Invites: aid.GenericSyncMap[PartyInvite]{},
2024-02-17 14:40:17 +00:00
Intentions: aid.GenericSyncMap[PartyIntention]{},
2024-01-29 23:46:22 +00:00
}
2024-02-10 01:55:56 +00:00
for _, ban := range databasePerson.BanHistory {
person.BanHistory.Set(ban.ID, &ban)
}
for _, stat := range databasePerson.Stats {
person.AllSeasonsStats.Set(fmt.Sprint(stat.Season), FromDatabaseSeasonStats(stat))
if stat.Season == aid.Config.Fortnite.Season {
person.CurrentSeasonStats = FromDatabaseSeasonStats(stat)
}
}
if person.CurrentSeasonStats == nil {
person.CurrentSeasonStats = NewSeasonStats(aid.Config.Fortnite.Season)
person.CurrentSeasonStats.PersonID = person.ID
person.AllSeasonsStats.Set(fmt.Sprint(aid.Config.Fortnite.Season), person.CurrentSeasonStats)
person.CurrentSeasonStats.Save()
}
2024-01-29 23:46:22 +00:00
if !shallow {
person.LoadRelationships()
2023-10-31 22:40:14 +00:00
}
2024-01-29 23:46:22 +00:00
if save {
cache.SavePerson(person)
}
2024-02-19 01:49:14 +00:00
return person
2023-10-31 22:40:14 +00:00
}
func AllFromDatabase() []*Person {
var persons []*Person
for _, person := range storage.Repo.GetAllPersons() {
persons = append(persons, Find(person.ID))
2023-10-31 22:40:14 +00:00
}
return persons
}
2023-11-05 02:43:21 +00:00
func AllFromCache() []*Person {
if cache == nil {
cache = NewPersonsCacheMutex()
}
var persons []*Person
cache.RangeEntry(func(key string, value *CacheEntry) bool {
persons = append(persons, value.Entry)
return true
})
return persons
}
func (p *Person) GetProfileFromType(profileType string) *Profile {
switch profileType {
case "athena":
return p.AthenaProfile
case "common_core":
return p.CommonCoreProfile
case "common_public":
return p.CommonPublicProfile
case "profile0":
return p.Profile0Profile
case "collections":
return p.CollectionsProfile
2023-12-08 15:35:00 +00:00
case "creative":
return p.CreativeProfile
}
return nil
}
2023-10-31 22:40:14 +00:00
func (p *Person) Save() {
dbPerson := p.ToDatabase()
storage.Repo.SavePerson(dbPerson)
2023-10-31 22:40:14 +00:00
}
2024-02-04 01:25:44 +00:00
func (p *Person) SaveShallow() {
dbPerson := p.ToDatabaseShallow()
storage.Repo.SavePerson(dbPerson)
}
2024-02-10 01:55:56 +00:00
func (p *Person) AddBan(reason string, issuedBy string, expiry ...string) {
t := time.Now().AddDate(0, 0, 7)
if len(expiry) > 0 && expiry[0] != "" {
parsed, err := aid.ParseDuration(expiry[0])
if err == nil {
t = time.Now().Add(parsed)
}
}
ban := &storage.DB_BanStatus{
ID: uuid.New().String(),
PersonID: p.ID,
2024-02-10 01:55:56 +00:00
IssuedBy: issuedBy,
Reason: reason,
Expiry: t,
}
2024-02-10 01:55:56 +00:00
p.BanHistory.Set(ban.ID, ban)
storage.Repo.SaveBanStatus(ban)
2023-12-14 19:47:54 +00:00
}
2024-02-10 01:55:56 +00:00
func (p *Person) ClearBans() {
p.BanHistory.Range(func(key string, ban *storage.DB_BanStatus) bool {
ban.Expiry = time.Now()
storage.Repo.SaveBanStatus(ban)
return true
})
}
func (p *Person) GetLatestActiveBan() *storage.DB_BanStatus {
var latestBan *storage.DB_BanStatus
p.BanHistory.Range(func(key string, ban *storage.DB_BanStatus) bool {
if latestBan == nil || ban.Expiry.After(latestBan.Expiry) {
latestBan = ban
}
return true
})
if latestBan != nil && latestBan.Expiry.Before(time.Now()) {
return nil
}
2024-02-10 01:55:56 +00:00
return latestBan
2023-12-14 19:47:54 +00:00
}
2024-02-09 21:51:26 +00:00
func (p *Person) AddPermission(permission Permission) {
p.Permissions |= permission
2024-02-04 01:25:44 +00:00
p.SaveShallow()
2023-12-14 19:47:54 +00:00
}
2024-02-09 21:51:26 +00:00
func (p *Person) RemovePermission(permission Permission) {
p.Permissions &= ^permission
2024-02-04 01:25:44 +00:00
p.SaveShallow()
2023-12-14 19:47:54 +00:00
}
func (p *Person) HasPermission(permission Permission) bool {
2024-02-09 21:51:26 +00:00
return p.Permissions & permission != 0
2023-12-14 19:47:54 +00:00
}
2023-10-31 22:40:14 +00:00
func (p *Person) ToDatabase() *storage.DB_Person {
dbPerson := storage.DB_Person{
ID: p.ID,
DisplayName: p.DisplayName,
2024-02-09 21:51:26 +00:00
Permissions: int64(p.Permissions),
2024-02-04 15:21:16 +00:00
RefundTickets: p.RefundTickets,
Receipts: []storage.DB_Receipt{},
BanHistory: []storage.DB_BanStatus{},
2023-10-31 22:40:14 +00:00
Profiles: []storage.DB_Profile{},
2023-12-11 00:06:41 +00:00
Stats: []storage.DB_SeasonStat{},
2023-12-14 19:47:54 +00:00
Discord: storage.DB_DiscordPerson{},
2023-12-10 23:54:31 +00:00
}
if p.Discord != nil {
dbPerson.Discord = *p.Discord
2023-10-31 22:40:14 +00:00
}
2023-11-01 00:05:17 +00:00
profilesToConvert := map[string]*Profile{
"common_core": p.CommonCoreProfile,
"athena": p.AthenaProfile,
"common_public": p.CommonPublicProfile,
"profile0": p.Profile0Profile,
"collections": p.CollectionsProfile,
2023-12-08 15:35:00 +00:00
"creative": p.CreativeProfile,
2023-11-01 00:05:17 +00:00
}
2023-10-31 22:40:14 +00:00
2024-02-10 01:55:56 +00:00
p.BanHistory.Range(func(key string, ban *storage.DB_BanStatus) bool {
dbPerson.BanHistory = append(dbPerson.BanHistory, *ban)
return true
})
p.Receipts.RangeReceipts(func(key string, receipt *Receipt) bool {
dbPerson.Receipts = append(dbPerson.Receipts, *receipt.ToDatabase())
return true
})
p.AllSeasonsStats.Range(func(key string, stat *SeasonStats) bool {
dbPerson.Stats = append(dbPerson.Stats, *stat.ToDatabase(p.ID))
return true
})
2023-10-31 22:40:14 +00:00
for profileType, profile := range profilesToConvert {
dbProfile := storage.DB_Profile{
ID: profile.ID,
PersonID: p.ID,
Type: profileType,
Items: []storage.DB_Item{},
Gifts: []storage.DB_Gift{},
Quests: []storage.DB_Quest{},
Loadouts: []storage.DB_Loadout{},
2024-02-04 02:05:31 +00:00
Attributes: []storage.DB_Attribute{},
Revision: profile.Revision,
2023-10-31 22:40:14 +00:00
}
profile.Items.RangeItems(func(id string, item *Item) bool {
dbProfile.Items = append(dbProfile.Items, *item.ToDatabase(p.ID))
return true
})
profile.Gifts.RangeGifts(func(id string, gift *Gift) bool {
dbProfile.Gifts = append(dbProfile.Gifts, *gift.ToDatabase(p.ID))
return true
})
profile.Quests.RangeQuests(func(id string, quest *Quest) bool {
dbProfile.Quests = append(dbProfile.Quests, *quest.ToDatabase(p.ID))
return true
})
2023-11-01 00:05:17 +00:00
profile.Attributes.RangeAttributes(func(key string, value *Attribute) bool {
dbProfile.Attributes = append(dbProfile.Attributes, *value.ToDatabase(p.ID))
2023-10-31 22:40:14 +00:00
return true
})
profile.Loadouts.RangeLoadouts(func(id string, loadout *Loadout) bool {
dbProfile.Loadouts = append(dbProfile.Loadouts, *loadout.ToDatabase(p.ID))
return true
})
2023-10-31 22:40:14 +00:00
dbPerson.Profiles = append(dbPerson.Profiles, dbProfile)
}
return &dbPerson
2023-11-01 00:05:17 +00:00
}
2024-02-04 01:25:44 +00:00
func (p *Person) ToDatabaseShallow() *storage.DB_Person {
dbPerson := storage.DB_Person{
ID: p.ID,
DisplayName: p.DisplayName,
2024-02-09 21:51:26 +00:00
Permissions: int64(p.Permissions),
2024-02-04 15:21:16 +00:00
RefundTickets: p.RefundTickets,
Receipts: []storage.DB_Receipt{},
BanHistory: []storage.DB_BanStatus{},
2024-02-04 01:25:44 +00:00
Profiles: []storage.DB_Profile{},
Stats: []storage.DB_SeasonStat{},
Discord: storage.DB_DiscordPerson{},
}
if p.Discord != nil {
dbPerson.Discord = *p.Discord
}
2024-02-10 01:55:56 +00:00
p.BanHistory.Range(func(key string, ban *storage.DB_BanStatus) bool {
dbPerson.BanHistory = append(dbPerson.BanHistory, *ban)
return true
})
p.Receipts.RangeReceipts(func(key string, receipt *Receipt) bool {
dbPerson.Receipts = append(dbPerson.Receipts, *receipt.ToDatabase())
return true
})
p.AllSeasonsStats.Range(func(key string, stat *SeasonStats) bool {
dbPerson.Stats = append(dbPerson.Stats, *stat.ToDatabase(p.ID))
return true
})
2024-02-04 01:25:44 +00:00
return &dbPerson
}
2023-11-01 00:05:17 +00:00
func (p *Person) Snapshot() *PersonSnapshot {
2024-02-10 01:55:56 +00:00
snapshot := &PersonSnapshot{
2023-11-01 00:05:17 +00:00
ID: p.ID,
DisplayName: p.DisplayName,
2024-02-19 01:49:14 +00:00
RefundTickets: p.RefundTickets,
2024-02-09 21:51:26 +00:00
Permissions: int64(p.Permissions),
2023-11-01 00:05:17 +00:00
AthenaProfile: *p.AthenaProfile.Snapshot(),
CommonCoreProfile: *p.CommonCoreProfile.Snapshot(),
CommonPublicProfile: *p.CommonPublicProfile.Snapshot(),
Profile0Profile: *p.Profile0Profile.Snapshot(),
CollectionsProfile: *p.CollectionsProfile.Snapshot(),
2023-12-08 15:35:00 +00:00
CreativeProfile: *p.CreativeProfile.Snapshot(),
CurrentSeasonStats: *p.CurrentSeasonStats,
AllSeasonsStats: []SeasonStats{},
2024-02-10 01:55:56 +00:00
BanHistory: []storage.DB_BanStatus{},
Receipts: []storage.DB_Receipt{},
Discord: *p.Discord,
2024-02-19 01:49:14 +00:00
Relationships: *p.Relationships.Snapshot(),
Parties: *p.Parties.Snapshot(),
Invites: *p.Invites.Snapshot(),
Intentions: *p.Intentions.Snapshot(),
2023-11-01 00:05:17 +00:00
}
2024-02-10 01:55:56 +00:00
p.BanHistory.Range(func(key string, ban *storage.DB_BanStatus) bool {
snapshot.BanHistory = append(snapshot.BanHistory, *ban)
return true
})
p.Receipts.RangeReceipts(func(key string, receipt *Receipt) bool {
snapshot.Receipts = append(snapshot.Receipts, *receipt.ToDatabase())
return true
})
p.AllSeasonsStats.Range(func(key string, stat *SeasonStats) bool {
snapshot.AllSeasonsStats = append(snapshot.AllSeasonsStats, *stat)
return true
})
2024-02-10 01:55:56 +00:00
return snapshot
2023-12-13 22:52:16 +00:00
}
func (p *Person) Delete() {
storage.Repo.DeletePerson(p.ID)
2024-02-10 00:33:10 +00:00
cache.DeletePerson(p.ID)
2024-02-04 15:21:16 +00:00
}
func (p *Person) SetPurchaseHistoryAttribute() {
purchases := []aid.JSON{}
p.AthenaProfile.Purchases.RangePurchases(func(key string, value *Purchase) bool {
purchases = append(purchases, value.GenerateFortnitePurchaseEntry())
return true
})
purchaseAttribute := p.CommonCoreProfile.Attributes.GetAttributeByKey("mtx_purchase_history")
purchaseAttribute.ValueJSON = aid.JSONStringify(aid.JSON{
"refundsUsed": p.AthenaProfile.Purchases.CountRefunded(),
"refundCredits": p.RefundTickets,
"purchases": purchases,
})
purchaseAttribute.Save()
}
func (p *Person) SetInAppPurchasesAttribute() {
receipts := []string{}
fulfillmentCounts := map[string]int{}
p.Receipts.RangeReceipts(func(key string, r *Receipt) bool {
pureOfferId := strings.ReplaceAll(r.OfferID, "app-", "")
receipts = append(receipts, r.ID)
fulfillmentCounts[pureOfferId]++
return true
})
inAppPurchaseAttribute := p.CommonCoreProfile.Attributes.GetAttributeByKey("in_app_purchases")
inAppPurchaseAttribute.ValueJSON = aid.JSONStringify(aid.JSON{
"ignoredReceipts": []string{},
"refreshTimers": aid.JSON{},
"receipts": receipts,
"fulfillmentCounts": fulfillmentCounts,
})
inAppPurchaseAttribute.Save()
}
func (p *Person) SyncVBucks(sourceProfileType string) {
antiSourceLookup := map[string]string{
"profile0": "common_core",
"common_core": "profile0",
}
sourceProfile := p.GetProfileFromType(sourceProfileType)
antiSourceProfile := p.GetProfileFromType(antiSourceLookup[sourceProfileType])
if sourceProfile == nil || antiSourceProfile == nil {
return
}
sourceCurrency := sourceProfile.Items.GetItemByTemplateID("Currency:MtxPurchased")
antiSourceCurrency := antiSourceProfile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if sourceCurrency == nil || antiSourceCurrency == nil {
return
}
antiSourceCurrency.Quantity = sourceCurrency.Quantity
antiSourceCurrency.Save()
}
func (p *Person) TakeAndSyncVbucks(quant int) {
currency := p.CommonCoreProfile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if currency == nil {
aid.Print("currency not found")
return
}
currency.Quantity -= quant
currency.Save()
p.SyncVBucks("common_core")
}
func (p *Person) GiveAndSyncVbucks(quant int) {
currency := p.CommonCoreProfile.Items.GetItemByTemplateID("Currency:MtxPurchased")
if currency == nil {
aid.Print("currency not found")
return
}
currency.Quantity += quant
currency.Save()
p.SyncVBucks("common_core")
2023-12-13 22:52:16 +00:00
}