Add more xmpp functions
This commit is contained in:
parent
444d6a4838
commit
ba4f3301be
13
aid/aid.go
13
aid/aid.go
|
@ -6,8 +6,6 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func WaitForExit() {
|
func WaitForExit() {
|
||||||
|
@ -16,17 +14,6 @@ func WaitForExit() {
|
||||||
<-sc
|
<-sc
|
||||||
}
|
}
|
||||||
|
|
||||||
func JSONStringify(input interface{}) string {
|
|
||||||
json, _ := json.Marshal(input)
|
|
||||||
return string(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
func JSONParse(input string) interface{} {
|
|
||||||
var output interface{}
|
|
||||||
json.Unmarshal([]byte(input), &output)
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func RandomString(n int) string {
|
func RandomString(n int) string {
|
||||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
|
|
||||||
|
|
|
@ -14,3 +14,14 @@ func (j *JSON) ToBytes() []byte {
|
||||||
json, _ := json.Marshal(j)
|
json, _ := json.Marshal(j)
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func JSONStringify(input interface{}) string {
|
||||||
|
json, _ := json.Marshal(input)
|
||||||
|
return string(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
func JSONParse(input string) interface{} {
|
||||||
|
var output interface{}
|
||||||
|
json.Unmarshal([]byte(input), &output)
|
||||||
|
return output
|
||||||
|
}
|
|
@ -10,17 +10,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type SocketType string
|
type SocketType string
|
||||||
var (
|
const SocketTypeXmpp SocketType = "xmpp"
|
||||||
SocketTypeXmpp SocketType = "xmpp"
|
const SocketTypeUnknown SocketType = "unknown"
|
||||||
SocketTypeUnknown SocketType = "unknown"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Socket struct {
|
type Socket struct {
|
||||||
ID string
|
ID string
|
||||||
JID string
|
|
||||||
Type SocketType
|
|
||||||
Connection *websocket.Conn
|
Connection *websocket.Conn
|
||||||
Person *person.Person
|
Person *person.Person
|
||||||
|
|
||||||
|
Type SocketType
|
||||||
|
PresenceState *PresenceState
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageToWrite struct {
|
type MessageToWrite struct {
|
||||||
|
@ -50,7 +49,7 @@ func (s *Socket) WriteTree(message *etree.Document) {
|
||||||
var (
|
var (
|
||||||
socketWriteQueue = make(chan MessageToWrite, 1000)
|
socketWriteQueue = make(chan MessageToWrite, 1000)
|
||||||
socketHandlers = map[SocketType]func(string) {
|
socketHandlers = map[SocketType]func(string) {
|
||||||
SocketTypeXmpp: handlePresenceSocket,
|
SocketTypeXmpp: presenceSocketHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
sockets = aid.GenericSyncMap[Socket]{}
|
sockets = aid.GenericSyncMap[Socket]{}
|
||||||
|
@ -98,6 +97,24 @@ func WebsocketConnection(c *websocket.Conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSocketByPerson(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() {
|
func init() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -110,7 +127,7 @@ func init() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
message := <-socketWriteQueue
|
message := <-socketWriteQueue
|
||||||
aid.Print("(socket) message sent", message.Socket.ID, string(message.Message))
|
aid.Print("(socket) writing message to", message.Socket.ID, string(message.Message))
|
||||||
message.Socket.Connection.WriteMessage(websocket.TextMessage, message.Message)
|
message.Socket.Connection.WriteMessage(websocket.TextMessage, message.Message)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
167
handlers/xmpp.go
167
handlers/xmpp.go
|
@ -8,23 +8,58 @@ import (
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/ectrc/snow/aid"
|
"github.com/ectrc/snow/aid"
|
||||||
p "github.com/ectrc/snow/person"
|
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 (
|
var (
|
||||||
socketXmppMessageHandlers = map[string]func(*Socket, *etree.Document) error {
|
socketXmppMessageHandlers = map[string]func(*Socket, *etree.Document) error {
|
||||||
"open": presenceSocketOpenEvent,
|
"open": presenceSocketOpenEvent,
|
||||||
"iq": presenceSocketIqRootEvent,
|
"iq": presenceSocketIqRootEvent,
|
||||||
|
"message": presenceSocketMessageEvent,
|
||||||
|
"presence": presenceSocketPresenceEvent,
|
||||||
"close": presenceSocketCloseEvent,
|
"close": presenceSocketCloseEvent,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func handlePresenceSocket(id string) {
|
func presenceSocketHandle(id string) {
|
||||||
aid.Print("(xmpp) connection opened", id)
|
|
||||||
socket, ok := sockets.Get(id)
|
socket, ok := sockets.Get(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
socket.Type = SocketTypeXmpp
|
||||||
|
socket.PresenceState = &PresenceState{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, message, err := socket.Connection.ReadMessage()
|
_, message, err := socket.Connection.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,15 +71,66 @@ func handlePresenceSocket(id string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
aid.Print("(xmpp) message received", string(message))
|
|
||||||
|
|
||||||
if handler, ok := socketXmppMessageHandlers[parsed.Root().Tag]; ok {
|
if handler, ok := socketXmppMessageHandlers[parsed.Root().Tag]; ok {
|
||||||
if err := handler(socket, parsed); err != nil {
|
if err := handler(socket, parsed); err != nil {
|
||||||
aid.Print("(xmpp) connection closed", id, err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, partial := range storage.Repo.GetFriendsForPerson(socket.Person.ID) {
|
||||||
|
friend := socket.Person.GetFriend(partial.ID)
|
||||||
|
if friend == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
friendSocket := GetSocketByPerson(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.WriteTree(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.WriteTree(document)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presenceSocketOpenEvent(socket *Socket, tree *etree.Document) error {
|
func presenceSocketOpenEvent(socket *Socket, tree *etree.Document) error {
|
||||||
|
@ -105,7 +191,7 @@ func presenceSocketIqSetEvent(socket *Socket, tree *etree.Document) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.Person = person
|
socket.Person = person
|
||||||
socket.JID = person.ID + "@prod.ol.epicgames.com/" + tree.Root().SelectElement("query").SelectElement("resource").Text()
|
socket.PresenceState.JID = person.ID + "@prod.ol.epicgames.com/" + tree.Root().SelectElement("query").SelectElement("resource").Text()
|
||||||
|
|
||||||
document := etree.NewDocument()
|
document := etree.NewDocument()
|
||||||
iq := document.CreateElement("iq")
|
iq := document.CreateElement("iq")
|
||||||
|
@ -114,6 +200,7 @@ func presenceSocketIqSetEvent(socket *Socket, tree *etree.Document) error {
|
||||||
iq.Attr = append(iq.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
|
iq.Attr = append(iq.Attr, etree.Attr{Key: "from", Value: "prod.ol.epicgames.com"})
|
||||||
|
|
||||||
socket.WriteTree(document)
|
socket.WriteTree(document)
|
||||||
|
SendPresenceSocketStatusToFriends(socket)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +218,74 @@ func presenceSocketIqGetEvent(socket *Socket, tree *etree.Document) error {
|
||||||
return nil
|
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 := GetSocketByPerson(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.WriteTree(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)
|
||||||
|
|
||||||
|
SendPresenceSocketStatusToFriends(socket)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendPresenceSocketStatusToFriends(socket *Socket) {
|
||||||
|
for _, partial := range storage.Repo.GetFriendsForPerson(socket.Person.ID) {
|
||||||
|
friend := socket.Person.GetFriend(partial.ID)
|
||||||
|
if friend == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
friendSocket := GetSocketByPerson(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(socket.PresenceState.RawStatus)
|
||||||
|
friendSocket.WriteTree(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: socket.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)
|
||||||
|
socket.WriteTree(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -10,7 +10,6 @@ Performance first, universal Fortnite private server backend written in Go.
|
||||||
- **Blazingly Fast** Written in Go and built upon Fast HTTP, it is extremely fast and can handle any profile action in milliseconds with its caching system.
|
- **Blazingly Fast** Written in Go and built upon Fast HTTP, it is extremely fast and can handle any profile action in milliseconds with its caching system.
|
||||||
- **Profile Changes** Automatically keeps track of profile changes exactly so any external changes are displayed in-game on the next action.
|
- **Profile Changes** Automatically keeps track of profile changes exactly so any external changes are displayed in-game on the next action.
|
||||||
- **Universal Database** It is possible to add new database types to satisfy your needs. Currently, it only supports `postgresql`.
|
- **Universal Database** It is possible to add new database types to satisfy your needs. Currently, it only supports `postgresql`.
|
||||||
- **Production Ready** Optimised and tested to be ran in production environments. It is also possible to run multiple instances of the backend to scale it horizontally.
|
|
||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user