underbbs/adapter/misskey.go

166 lines
3.9 KiB
Go

package adapter
import (
"fmt"
. "forge.lightcrystal.systems/lightcrystal/underbbs/models"
"github.com/yitsushi/go-misskey"
mkm "github.com/yitsushi/go-misskey/models"
notes "github.com/yitsushi/go-misskey/services/notes"
tl "github.com/yitsushi/go-misskey/services/notes/timeline"
_ "strings"
"time"
)
type MisskeyAdapter struct {
data chan SocketData
nickname string
server string
apiKey string
mk *misskey.Client
// unlike the mastodon client, we have to manage combining resources
// from different API calls instead of streaming them in a single channel
cache map[string]time.Time
stop chan bool
notes chan mkm.Note
users chan mkm.User
follows chan mkm.FollowStatus
}
func (self *MisskeyAdapter) Init(settings Settings, data chan SocketData) error {
fmt.Println("initializing misskey adapter")
self.nickname = settings.Nickname
self.server = *settings.Server
self.apiKey = *settings.ApiKey
self.data = data
fmt.Println("getting ready to initialize internal client")
client, err := misskey.NewClientWithOptions(
misskey.WithAPIToken(self.apiKey),
misskey.WithBaseURL("https", self.server, ""),
)
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Println("misskey client initialized")
self.mk = client
self.cache = make(map[string]time.Time)
return nil
}
func (self *MisskeyAdapter) Subscribe(filter string) []error {
// misskey streaming API is undocumented....
// we could try to reverse engineer it by directly connecting to the websocket???
// alternatively, we can poll timelines, mentions, etc with a cancellation channel,
// keep a cache of IDs in memory, and send new objects on the data channel
// TODO: decode the filter so we can send data to the mk services
// same as in masto, we will want to close and reopen the stop channel
if self.stop != nil {
close(self.stop)
}
self.stop = make(chan bool)
// in the background, continuously read data from these API endpoints
// if they are newer than the cache, convert them to UnderBBS objects
// and send them on the data channel
go func() {
notesService := self.mk.Notes()
timelineService := notesService.Timeline()
for {
select {
case _, ok := <-self.stop:
if !ok {
return
}
default:
// TODO: we have to actually decode and pass our filter criteria
tlnotes, tlerr := timelineService.Get(tl.GetRequest{
Limit: 50,
// how are you supposed to bootstrap these??
SinceID: "xxxxxxxxxx",
UntilID: "xxxxxxxxxx",
SinceDate: 0,
})
mentions, merr := notesService.Mentions(notes.MentionsRequest{
Limit: 50,
})
if tlerr != nil {
fmt.Println(tlerr.Error())
}
if merr != nil {
fmt.Println(merr.Error())
}
// 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 tlnotes {
msg := self.cacheAndConvert(n)
if msg != nil {
self.data <- msg
}
}
for _, n := range mentions {
msg := self.cacheAndConvert(n)
if msg != nil {
self.data <- msg
}
}
}
}
}()
return nil
}
func (self *MisskeyAdapter) cacheAndConvert(n mkm.Note) *Message {
timestamp, exists := self.cache[n.ID]
if !exists || timestamp.Before(n.CreatedAt) {
self.cache[n.ID] = n.CreatedAt
msg := Message{
Uri: n.URI,
Author: Author{
Id: n.User.ID,
Name: n.User.Name,
// ProfileUri: *n.User.URL,
ProfilePic: n.User.AvatarURL,
},
Protocol: "misskey",
Adapter: self.nickname,
Created: n.CreatedAt,
Content: n.Text,
}
return &msg
}
return nil
}
func (self *MisskeyAdapter) Fetch(query string) error {
return nil
}
func (self *MisskeyAdapter) Do(action string) error {
return nil
}
func (self *MisskeyAdapter) DefaultSubscriptionFilter() string {
return ""
}