diff --git a/handlers/parties.go b/handlers/parties.go index 99ee5b2..9d93382 100644 --- a/handlers/parties.go +++ b/handlers/parties.go @@ -240,9 +240,12 @@ func PostPartyInvite(c *fiber.Ctx) error { invite := p.NewPartyInvite(party, person, towards, body) party.AddInvite(invite) towards.Invites.Set(party.ID, invite) - socket.EmitPartyInvite(invite) + if c.QueryBool("sendPing", false) { + socket.EmitPartyPingFromInvite(invite) + } + return c.SendStatus(204) } @@ -257,6 +260,16 @@ func PostPartyJoin(c *fiber.Ctx) error { return c.Status(400).JSON(aid.ErrorBadRequest("Party Not Found")) } + if party.Config["joinability"] != "OPEN" { + invite := party.GetInvite(person) + if invite == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("No Invite Found")) + } + + party.RemoveInvite(invite) + person.Invites.Delete(party.ID) + } + var body struct { Meta map[string]interface{} `json:"meta"` Connection aid.JSON `json:"connection"` @@ -300,5 +313,100 @@ func PostPartyPromoteMember(c *fiber.Ctx) error { party.PromoteMember(member) socket.EmitPartyNewCaptain(party) + return c.SendStatus(204) +} + +func PostPartyCreateIntention(c *fiber.Ctx) error { + person := c.Locals("person").(*p.Person) + + var body map[string]interface{} + if err := c.BodyParser(&body); err != nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request")) + } + + towards := p.Find(c.Params("friendId")) + if towards == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Person Not Found")) + } + + var party *p.Party + towards.Parties.Range(func(key string, p *p.Party) bool { + party = p + return false + }) + + if party == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Party Not Found")) + } + + intention := p.NewPartyIntention(party, person, towards, body) + party.AddIntention(intention) + person.Intentions.Set(towards.ID, intention) + socket.EmitPartyIntention(intention) + + return c.Status(204).JSON(intention.GenerateFortnitePartyIntention()) +} + +func PostPartyJoinFromPing(c *fiber.Ctx) error { + person := c.Locals("person").(*p.Person) + if person.Parties.Len() != 0 { + return c.Status(400).JSON(aid.ErrorBadRequest("Already in a party")) + } + + party, ok := p.Parties.Get(c.Params("partyId")) + if !ok { + return c.Status(400).JSON(aid.ErrorBadRequest("Party Not Found")) + } + + intention, ok := person.Intentions.Get(c.Params("friendId")) + if !ok { + return c.Status(400).JSON(aid.ErrorBadRequest("Intention Not Found")) + } + + if intention.Party.ID != party.ID { + return c.Status(400).JSON(aid.ErrorBadRequest("Intention Not for this party")) + } + + var body struct { + Meta map[string]interface{} `json:"meta"` + Connection aid.JSON `json:"connection"` + } + + if err := c.BodyParser(&body); err != nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request")) + } + + party.AddMember(person) + party.UpdateMemberMeta(person, body.Meta) + party.UpdateMemberConnection(person, body.Connection) + party.RemoveIntention(intention) + + member := party.GetMember(person) + socket.EmitPartyMemberJoined(party, member) + socket.EmitPartyMemberMetaUpdated(party, party.GetMember(person), body.Meta, []string{}) + socket.EmitPartyMetaUpdated(party, party.Meta, []string{}, map[string]interface{}{}) + + return c.Status(200).JSON(aid.JSON{ + "party_id": party.ID, + "status": "JOINED", + }) +} + +func PostPartyDeletePings(c *fiber.Ctx) error { + person := c.Locals("person").(*p.Person) + + friend := p.Find(c.Params("friendId")) + if friend == nil { + c.Status(400).JSON(aid.ErrorBadRequest("Friend Not Found")) + return nil + } + + person.Intentions.Delete(friend.ID) + friend.Parties.Range(func(key string, party *p.Party) bool { + intent := party.GetIntention(person) + party.RemoveIntention(intent) + return true + }) + return c.SendStatus(204) } \ No newline at end of file diff --git a/handlers/storage.go b/handlers/storage.go index d98a689..93a6d7d 100644 --- a/handlers/storage.go +++ b/handlers/storage.go @@ -4,6 +4,7 @@ import ( "crypto/sha1" "crypto/sha256" "encoding/hex" + "time" "github.com/ectrc/snow/aid" "github.com/ectrc/snow/person" @@ -26,12 +27,12 @@ func (c *cloudstorage) Add(name string, bytes []byte) error { "hash256": hex.EncodeToString(sumation256[:]), "length": len(bytes), "contentType": "application/octet-stream", - "uploaded": aid.TimeStartOfDay(), + "uploaded": time.Now().Format(time.RFC3339), "storageType": "S3", "storageIds": aid.JSON{ "primary": "primary", }, - "doNotCache": false, + "doNotCache": true, }) return nil diff --git a/main.go b/main.go index 2c5d9cd..f558590 100644 --- a/main.go +++ b/main.go @@ -156,6 +156,8 @@ func main() { party.Get("/user/:accountId/settings/privacy", handlers.GetPartyUserPrivacy) party.Get("/user/:accountId/notifications/undelivered/count", handlers.GetPartyNotifications) party.Get("/user/:accountId/pings/:friendId/parties", handlers.GetPartyPingsFromFriend) + party.Post("/user/:accountId/pings/:friendId/join", handlers.PostPartyJoinFromPing) + party.Delete("/user/:accountId/pings/:friendId", handlers.PostPartyDeletePings) party.Get("/parties/:partyId", handlers.GetPartyForMember) party.Post("/parties", handlers.PostPartyCreate) party.Post("/parties/:partyId/invites/:accountId", handlers.PostPartyInvite) @@ -164,12 +166,7 @@ func main() { party.Patch("/parties/:partyId", handlers.PatchPartyUpdateState) party.Patch("/parties/:partyId/members/:accountId/meta", handlers.PatchPartyUpdateMemberState) party.Delete("/parties/:partyId/members/:accountId", handlers.DeletePartyMember) - // post /parties/:partyId/members/:accountId/conferences/connection (join a voip channel) - // get /user/:accountId/pings/:pinger/friendId/parties (get pings from a friend) - // post /user/:accountId/pings/:pinger/join (join a party from a ping) - // post /user/:friendId/pings/:accountId (send a ping) - // delete /user/:accountId/pings/:pinger/friendId (delete pings) - // post /members/:friendId/intentions/:accountId (send an invite and add invite to party) + party.Post("/members/:friendId/intentions/:accountId", handlers.PostPartyCreateIntention) snow := r.Group("/snow") snow.Use(handlers.MiddlewareOnlyDebug) @@ -195,13 +192,12 @@ func main() { r.All("*", func(c *fiber.Ctx) error { return c.Status(fiber.StatusNotFound).JSON(aid.ErrorNotFound) }) if aid.Config.Fortnite.Season <= 2 { - t := handlers.NewServer() - go t.Listen() + // t := handlers.NewServer() + // go t.Listen() } err := r.Listen("0.0.0.0" + aid.Config.API.Port) if err != nil { panic(fmt.Sprintf("(fiber) failed to listen: %v", err)) } - } diff --git a/person/parties.go b/person/parties.go index a4af35a..0ebb683 100644 --- a/person/parties.go +++ b/person/parties.go @@ -8,6 +8,40 @@ import ( "github.com/google/uuid" ) +type PartyIntention struct{ + Party *Party `json:"-"` + Requester *Person `json:"-"` + Towards *Person `json:"-"` + Meta map[string]interface{} + CreatedAt time.Time + ExpiresAt time.Time +} + +func NewPartyIntention(party *Party, requester *Person, towards *Person, meta map[string]interface{}) *PartyIntention { + return &PartyIntention{ + Party: party, + Requester: requester, + Towards: towards, + Meta: meta, + CreatedAt: time.Now(), + ExpiresAt: time.Now().Add(time.Minute * 60), + } +} + +func (pi *PartyIntention) GenerateFortnitePartyIntention() aid.JSON { + return aid.JSON{ + "requester_id": pi.Requester.ID, + "requester_dn": pi.Requester.DisplayName, + "requester_pl": "", + "requester_pl_dn": "", + "requestee_id": pi.Towards.ID, + "meta": pi.Meta, + "expires_at": pi.ExpiresAt.Format(time.RFC3339), + "sent_at": pi.CreatedAt.Format(time.RFC3339), + } +} + + type PartyInvite struct{ Party *Party `json:"-"` Inviter *Person `json:"-"` @@ -81,6 +115,7 @@ type Party struct{ Captain *PartyMember Members map[string]*PartyMember Invites []*PartyInvite + Intentions []*PartyIntention Config map[string]interface{} Meta map[string]interface{} CreatedAt time.Time @@ -95,15 +130,16 @@ var ( func NewParty() *Party { party := &Party{ ID: uuid.New().String(), - Members: make(map[string]*PartyMember), + Members: map[string]*PartyMember{}, Config: map[string]interface{}{ "type": "DEFAULT", "sub_type": "default", "intention_ttl:": 60, "invite_ttl:": 60, }, - Meta: make(map[string]interface{}), + Meta: map[string]interface{}{}, Invites: []*PartyInvite{}, + Intentions: []*PartyIntention{}, CreatedAt: time.Now(), } @@ -261,6 +297,19 @@ func (p *Party) AddInvite(invite *PartyInvite) { p.Invites = append(p.Invites, invite) } +func (p *Party) GetInvite(person *Person) *PartyInvite { + p.m.Lock() + defer p.m.Unlock() + + for _, invite := range p.Invites { + if invite.Towards == person { + return invite + } + } + + return nil +} + func (p *Party) RemoveInvite(invite *PartyInvite) { p.m.Lock() defer p.m.Unlock() @@ -273,6 +322,38 @@ func (p *Party) RemoveInvite(invite *PartyInvite) { } } +func (p *Party) AddIntention(intention *PartyIntention) { + p.m.Lock() + defer p.m.Unlock() + + p.Intentions = append(p.Intentions, intention) +} + +func (p *Party) GetIntention(person *Person) *PartyIntention { + p.m.Lock() + defer p.m.Unlock() + + for _, intention := range p.Intentions { + if intention.Towards == person { + return intention + } + } + + return nil +} + +func (p *Party) RemoveIntention(intention *PartyIntention) { + p.m.Lock() + defer p.m.Unlock() + + for i, v := range p.Intentions { + if v == intention { + p.Intentions = append(p.Intentions[:i], p.Intentions[i+1:]...) + break + } + } +} + func (p *Party) GenerateFortniteParty() aid.JSON { p.m.Lock() defer p.m.Unlock() @@ -298,5 +379,9 @@ func (p *Party) GenerateFortniteParty() aid.JSON { party["invites"] = append(party["invites"].([]aid.JSON), invite.GenerateFortnitePartyInvite()) } + for _, intention := range p.Intentions { + party["intentions"] = append(party["intentions"].([]aid.JSON), intention.GenerateFortnitePartyIntention()) + } + return party } \ No newline at end of file diff --git a/person/person.go b/person/person.go index bc694b2..c69b492 100644 --- a/person/person.go +++ b/person/person.go @@ -24,6 +24,7 @@ type Person struct { Relationships aid.GenericSyncMap[Relationship] Parties aid.GenericSyncMap[Party] Invites aid.GenericSyncMap[PartyInvite] + Intentions aid.GenericSyncMap[PartyIntention] } func NewPerson() *Person { @@ -42,6 +43,7 @@ func NewPerson() *Person { Relationships: aid.GenericSyncMap[Relationship]{}, Parties: aid.GenericSyncMap[Party]{}, Invites: aid.GenericSyncMap[PartyInvite]{}, + Intentions: aid.GenericSyncMap[PartyIntention]{}, } } @@ -61,6 +63,7 @@ func NewPersonWithCustomID(id string) *Person { Relationships: aid.GenericSyncMap[Relationship]{}, Parties: aid.GenericSyncMap[Party]{}, Invites: aid.GenericSyncMap[PartyInvite]{}, + Intentions: aid.GenericSyncMap[PartyIntention]{}, } } @@ -210,6 +213,7 @@ func findHelper(databasePerson *storage.DB_Person, shallow bool, save bool) *Per Relationships: aid.GenericSyncMap[Relationship]{}, Parties: aid.GenericSyncMap[Party]{}, Invites: aid.GenericSyncMap[PartyInvite]{}, + Intentions: aid.GenericSyncMap[PartyIntention]{}, } for _, ban := range databasePerson.BanHistory { diff --git a/socket/events.go b/socket/events.go index e1cf092..cd15c30 100644 --- a/socket/events.go +++ b/socket/events.go @@ -163,4 +163,52 @@ func EmitPartyInvite(invite *person.PartyInvite) { "sent": time.Now().Format(time.RFC3339), "type": "com.epicgames.social.party.notification.v0.INITIAL_INVITE", }) +} + +func EmitPartyIntention(invite *person.PartyIntention) { + s, ok := JabberSockets.Get(invite.Towards.ID) + if !ok { + return + } + + s.JabberSendMessageToPerson(aid.JSON{ + "requester_id": invite.Requester.ID, + "requester_dn": invite.Requester.DisplayName, + "requester_pl": "win", + "requester_pl_dn": invite.Requester.DisplayName, + "requestee_id": invite.Towards.ID, + "meta": invite.Meta, + "sent_at": invite.CreatedAt.Format(time.RFC3339), + "friends_ids": []string{}, + "members_count": len(invite.Party.Members), + "party_id": invite.Party.ID, + "ns": "Fortnite", + "sent": time.Now().Format(time.RFC3339), + "type": "com.epicgames.social.party.notification.v0.INITIAL_INTENTION", + }) +} + +func EmitPartyPingFromInvite(i *person.PartyInvite) { + s, ok := JabberSockets.Get(i.Towards.ID) + if !ok { + return + } + + meta := i.Meta + meta["urn:epic:member:dn_s"] = i.Inviter.DisplayName + meta["urn:epic:invite:platformdata_s"] = "RequestToJoin" + meta["urn:epic:conn:platform_s"] = "WIN" + meta["urn:epic:conn:platform:dn_s"] = i.Inviter.DisplayName + + s.JabberSendMessageToPerson(aid.JSON{ + "expires_at": i.CreatedAt.Add(time.Minute * 60).Format(time.RFC3339), + "pinger_id": i.Inviter.ID, + "pinger_dn": i.Inviter.DisplayName, + "pinger_pl": "win", + "pinger_pl_dn": i.Inviter.DisplayName, + "meta": meta, + "ns": "Fortnite", + "sent": time.Now().Format(time.RFC3339), + "type": "com.epicgames.social.party.notification.v0.PING", + }) } \ No newline at end of file