all repos — underbbs @ 9dc27a4b9a016a92b6ab8f0041e3c5319e147913

decentralized social media client

misskey adapter can fetch notes; it needs a lock for its cache
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQT/foVVmI9pK13hPWFohAcXSWbK8wUCZooH3QAKCRBohAcXSWbK
80nuAP0Rd/2TH06a+xXuF3ngysNhtE+uUbaYHooXqTh9DZxjZQD+P9WrTGS+fQqb
fuX2F7xajqSTO4FbsjtEGZXbo69mAww=
=R36Y
-----END PGP SIGNATURE-----
commit

9dc27a4b9a016a92b6ab8f0041e3c5319e147913

parent

e9e286ba3c72ff04d702ca7306c13307b4e0a1db

M adapter/adapter.goadapter/adapter.go

@@ -6,8 +6,9 @@ )

type Adapter interface { Init(Settings, chan SocketData) error + Name() string Subscribe(string) []error - Fetch(string) error + Fetch(string, string) error Do(string) error DefaultSubscriptionFilter() string }
M adapter/mastodon.goadapter/mastodon.go

@@ -21,6 +21,11 @@ }

var scopes = []string{"read", "write", "follow"} +func (self *MastoAdapter) Name() string { + return self.nickname +} + + func (self *MastoAdapter) Init(settings Settings, data chan SocketData) error { self.nickname = settings.Nickname self.server = *settings.Server

@@ -86,7 +91,7 @@ // the stopCh will be closed by a subsequent call to subscribe

return nil } -func (self *MastoAdapter) Fetch(query string) error { +func (self *MastoAdapter) Fetch(etype string, id string) error { return nil }
M adapter/misskey.goadapter/misskey.go

@@ -7,8 +7,9 @@ "github.com/yitsushi/go-misskey"

mkcore "github.com/yitsushi/go-misskey/core" mkm "github.com/yitsushi/go-misskey/models" n "github.com/yitsushi/go-misskey/services/notes" + users "github.com/yitsushi/go-misskey/services/users" tl "github.com/yitsushi/go-misskey/services/notes/timeline" - _ "strings" + "strings" "time" )

@@ -26,6 +27,10 @@

cache map[string]time.Time stop chan bool +} + +func (self *MisskeyAdapter) Name() string { + return self.nickname } func (self *MisskeyAdapter) Init(settings Settings, data chan SocketData) error {

@@ -121,13 +126,13 @@ // check the cache for everything we just collected

// if anything is newer or as of yet not in the cache, add it // and convert it to a SocketData implementation before sending on data channel for _, n := range notes { - msg := self.cacheAndConvert(n) + msg := self.toMessageIfNew(n) if msg != nil { self.data <- msg } } for _, n := range mentions { - msg := self.cacheAndConvert(n) + msg := self.toMessageIfNew(n) if msg != nil { self.data <- msg }

@@ -145,7 +150,7 @@ if err != nil {

fmt.Println(err.Error()) } for _, n := range notes { - msg := self.cacheAndConvert(n) + msg := self.toMessageIfNew(n) if msg == nil { latest = &probenote[0].CreatedAt break

@@ -171,9 +176,21 @@ timestamp, exists := self.cache[n.ID]

return !exists || timestamp.Before(n.CreatedAt) } -func (self *MisskeyAdapter) cacheAndConvert(n mkm.Note) *Message { +func (self *MisskeyAdapter) toMessageIfNew(n mkm.Note) *Message { + return self.toMessage(n, false); +} + +func (self *MisskeyAdapter) toMessage(n mkm.Note, bustCache bool) *Message { timestamp, exists := self.cache[n.ID] - if !exists || timestamp.Before(n.CreatedAt) { + if bustCache || !exists || timestamp.Before(n.CreatedAt) { + host := mkcore.StringValue(n.User.Host) + authorId := "" + if host != "" { + authorId = fmt.Sprintf("@%s@%s", n.User.Username, host) + } else { + authorId = fmt.Sprintf("@%s", n.User.Username) + } + self.cache[n.ID] = n.CreatedAt msg := Message{ Datagram: Datagram{

@@ -186,7 +203,7 @@ },

Created: n.CreatedAt, - Author: fmt.Sprintf("%s@%s", n.User.Username, mkcore.StringValue(n.User.Host)), + Author: authorId, Content: n.Text, Attachments: []Attachment{}, Visibility: n.Visibility,

@@ -209,7 +226,103 @@ }

return nil } -func (self *MisskeyAdapter) Fetch(query string) error { +func (self *MisskeyAdapter) toAuthor(usr mkm.User) *Author { + fmt.Println("converting author: " + usr.ID) + host := mkcore.StringValue(usr.Host) + authorId := "" + if host != "" { + authorId = fmt.Sprintf("@%s@%s", usr.Username, host) + } else { + authorId = fmt.Sprintf("@%s", usr.Username) + } + + author := Author{ + Datagram: Datagram { + Id: authorId, + Uri: mkcore.StringValue(usr.URL), + Protocol: "misskey", + Adapter: self.nickname, + Type: "author", + }, + Name: usr.Name, + ProfilePic: usr.AvatarURL, + ProfileData: usr.Description, + } + + return &author +} + +func (self *MisskeyAdapter) Fetch(etype, id string) error { + switch (etype) { + case "message": + data, err := self.mk.Notes().Show(id) + if err != nil { + return err + } else { + msg := self.toMessage(data, true) + if msg != nil { + self.data <- msg + } + } + case "children": + data, err := self.mk.Notes().Children(n.ChildrenRequest{ + NoteID: id, + Limit: 100, + }) + if err != nil { + return err + } else { + for _, n := range data { + msg := self.toMessage(n, true) + if msg != nil { + self.data <- msg + } + } + } + case "convoy": + data, err := self.mk.Notes().Conversation(n.ConversationRequest{ + NoteID: id, + Limit: 100, + }) + if err != nil { + return err + } else { + for _, n := range data { + msg := self.toMessage(n, true) + if msg != nil{ + self.data <- msg + } + } + } + case "author": + user := "" + host := "" + idParts := strings.Split(id, "@") + user = idParts[0] + if len(idParts) == 2 { + host = idParts[1] + } + + var hostPtr *string = nil + if len(host) > 0 { + hostPtr = &host + } + + // fmt.Printf("attempting user resolution: @%s@%s\n", user, host) + data, err := self.mk.Users().Show(users.ShowRequest{ + Username: &user, + Host: hostPtr, + }) + if err != nil { + return err + } else { + a := self.toAuthor(data) + if a != nil { + self.data <- a + } + } + + } return nil }
M adapter/nostr.goadapter/nostr.go

@@ -17,6 +17,10 @@ privkey string

relays []*nostr.Relay } +func (self *NostrAdapter) Name() string { + return self.nickname +} + func (self *NostrAdapter) Init(settings Settings, data chan SocketData) error { self.nickname = settings.Nickname self.privkey = *settings.PrivKey

@@ -75,7 +79,7 @@ fmt.Println("subscription operation completed without errors")

return nil } -func (self *NostrAdapter) Fetch(query string) error { +func (self *NostrAdapter) Fetch(etype, id string) error { return nil }
M models/msg.gomodels/msg.go

@@ -55,3 +55,11 @@ panic(err.Error())

} return data } + +func (self Author) ToDatagram() []byte { + data, err := json.Marshal(self) + if err != nil { + panic(err.Error()) + } + return data +}
M server/api.goserver/api.go

@@ -147,17 +147,44 @@ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

authHeader := req.Header.Get("Authorization") if strings.HasPrefix(authHeader, "Bearer ") { subscriberKey := strings.Split(authHeader, "Bearer ")[1] - for s, _ := range subscribers { - if s.key == subscriberKey { - next.ServeHTTP(w, req) - return - } + if getSubscriberByKey(subscriberKey, subscribers) != nil { + next.ServeHTTP(w, req); + return } } w.WriteHeader(http.StatusUnauthorized) }) } +func apiAdapterFetch(next http.Handler, subscribers map[*Subscriber][]adapter.Adapter) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + authHeader := req.Header.Get("Authorization") + if strings.HasPrefix(authHeader, "Bearer ") { + subscriberKey := strings.Split(authHeader, "Bearer ")[1] + s := getSubscriberByKey(subscriberKey, subscribers) + if s != nil { + apiParams := req.Context().Value("params").(map[string]string) + queryParams := req.URL.Query() + for _, a := range subscribers[s] { + if a.Name() == apiParams["adapter_id"] { + err := a.Fetch(queryParams["entity_type"][0], queryParams["entity_id"][0]) + if err != nil { + fmt.Println(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusAccepted) + } + next.ServeHTTP(w, req) + return + } + } + } + } + w.WriteHeader(http.StatusUnauthorized) + }) +} + + func (self *BBSServer) apiMux() http.Handler { errTemplate, err := template.New("err").Parse("{{ $params := (.Context).Value \"params\" }}<html><body><h1>ERROR {{ $params.ErrorCode }}</h1><p class='error'>{{ $params.ErrorMessage }}</p></body></html>") if err != nil {

@@ -181,6 +208,11 @@ // adapters/:name/subscribe

rtr.Post(`/adapters/(?P<id>\S+)/subscribe`, ProtectWithSubscriberKey( apiAdapterSubscribe(renderer.JSON("data")), self.subscribers, + )) + + rtr.Get(`/adapters/(?P<adapter_id>\S+)/fetch`, ProtectWithSubscriberKey( + apiAdapterFetch(renderer.JSON("data"), self.subscribers), + self.subscribers, )) return http.HandlerFunc(rtr.ServeHTTP)
M server/server.goserver/server.go

@@ -48,7 +48,8 @@ srvr.serveMux.Handle("/api/", http.StripPrefix("/api", srvr.apiMux()))

// websocket srvr.serveMux.HandleFunc("/subscribe", srvr.subscribeHandler) - srvr.serveMux.HandleFunc("/publish", srvr.publishHandler) + // publish is unused currently, we just use the API and send data back on the websocket + // srvr.serveMux.HandleFunc("/publish", srvr.publishHandler) return srvr }
M ts/adapter-element.tsts/adapter-element.ts

@@ -213,7 +213,14 @@

// otherwise we can orphan it and try to fill it in later if (this._orphans.filter(o=>o.id == msg.id).length == 0) { this._orphans.push(msg); - // TODO: request the parent's data + if (msg.replyTo) { + // request the parent's data, which will try to adopt this orphan when it comes in + util.authorizedFetch( + "GET", + `/api/adapters/${this._name}/fetch?entity_type=message&entity_id=${msg.replyTo}`, + null); + } + } return null; }
M ts/thread-summary-element.tsts/thread-summary-element.ts

@@ -41,7 +41,10 @@ threadText.innerHTML = this._msg.content;

} let author = datastore.profileCache.get(this._msg.author); if (!author) { - // request it! + util.authorizedFetch( + "GET", + `/api/adapters/${this._adapter}/fetch?entity_type=author&entity_id=${this._msg.author}`, + null); } this._author = author || <Author>{ id: this._msg.author }; const threadAuthor = this.querySelector(".thread_author");