diff --git a/adapter/anonAp.go b/adapter/anonAp.go index d099cb0..c13f1b2 100644 --- a/adapter/anonAp.go +++ b/adapter/anonAp.go @@ -13,9 +13,9 @@ import ( ) type anonAPAdapter struct { - data *chan models.SocketData - server string - client http.Client + data *chan models.SocketData + server string + client http.Client protocol string nickname string } @@ -27,17 +27,17 @@ type apLink struct { } type apAttachment struct { - MediaType string - Type string - Name string - Summary string - Url string + MediaType string + Type string + Name string + Summary string + Url string } type apTag struct { - Href string - Name string - Type string + Href string + Name string + Type string } type apIcon struct { @@ -55,20 +55,70 @@ type apActor struct { Url string } +type apOutbox struct { + OrderedItems []interface{} `json:"-"` + RawOrderedItems []json.RawMessage `json:"OrderedItems"` +} + +func (self *apOutbox) UnmarshalJSON(b []byte) error { + type obInternal apOutbox + err := json.Unmarshal(b, (*obInternal)(self)) + if err != nil { + return err + } + + for _, raw := range self.RawOrderedItems { + var i apOutboxItem + err := json.Unmarshal(raw, &i) + + var x interface{} + switch i.Type { + case "Create": + fallthrough + case "Update": + x = &apOutboxItemCreateUpdate{} + case "Announce": + x = &apOutboxItemAnnounce{} + } + err = json.Unmarshal(raw, &x) + if err != nil { + return err + } + self.OrderedItems = append(self.OrderedItems, x) + } + return nil +} + +type apOutboxItem struct { + Actor string + Id string + Type string + To string + Published string +} + +type apOutboxItemCreateUpdate struct { + Object apActivity + apOutboxItem +} + +type apOutboxItemAnnounce struct { + Object string + apOutboxItem +} + type apActivity struct { - Id string - Content string - AttributedTo string - Context string - Conversation string - Published string - Tag []apTag - Attachment []apAttachment - To string - Url string - Actor *string - Object *string - InReplyTo *string + Id string + Content string + AttributedTo string + Context string + Conversation string + Published string + Tag []apTag + Attachment []apAttachment + To string + Url string + InReplyTo *string } type webFinger struct { @@ -85,6 +135,10 @@ func (self *anonAPAdapter) Init(data *chan models.SocketData, server, protocol, func getBodyJson(res *http.Response) []byte { l := res.ContentLength + // 4k is a reasonable max size if we get unknown length right? + if l < 0 { + l = 4096 + } jsonData := make([]byte, l) res.Body.Read(jsonData) return jsonData @@ -101,36 +155,36 @@ func (self *anonAPAdapter) makeApRequest(method, url string, data io.Reader) (*h } func (self *anonAPAdapter) toMsg(activity apActivity) *models.Message { - t, err := time.Parse(time.RFC3339, activity.Published) - if err != nil { - t = time.Now() - } - vis := strings.Split(activity.To, "#") - if len(vis) > 1 { - activity.To = vis[1] - } - m := &models.Message{ - Datagram: models.Datagram{ - Id: activity.Id, - Uri: activity.Url, - Type: "message", - Created: t.UnixMilli(), - Updated: nil, - Protocol: self.protocol, - Adapter: self.nickname, - }, - Author: activity.AttributedTo, - Content: activity.Content, - ReplyTo: activity.InReplyTo, - Visibility: activity.To, - } - - for _, a := range activity.Attachment { - m.Attachments = append(m.Attachments, models.Attachment{ - Src: a.Url, - Desc: a.Summary, - }) - } + t, err := time.Parse(time.RFC3339, activity.Published) + if err != nil { + t = time.Now() + } + vis := strings.Split(activity.To, "#") + if len(vis) > 1 { + activity.To = vis[1] + } + m := &models.Message{ + Datagram: models.Datagram{ + Id: activity.Id, + Uri: activity.Url, + Type: "message", + Created: t.UnixMilli(), + Updated: nil, + Protocol: self.protocol, + Adapter: self.nickname, + }, + Author: activity.AttributedTo, + Content: activity.Content, + ReplyTo: activity.InReplyTo, + Visibility: activity.To, + } + + for _, a := range activity.Attachment { + m.Attachments = append(m.Attachments, models.Attachment{ + Src: a.Url, + Desc: a.Summary, + }) + } return m } @@ -147,13 +201,13 @@ func (self *anonAPAdapter) toAuthor(actor apActor) *models.Author { curtime := time.Now().UnixMilli() a := &models.Author{ Datagram: models.Datagram{ - Id: actor.Id, - Uri: actor.Url, - Type: "author", - Created: curtime, - Updated: &curtime, + Id: actor.Id, + Uri: actor.Url, + Type: "author", + Created: curtime, + Updated: &curtime, Protocol: self.protocol, - Adapter: self.nickname, + Adapter: self.nickname, }, Name: actor.PreferredUsername, ProfileData: actor.Summary, @@ -195,22 +249,90 @@ func (self *anonAPAdapter) Fetch(etype string, ids []string) error { self.send(author) } case "byAuthor": - // get outbox - // for each item in outbox, check if it's a Create/Update or an Announce - // Create/Update you can directly deserialize the object - // if it's an Announce, try to get the object and deserialize it, build a boost out of it + // get outbox + if string([]byte{id[0]}) == "@" { + id = id[1:] + } + res, err := http.Get(self.server + "/.well-known/webfinger?resource=acct:" + id) + if err != nil { + return err + } + data := getBodyJson(res) + wf := webFinger{} + json.Unmarshal(data, &wf) + var profile string + for _, l := range wf.Links { + if l.Rel == "self" { + profile = l.Href + break + } + } + res, err = self.makeApRequest("GET", profile+"/outbox", nil) + if err != nil { + return err + } + obData := getBodyJson(res) + ob := apOutbox{} + ob.UnmarshalJSON(obData) + for _, i := range ob.OrderedItems { + switch a := i.(type) { + case *apOutboxItemCreateUpdate: + msg := self.toMsg(a.Object) + if msg != nil { + self.send(msg) + } + case *apOutboxItemAnnounce: + res, err = self.makeApRequest("GET", a.Object, nil) + if err != nil { + return err + } + fmt.Println(a.Object) + activityData := getBodyJson(res) + activity := apActivity{} + json.Unmarshal(activityData, &activity) + ogMsg := self.toMsg(activity) + if ogMsg != nil { + self.send(ogMsg) + } + t, err := time.Parse(time.RFC3339, a.Published) + if err != nil { + t = time.Now() + } + vis := strings.Split(a.To, "#") + if len(vis) > 1 { + a.To = vis[1] + } + boostMsg := models.Message{ + Datagram: models.Datagram{ + Id: a.Id, + Uri: a.Id, + Protocol: self.protocol, + Adapter: self.nickname, + Type: "message", + Created: t.UnixMilli(), + }, + Author: a.Actor, + RenoteId: &ogMsg.Id, + Visibility: a.To, + } + self.send(boostMsg) + } + } + // for each item in outbox, check if it's a Create/Update or an Announce + // Create/Update you can directly deserialize the object + // if it's an Announce, try to get the object and deserialize it, build a boost out of it case "message": - res, err := self.makeApRequest("GET", id, nil) - if err != nil { - return err - } - activityData := getBodyJson(res) - activity := apActivity{} - json.Unmarshal(activityData, &activity) - message := self.toMsg(activity) - if message != nil { - self.send(message) - } + res, err := self.makeApRequest("GET", id, nil) + if err != nil { + return err + } + activityData := getBodyJson(res) + activity := apActivity{} + json.Unmarshal(activityData, &activity) + message := self.toMsg(activity) + if message != nil { + self.send(message) + } case "children": case "convoy": default: