Remove friends and fix client credentials bug

This commit is contained in:
Eccentric 2024-01-28 19:40:45 +00:00
parent 8619cac5ca
commit 20cbc92e7f
14 changed files with 23 additions and 775 deletions

View File

@ -35,6 +35,7 @@ type CS struct {
Build float64 Build float64
Everything bool Everything bool
Password bool Password bool
DisableClientCredentials bool
} }
} }
@ -134,4 +135,5 @@ func LoadConfig(file []byte) {
Config.Fortnite.Season = parsedSeason Config.Fortnite.Season = parsedSeason
Config.Fortnite.Everything = cfg.Section("fortnite").Key("everything").MustBool(false) Config.Fortnite.Everything = cfg.Section("fortnite").Key("everything").MustBool(false)
Config.Fortnite.Password = cfg.Section("fortnite").Key("password").MustBool(false) Config.Fortnite.Password = cfg.Section("fortnite").Key("password").MustBool(false)
Config.Fortnite.DisableClientCredentials = cfg.Section("fortnite").Key("disable_client_credentials").MustBool(false)
} }

View File

@ -45,3 +45,9 @@ everything=true
; if this is set to false, you can login to any account with just the username ; if this is set to false, you can login to any account with just the username
; if this is true you must login using an exchange code given by the bot ; if this is true you must login using an exchange code given by the bot
password=true password=true
; if you recieve lots of /account/api/oauth/token requests, set this to true
; this will disable the client credentials grant type
; however this will also disable a user to get the hotfixes before login
; so xmpp and other hotfix related things will be delayed by ~1 minute
disable_client_credentials=false

6
go.mod
View File

@ -3,10 +3,8 @@ module github.com/ectrc/snow
go 1.21.3 go 1.21.3
require ( require (
github.com/beevik/etree v1.3.0
github.com/bwmarrin/discordgo v0.27.1 github.com/bwmarrin/discordgo v0.27.1
github.com/goccy/go-json v0.10.2 github.com/goccy/go-json v0.10.2
github.com/gofiber/contrib/websocket v1.3.0
github.com/gofiber/fiber/v2 v2.51.0 github.com/gofiber/fiber/v2 v2.51.0
github.com/golang-jwt/jwt/v5 v5.2.0 github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.4.0 github.com/google/uuid v1.4.0
@ -20,7 +18,6 @@ require (
require ( require (
github.com/andybalholm/brotli v1.0.6 // indirect github.com/andybalholm/brotli v1.0.6 // indirect
github.com/fasthttp/websocket v1.5.7 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@ -33,7 +30,7 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/philhofer/fwd v1.1.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/stretchr/testify v1.8.4 // indirect
github.com/tinylib/msgp v1.1.8 // indirect github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect
@ -41,7 +38,6 @@ require (
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/crypto v0.15.0 // indirect golang.org/x/crypto v0.15.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
) )

10
go.sum
View File

@ -1,18 +1,12 @@
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.7 h1:0a6o2OfeATvtGgoMKleURhLT6JqWPg7fYfWnH4KHau4=
github.com/fasthttp/websocket v1.5.7/go.mod h1:bC4fxSono9czeXHQUVKxsC0sNjbm7lPJR04GDFqClfU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofiber/contrib/websocket v1.3.0 h1:XADFAGorer1VJ1bqC4UkCjqS37kwRTV0415+050NrMk=
github.com/gofiber/contrib/websocket v1.3.0/go.mod h1:xguaOzn2ZZ759LavtosEP+rcxIgBEE/rdumPINhR+Xo=
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
@ -53,8 +47,6 @@ github.com/r3labs/diff/v3 v3.0.1/go.mod h1:f1S9bourRbiM66NskseyUdo0fTmEE0qKrikYJ
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -85,8 +77,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -40,6 +40,10 @@ func PostFortniteToken(c *fiber.Ctx) error {
} }
func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error { func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error {
if aid.Config.Fortnite.DisableClientCredentials {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Client Credentials is disabled."))
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{ return c.Status(fiber.StatusOK).JSON(aid.JSON{
"access_token": "snow", "access_token": "snow",
"token_type": "bearer", "token_type": "bearer",

View File

@ -1,138 +1,26 @@
package handlers package handlers
import ( import (
"time"
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person"
"github.com/ectrc/snow/storage"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func GetFriendList(c *fiber.Ctx) error { func GetFriendList(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person) return c.Status(200).JSON([]aid.JSON{})
result := map[string]aid.JSON{}
for _, partial := range storage.Repo.GetFriendsForPerson(person.ID) {
friend := person.GetFriend(partial.ID)
if friend == nil {
continue
}
result[partial.ID] = friend.GenerateFriendResponse()
}
response := []aid.JSON{}
for _, friend := range result {
response = append(response, friend)
}
return c.Status(200).JSON(response)
} }
func PostCreateFriend(c *fiber.Ctx) error { func PostCreateFriend(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person)
friendId := c.Params("wanted")
existing := person.GetFriend(friendId)
if existing != nil && (existing.Direction == "BOTH" || existing.Direction == "OUTGOING") {
return c.Status(400).JSON(aid.ErrorBadRequest("already active friend request"))
}
person.AddFriend(friendId)
socket := FindSocketForPerson(person)
socket.PresenceWriteJSON(aid.JSON{
"payload": person.GetFriend(friendId).GenerateFriendResponse(),
"type": "com.epicgames.friends.core.apiobjects.Friend",
"timestamp": time.Now().Format(time.RFC3339),
})
friendSocket := FindSocketForPerson(p.Find(friendId))
friendSocket.PresenceWriteJSON(aid.JSON{
"payload": friendSocket.Person.GetFriend(person.ID).GenerateFriendResponse(),
"type": "com.epicgames.friends.core.apiobjects.Friend",
"timestamp": time.Now().Format(time.RFC3339),
})
return c.SendStatus(204) return c.SendStatus(204)
} }
func DeleteFriend(c *fiber.Ctx) error { func DeleteFriend(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person)
wanted := c.Params("wanted")
existing := person.GetFriend(wanted)
if existing == nil {
return c.Status(400).JSON(aid.ErrorBadRequest("not friends"))
}
person.RemoveFriend(wanted)
existing.Person.RemoveFriend(person.ID)
return c.SendStatus(204) return c.SendStatus(204)
} }
func GetFriendListSummary(c *fiber.Ctx) error { func GetFriendListSummary(c *fiber.Ctx) error {
person := c.Locals("person").(*p.Person) return c.Status(200).JSON([]aid.JSON{})
all := map[string]*p.Friend{}
for _, partial := range storage.Repo.GetFriendsForPerson(person.ID) {
friend := person.GetFriend(partial.ID)
if friend == nil {
continue
}
all[partial.ID] = friend
}
result := aid.JSON{
"friends": []aid.JSON{},
"incoming": []aid.JSON{},
"outgoing": []aid.JSON{},
"settings": aid.JSON{
"acceptInvites": "public",
},
}
for _, friend := range all {
switch friend.Status {
case p.FriendStatusAccepted:
result["friends"] = append(result["friends"].([]aid.JSON), friend.GenerateSummaryResponse())
case p.FriendStatusPending:
switch friend.Direction {
case p.FriendDirectionIncoming:
result["incoming"] = append(result["incoming"].([]aid.JSON), friend.GenerateSummaryResponse())
case p.FriendDirectionOutgoing:
result["outgoing"] = append(result["outgoing"].([]aid.JSON), friend.GenerateSummaryResponse())
}
}
}
return c.Status(200).JSON(result)
} }
func GetPersonSearch(c *fiber.Ctx) error { func GetPersonSearch(c *fiber.Ctx) error {
query := c.Query("prefix")
matches := storage.Repo.GetPersonsByPartialDisplayFromDB(query)
if matches == nil {
return c.Status(200).JSON([]aid.JSON{}) return c.Status(200).JSON([]aid.JSON{})
}
result := []aid.JSON{}
for i, match := range matches {
result = append(result, aid.JSON{
"accountId": match.ID,
"epicMutuals": 0,
"sortPosition": i,
"matchType": "prefix",
"matches": []aid.JSON{{
"value": match.DisplayName,
"matchType": "prefix",
}},
})
}
return c.Status(200).JSON(result)
} }

View File

@ -1,130 +0,0 @@
package handlers
import (
"github.com/ectrc/snow/aid"
"github.com/ectrc/snow/person"
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
type SocketType string
const SocketTypeXmpp SocketType = "xmpp"
const SocketTypeUnknown SocketType = "unknown"
type Socket struct {
ID string
Connection *websocket.Conn
Person *person.Person
Type SocketType
PresenceState *PresenceState
}
type MessageToWrite struct {
Socket *Socket
Message []byte
}
func (s *Socket) Write(message []byte) {
socketWriteQueue <- MessageToWrite{
Socket: s,
Message: message,
}
}
var (
socketWriteQueue = make(chan MessageToWrite, 1000)
socketHandlers = map[SocketType]func(string) {
SocketTypeXmpp: presenceSocketHandle,
}
sockets = aid.GenericSyncMap[Socket]{}
)
func MiddlewareWebsocket(c *fiber.Ctx) error {
if !websocket.IsWebSocketUpgrade(c) {
return fiber.ErrUpgradeRequired
}
var protocol SocketType
switch c.Get("Sec-WebSocket-Protocol") {
case "xmpp":
protocol = SocketTypeXmpp
default:
protocol = SocketTypeUnknown
}
c.Locals("uuid", uuid.New().String())
c.Locals("protocol", protocol)
return c.Next()
}
func WebsocketConnection(c *websocket.Conn) {
protocol := c.Locals("protocol").(SocketType)
uuid := c.Locals("uuid").(string)
sockets.Add(uuid, &Socket{
ID: uuid,
Type: protocol,
Connection: c,
})
defer func() {
socket, ok := sockets.Get(uuid)
if !ok {
return
}
socket.Connection.Close()
sockets.Delete(uuid)
aid.Print("(xmpp) connection closed", uuid)
}()
if handle, ok := socketHandlers[protocol]; ok {
handle(uuid)
}
}
func GetAllSockets(c *fiber.Ctx) error {
result := []any{}
sockets.Range(func(key string, socket *Socket) bool {
result = append(result, *socket)
return true
})
return c.Status(200).JSON(result)
}
func FindSocketForPerson(person *person.Person) *Socket {
var recieverSocket *Socket
sockets.Range(func(key string, value *Socket) bool {
if value.Person == nil {
return true
}
if value.Person.ID == person.ID {
recieverSocket = value
return false
}
return true
})
return recieverSocket
}
func init() {
go func() {
for {
if aid.Config != nil {
break
}
}
for {
message := <-socketWriteQueue
aid.Print("(socket) writing message to", message.Socket.ID, string(message.Message))
message.Socket.Connection.WriteMessage(websocket.TextMessage, message.Message)
}
}()
}

View File

@ -1,355 +0,0 @@
package handlers
import (
"fmt"
"strings"
"time"
"github.com/beevik/etree"
"github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person"
"github.com/ectrc/snow/storage"
"github.com/goccy/go-json"
"github.com/google/uuid"
)
type PresenceStatus struct {
Status string `json:"Status"`
IsPlaying bool `json:"bIsPlaying"`
IsJoinable bool `json:"bIsJoinable"`
HasVoiceSupport bool `json:"bHasVoiceSupport"`
SessionId string `json:"SessionId"`
Properties map[string]struct {
SourceId string `json:"sourceId"`
SourceDisplayName string `json:"sourceDisplayName"`
SourcePlatform string `json:"sourcePlatform"`
PartyId string `json:"partyId"`
PartyTypeId int `json:"partyTypeId"`
Key string `json:"key"`
AppId string `json:"appId"`
BuildId string `json:"buildId"`
PartyFlags int `json:"partyFlags"`
NotAcceptingReason int `json:"notAcceptingReason"`
Pc int `json:"pc"`
} `json:"Properties"`
}
type PresenceState struct {
JID string
Open bool
RawStatus string
ParsedStatus PresenceStatus
}
var (
socketXmppMessageHandlers = map[string]func(*Socket, *etree.Document) error {
"open": presenceSocketOpenEvent,
"iq": presenceSocketIqRootEvent,
"message": presenceSocketMessageEvent,
"presence": presenceSocketPresenceEvent,
"close": presenceSocketCloseEvent,
}
)
func presenceSocketHandle(id string) {
socket, ok := sockets.Get(id)
if !ok {
return
}
socket.Type = SocketTypeXmpp
socket.PresenceState = &PresenceState{}
for {
_, message, err := socket.Connection.ReadMessage()
if err != nil {
break
}
parsed := etree.NewDocument()
if err := parsed.ReadFromBytes(message); err != nil {
return
}
if handler, ok := socketXmppMessageHandlers[parsed.Root().Tag]; ok {
if err := handler(socket, parsed); err != nil {
return
}
}
}
for _, partial := range storage.Repo.GetFriendsForPerson(socket.Person.ID) {
friend := socket.Person.GetFriend(partial.ID)
if friend == nil {
continue
}
friendSocket := FindSocketForPerson(friend.Person)
if friendSocket == nil {
continue
}
friendDocument := etree.NewDocument()
friendPresence := friendDocument.CreateElement("presence")
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "from", Value: socket.PresenceState.JID})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "to", Value: friendSocket.PresenceState.JID})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "type", Value: "available"})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
friendPresence.CreateElement("status").SetText(aid.JSONStringify(aid.JSON{}))
friendPresence.CreateElement("show").SetText("away")
friendSocket.PresenceWrite(friendDocument)
}
for _, party := range socket.PresenceState.ParsedStatus.Properties {
if party.PartyId == "" {
continue
}
sockets.Range(func(_ string, recieverSocket *Socket) bool {
if recieverSocket.Type != SocketTypeXmpp || recieverSocket.Person == nil {
return true
}
document := etree.NewDocument()
message := document.CreateElement("message")
message.Attr = append(message.Attr, etree.Attr{Key: "id", Value: uuid.New().String()})
message.Attr = append(message.Attr, etree.Attr{Key: "from", Value: socket.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "to", Value: recieverSocket.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
message.CreateElement("body").SetText(aid.JSONStringify(aid.JSON{
"type": "com.epicgames.party.memberexited",
"timestamp": time.Now().Format(time.RFC3339),
"payload": aid.JSON{
"memberId": socket.Person.ID,
"partyId": party.PartyId,
"wasKicked": false,
},
}))
recieverSocket.PresenceWrite(document)
return true
})
}
}
func presenceSocketOpenEvent(socket *Socket, tree *etree.Document) error {
document := etree.NewDocument()
open := document.CreateElement("open")
open.Attr = append(open.Attr, etree.Attr{Key: "xmlns", Value: "urn:ietf:params:xml:ns:xmpp-framing"})
open.Attr = append(open.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
open.Attr = append(open.Attr, etree.Attr{Key: "version", Value: "1.0"})
open.Attr = append(open.Attr, etree.Attr{Key: "id", Value: socket.ID})
socket.PresenceWrite(document)
return nil
}
func presenceSocketCloseEvent(socket *Socket, tree *etree.Document) error {
return fmt.Errorf("safe exit")
}
func presenceSocketIqRootEvent(socket *Socket, tree *etree.Document) error {
redirect := map[string]func(*Socket, *etree.Document) error{
"set": presenceSocketIqSetEvent,
"get": presenceSocketIqGetEvent,
}
if handler, ok := redirect[tree.Root().SelectAttr("type").Value]; ok {
if err := handler(socket, tree); err != nil {
return err
}
}
return nil
}
func presenceSocketIqSetEvent(socket *Socket, tree *etree.Document) error {
token := tree.Root().SelectElement("query").SelectElement("password")
if token == nil || token.Text() == "" {
return fmt.Errorf("invalid token")
}
real := strings.ReplaceAll(token.Text(), "eg1~", "")
claims, err := aid.JWTVerify(real)
if err != nil {
return fmt.Errorf("invalid token")
}
if claims["snow_id"] == nil {
return fmt.Errorf("invalid token")
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return fmt.Errorf("invalid token")
}
person := p.Find(snowId)
if person == nil {
return fmt.Errorf("invalid token")
}
socket.Person = person
socket.PresenceState.JID = person.ID + "@prod.ol.epicgames.com/" + tree.Root().SelectElement("query").SelectElement("resource").Text()
document := etree.NewDocument()
iq := document.CreateElement("iq")
iq.Attr = append(iq.Attr, etree.Attr{Key: "type", Value: "result"})
iq.Attr = append(iq.Attr, etree.Attr{Key: "id", Value: "_xmpp_auth1"})
iq.Attr = append(iq.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
socket.PresenceWrite(document)
socket.PresenceWriteStatus()
return nil
}
func presenceSocketIqGetEvent(socket *Socket, tree *etree.Document) error {
document := etree.NewDocument()
iq := document.CreateElement("iq")
iq.Attr = append(iq.Attr, etree.Attr{Key: "type", Value: "result"})
iq.Attr = append(iq.Attr, etree.Attr{Key: "id", Value: tree.Root().SelectAttr("id").Value})
iq.Attr = append(iq.Attr, etree.Attr{Key: "to", Value: tree.Root().SelectAttr("from").Value})
iq.Attr = append(iq.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
ping := iq.CreateElement("ping")
ping.Attr = append(ping.Attr, etree.Attr{Key: "xmlns", Value: "urn:xmpp:ping"})
socket.PresenceWrite(document)
return nil
}
func presenceSocketMessageEvent(socket *Socket, tree *etree.Document) error {
reciever := p.Find(strings.Split(tree.Root().SelectAttr("to").Value, "@")[0])
if reciever == nil {
return nil
}
recieverSocket := FindSocketForPerson(reciever)
if recieverSocket == nil {
return nil
}
document := etree.NewDocument()
message := document.CreateElement("message")
message.Attr = append(message.Attr, etree.Attr{Key: "id", Value: tree.Root().SelectAttr("id").Value})
message.Attr = append(message.Attr, etree.Attr{Key: "from", Value: socket.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "to", Value: recieverSocket.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
message.CreateElement("body").SetText(tree.Root().SelectElement("body").Text())
recieverSocket.PresenceWrite(document)
return nil
}
func presenceSocketPresenceEvent(socket *Socket, tree *etree.Document) error {
status := tree.Root().SelectElement("status")
if status == nil {
return nil
}
socket.PresenceState.RawStatus = status.Text()
json.NewDecoder(strings.NewReader(status.Text())).Decode(&socket.PresenceState.ParsedStatus)
socket.PresenceWriteStatus()
return nil
}
func (s *Socket) PresenceWrite(message *etree.Document) {
bytes, err := message.WriteToBytes()
if err != nil {
return
}
s.Write(bytes)
}
func (s *Socket) PresenceWriteStatus() {
for _, partial := range storage.Repo.GetFriendsForPerson(s.Person.ID) {
friend := s.Person.GetFriend(partial.ID)
if friend == nil {
continue
}
friendSocket := FindSocketForPerson(friend.Person)
if friendSocket == nil {
continue
}
friendDocument := etree.NewDocument()
friendPresence := friendDocument.CreateElement("presence")
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "from", Value: s.PresenceState.JID})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "to", Value: friendSocket.PresenceState.JID})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "type", Value: "available"})
friendPresence.Attr = append(friendPresence.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
friendPresence.CreateElement("status").SetText(s.PresenceState.RawStatus)
friendSocket.PresenceWrite(friendDocument)
document := etree.NewDocument()
presence := document.CreateElement("presence")
presence.Attr = append(presence.Attr, etree.Attr{Key: "from", Value: friendSocket.PresenceState.JID})
presence.Attr = append(presence.Attr, etree.Attr{Key: "to", Value: s.PresenceState.JID})
presence.Attr = append(presence.Attr, etree.Attr{Key: "type", Value: "available"})
presence.Attr = append(presence.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
presence.CreateElement("status").SetText(friendSocket.PresenceState.RawStatus)
s.PresenceWrite(document)
}
}
func (s *Socket) PresenceWriteJSON(body aid.JSON) {
aid.PrintJSON(body)
document := etree.NewDocument()
message := document.CreateElement("message")
message.Attr = append(message.Attr, etree.Attr{Key: "id", Value: uuid.New().String()})
message.Attr = append(message.Attr, etree.Attr{Key: "from", Value: "xmpp-admin@prod.ol.epicgames.com"})
message.Attr = append(message.Attr, etree.Attr{Key: "to", Value: s.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
message.CreateElement("body").SetText(aid.JSONStringify(body))
s.PresenceWrite(document)
}
func (s *Socket) PresenceWriteGiftReceived() {
document := etree.NewDocument()
message := document.CreateElement("message")
message.Attr = append(message.Attr, etree.Attr{Key: "id", Value: uuid.New().String()})
message.Attr = append(message.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
message.Attr = append(message.Attr, etree.Attr{Key: "to", Value: s.PresenceState.JID})
message.Attr = append(message.Attr, etree.Attr{Key: "xmlns", Value: "jabber:client"})
message.CreateElement("body").SetText(aid.JSONStringify(aid.JSON{
"type": "com.epicgames.gift.received",
"timestamp": time.Now().Format(time.RFC3339),
"payload": aid.JSON{},
}))
s.PresenceWrite(document)
}
func init() {
go func() {
for {
if aid.Config != nil {
break
}
}
timer := time.NewTicker(30 * time.Second)
for {
<-timer.C
sockets.Range(func(key string, socket *Socket) bool {
if socket.Type != SocketTypeXmpp || socket.Person == nil {
return true
}
document := etree.NewDocument()
iq := document.CreateElement("iq")
iq.Attr = append(iq.Attr, etree.Attr{Key: "id", Value: "_xmpp_auth1"})
iq.Attr = append(iq.Attr, etree.Attr{Key: "type", Value: "get"})
iq.Attr = append(iq.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
ping := iq.CreateElement("ping")
ping.Attr = append(iq.Attr, etree.Attr{Key: "xmlns", Value: "urn:xmpp:ping"})
socket.PresenceWrite(document)
return true
})
}
}()
}

View File

@ -12,7 +12,6 @@ import (
"github.com/ectrc/snow/storage" "github.com/ectrc/snow/storage"
"github.com/goccy/go-json" "github.com/goccy/go-json"
"github.com/gofiber/contrib/websocket"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -69,7 +68,7 @@ func main() {
r.Post("/api/v1/assets/Fortnite/:versionId/:assetName", handlers.PostAssets) r.Post("/api/v1/assets/Fortnite/:versionId/:assetName", handlers.PostAssets)
r.Get("//", func(c *fiber.Ctx) error { return c.Redirect("/socket") }) r.Get("//", func(c *fiber.Ctx) error { return c.Redirect("/socket") })
r.Get("/socket", handlers.MiddlewareWebsocket, websocket.New(handlers.WebsocketConnection)) // r.Get("/socket", handlers.MiddlewareWebsocket, websocket.New(handlers.WebsocketConnection))
account := r.Group("/account/api") account := r.Group("/account/api")
account.Get("/public/account", handlers.GetPublicAccounts) account.Get("/public/account", handlers.GetPublicAccounts)
@ -131,7 +130,6 @@ func main() {
snow := r.Group("/snow") snow := r.Group("/snow")
snow.Get("/cosmetics", handlers.GetPreloadedCosmetics) snow.Get("/cosmetics", handlers.GetPreloadedCosmetics)
snow.Get("/image/:playlist", handlers.GetPlaylistImage) snow.Get("/image/:playlist", handlers.GetPlaylistImage)
snow.Get("/sockets", handlers.GetAllSockets)
discord := snow.Group("/discord") discord := snow.Group("/discord")
discord.Get("/", handlers.GetDiscordOAuthURL) discord.Get("/", handlers.GetDiscordOAuthURL)

View File

@ -1,45 +0,0 @@
package person
import (
"time"
"github.com/ectrc/snow/aid"
)
type FriendDirection string
var FriendDirectionBoth FriendDirection = "BOTH"
var FriendDirectionIncoming FriendDirection = "INCOMING"
var FriendDirectionOutgoing FriendDirection = "OUTGOING"
type FriendStatus string
var FriendStatusPending FriendStatus = "PENDING"
var FriendStatusAccepted FriendStatus = "ACCEPTED"
var FriendStatusDeleted FriendStatus = "DELETED"
type Friend struct {
Person *Person
Status FriendStatus
Direction FriendDirection
}
func (f *Friend) GenerateSummaryResponse() aid.JSON {
return aid.JSON{
"accountId": f.Person.ID,
"groups": []string{},
"mutual": 0,
"alias": "",
"note": "",
"favorite": false,
"created": time.Now().Add(-time.Hour * 24 * 7).Format(time.RFC3339),
}
}
func (f *Friend) GenerateFriendResponse() aid.JSON {
return aid.JSON{
"accountId": f.Person.ID,
"status": f.Status,
"direction": f.Direction,
"created": time.Now().Add(-time.Hour * 24 * 7).Format(time.RFC3339),
"favourite": false,
}
}

View File

@ -10,7 +10,6 @@ type Person struct {
DisplayName string DisplayName string
Permissions []string Permissions []string
IsBanned bool IsBanned bool
Friends []string
AthenaProfile *Profile AthenaProfile *Profile
CommonCoreProfile *Profile CommonCoreProfile *Profile
CommonPublicProfile *Profile CommonPublicProfile *Profile
@ -26,7 +25,6 @@ func NewPerson() *Person {
DisplayName: uuid.New().String(), DisplayName: uuid.New().String(),
Permissions: []string{}, Permissions: []string{},
IsBanned: false, IsBanned: false,
Friends: []string{},
AthenaProfile: NewProfile("athena"), AthenaProfile: NewProfile("athena"),
CommonCoreProfile: NewProfile("common_core"), CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"), CommonPublicProfile: NewProfile("common_public"),
@ -42,7 +40,6 @@ func NewPersonWithCustomID(id string) *Person {
DisplayName: uuid.New().String(), DisplayName: uuid.New().String(),
Permissions: []string{}, Permissions: []string{},
IsBanned: false, IsBanned: false,
Friends: []string{},
AthenaProfile: NewProfile("athena"), AthenaProfile: NewProfile("athena"),
CommonCoreProfile: NewProfile("common_core"), CommonCoreProfile: NewProfile("common_core"),
CommonPublicProfile: NewProfile("common_public"), CommonPublicProfile: NewProfile("common_public"),
@ -151,7 +148,6 @@ func findHelper(databasePerson *storage.DB_Person) *Person {
DisplayName: databasePerson.DisplayName, DisplayName: databasePerson.DisplayName,
Permissions: databasePerson.Permissions, Permissions: databasePerson.Permissions,
IsBanned: databasePerson.IsBanned, IsBanned: databasePerson.IsBanned,
Friends: databasePerson.Friends,
AthenaProfile: athenaProfile, AthenaProfile: athenaProfile,
CommonCoreProfile: commonCoreProfile, CommonCoreProfile: commonCoreProfile,
CommonPublicProfile: commonPublicProfile, CommonPublicProfile: commonPublicProfile,
@ -251,71 +247,12 @@ func (p *Person) HasPermission(permission Permission) bool {
return false return false
} }
func (p *Person) AddFriend(friendId string) {
p.Friends = append(p.Friends, friendId)
p.Save()
}
func (p *Person) RemoveFriend(friendId string) {
for i, friend := range p.Friends {
if friend == friendId {
p.Friends = append(p.Friends[:i], p.Friends[i+1:]...)
break
}
}
p.Save()
}
func (p *Person) GetFriend(friendId string) *Friend {
friend := Find(friendId)
if friend == nil {
return nil
}
if p.IsFriendInFriendList(friendId) {
if friend.IsFriendInFriendList(p.ID) {
return &Friend{
Person: friend,
Status: FriendStatusAccepted,
Direction: FriendDirectionBoth,
}
}
return &Friend{
Person: friend,
Status: FriendStatusPending,
Direction: FriendDirectionOutgoing,
}
}
if friend.IsFriendInFriendList(p.ID) {
return &Friend{
Person: friend,
Status: FriendStatusPending,
Direction: FriendDirectionIncoming,
}
}
return nil
}
func (p *Person) IsFriendInFriendList(friendId string) bool {
for _, idA := range p.Friends {
if idA == friendId {
return true
}
}
return false
}
func (p *Person) ToDatabase() *storage.DB_Person { func (p *Person) ToDatabase() *storage.DB_Person {
dbPerson := storage.DB_Person{ dbPerson := storage.DB_Person{
ID: p.ID, ID: p.ID,
DisplayName: p.DisplayName, DisplayName: p.DisplayName,
Permissions: p.Permissions, Permissions: p.Permissions,
IsBanned: p.IsBanned, IsBanned: p.IsBanned,
Friends: p.Friends,
Profiles: []storage.DB_Profile{}, Profiles: []storage.DB_Profile{},
Stats: []storage.DB_SeasonStat{}, Stats: []storage.DB_SeasonStat{},
Discord: storage.DB_DiscordPerson{}, Discord: storage.DB_DiscordPerson{},

View File

@ -57,7 +57,7 @@ func (s *PostgresStorage) GetPerson(personId string) *DB_Person {
Model(&DB_Person{}). Model(&DB_Person{}).
Preload("Profiles"). Preload("Profiles").
Preload("Profiles.Loadouts"). Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants"). // Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot"). Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes"). Preload("Profiles.Attributes").
Preload("Profiles.Items"). Preload("Profiles.Items").
@ -80,7 +80,7 @@ func (s *PostgresStorage) GetPersonByDisplay(displayName string) *DB_Person {
Model(&DB_Person{}). Model(&DB_Person{}).
Preload("Profiles"). Preload("Profiles").
Preload("Profiles.Loadouts"). Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants"). // Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot"). Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes"). Preload("Profiles.Attributes").
Preload("Profiles.Items"). Preload("Profiles.Items").
@ -103,7 +103,7 @@ func (s *PostgresStorage) GetPersonsByPartialDisplay(displayName string) []*DB_P
Model(&DB_Person{}). Model(&DB_Person{}).
Preload("Profiles"). Preload("Profiles").
Preload("Profiles.Loadouts"). Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants"). // Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot"). Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes"). Preload("Profiles.Attributes").
Preload("Profiles.Items"). Preload("Profiles.Items").
@ -138,7 +138,7 @@ func (s *PostgresStorage) GetAllPersons() []*DB_Person {
Model(&DB_Person{}). Model(&DB_Person{}).
Preload("Profiles"). Preload("Profiles").
Preload("Profiles.Loadouts"). Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants"). // Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot"). Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes"). Preload("Profiles.Attributes").
Preload("Profiles.Items"). Preload("Profiles.Items").
@ -156,42 +156,6 @@ func (s *PostgresStorage) GetPersonsCount() int {
return int(count) return int(count)
} }
func (s *PostgresStorage) GetFriendsForPerson(personId string) []*DB_Person {
person := s.GetPerson(personId)
var mine []*DB_Person
s.Postgres.
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Gifts").
Preload("Profiles.Quests").
Preload("Discord").
Where("id IN (?)", person.Friends).
Find(&mine)
var theirs []*DB_Person
s.Postgres.
Model(&DB_Person{}).
Preload("Profiles").
Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes").
Preload("Profiles.Items").
Preload("Profiles.Gifts").
Preload("Profiles.Quests").
Preload("Discord").
Where("? = ?", person.ID, gorm.Expr("ANY(friends)")).
Find(&theirs)
return append(mine, theirs...)
}
func (s *PostgresStorage) TotalVBucks() int { func (s *PostgresStorage) TotalVBucks() int {
var total int64 var total int64
s.Postgres.Model(&DB_Item{}).Select("sum(quantity)").Where("template_id = ?", "Currency:MtxPurchased").Find(&total) s.Postgres.Model(&DB_Item{}).Select("sum(quantity)").Where("template_id = ?", "Currency:MtxPurchased").Find(&total)
@ -207,7 +171,7 @@ func (s *PostgresStorage) DeletePerson(personId string) {
Model(&DB_Person{}). Model(&DB_Person{}).
Preload("Profiles"). Preload("Profiles").
Preload("Profiles.Loadouts"). Preload("Profiles.Loadouts").
Preload("Profiles.Items.Variants"). // Preload("Profiles.Items.Variants").
Preload("Profiles.Gifts.Loot"). Preload("Profiles.Gifts.Loot").
Preload("Profiles.Attributes"). Preload("Profiles.Attributes").
Preload("Profiles.Items"). Preload("Profiles.Items").

View File

@ -19,8 +19,6 @@ type Storage interface {
SavePerson(person *DB_Person) SavePerson(person *DB_Person)
DeletePerson(personId string) DeletePerson(personId string)
GetFriendsForPerson(personId string) []*DB_Person
SaveProfile(profile *DB_Profile) SaveProfile(profile *DB_Profile)
DeleteProfile(profileId string) DeleteProfile(profileId string)
@ -108,10 +106,6 @@ func (r *Repository) GetPersonsCount() int {
return r.Storage.GetPersonsCount() return r.Storage.GetPersonsCount()
} }
func (r *Repository) GetFriendsForPerson(personId string) []*DB_Person {
return r.Storage.GetFriendsForPerson(personId)
}
func (r *Repository) SavePerson(person *DB_Person) { func (r *Repository) SavePerson(person *DB_Person) {
r.Storage.SavePerson(person) r.Storage.SavePerson(person)
} }

View File

@ -13,7 +13,6 @@ type DB_Person struct {
IsBanned bool IsBanned bool
Profiles []DB_Profile `gorm:"foreignkey:PersonID"` Profiles []DB_Profile `gorm:"foreignkey:PersonID"`
Stats []DB_SeasonStat `gorm:"foreignkey:PersonID"` Stats []DB_SeasonStat `gorm:"foreignkey:PersonID"`
Friends pq.StringArray `gorm:"type:text[]"`
Discord DB_DiscordPerson `gorm:"foreignkey:PersonID"` Discord DB_DiscordPerson `gorm:"foreignkey:PersonID"`
} }