package adapter import ( "encoding/json" "errors" "fmt" . "forge.lightcrystal.systems/lightcrystal/underbbs/models" "net/http" "net/url" "os" "strconv" "strings" "sync" "time" ) type donk struct { Desc string URL string } type honk struct { ID int Honker string Handles []string Oonker *string XID string RID *string Noise string Donks []donk Convoy string Public bool Date string } type HonkAdapter struct { data *chan SocketData nickname string server string username string password string token string cache map[string]time.Time maxId int mtx sync.RWMutex stop chan bool } func (self *HonkAdapter) isAnonymous() bool { return self.token == "" } func (self *HonkAdapter) send(data SocketData) { if self.data != nil { *self.data <- data } else { fmt.Fprintln(os.Stdout, string(data.ToDatagram())) } } func (self *HonkAdapter) Name() string { return self.nickname } func (self *HonkAdapter) Init(settings Settings, data *chan SocketData) error { self.data = data // separate name and server in handle parts := strings.Split(*settings.Handle, "@") self.username = parts[1] self.server = "https://" + parts[2] self.nickname = settings.Nickname if settings.Password == nil || *settings.Password == "" { // we're anonymous! return nil } self.password = *settings.Password r, err := http.PostForm(self.server+"/dologin", url.Values{ "username": []string{self.username}, "password": []string{self.password}, "gettoken": []string{"1"}, }) if err != nil { return err } var buf [32]byte _, err = r.Body.Read(buf[:]) if err != nil { return err } self.token = string(buf[:]) fmt.Println(self.token) return nil } func (self *HonkAdapter) Subscribe(filter string) []error { if self.stop != nil { close(self.stop) self.maxId = 0 } self.stop = make(chan bool) go self.gethonks(filter) return nil } func (self *HonkAdapter) gethonks(filter string) { for { select { case _, ok := <-self.stop: if !ok { return } default: honkForm := url.Values{ "action": []string{"gethonks"}, "token": []string{self.token}, "page": []string{filter}, } if self.maxId != 0 { honkForm["after"] = []string{strconv.FormatInt(int64(self.maxId), 10)} } res, err := http.PostForm(self.server+"/api", honkForm) if err != nil { // return? } honksData := getBodyJson(res) honks := []honk{} json.Unmarshal(honksData, &honks) for _, h := range honks { if h.ID > self.maxId { self.maxId = h.ID msg := self.toMsg(h) self.send(msg) } } time.Sleep(5 * time.Second) } } } func (self *HonkAdapter) toMsg(h honk) Message { t, err := time.Parse(time.RFC3339, h.Date) if err != nil { t = time.Now() } tt := t.UnixMilli() a := h.Honker if h.Oonker != nil { a = *h.Oonker } msg := Message{ Datagram: Datagram{ Id: h.XID, Uri: h.XID, Protocol: "honk", Adapter: self.nickname, Type: "message", Created: tt, }, Author: a, Content: h.Noise, ReplyTo: h.RID, Visibility: "Private", } if h.Public { msg.Visibility = "Public" } if h.Oonker != nil { r := fmt.Sprintf("%s/bonk/%d", h.Honker, h.ID) msg.Renoter = h.Oonker msg.RenoteId = &r msg.RenoteTime = &tt } for _, d := range h.Donks { a := Attachment{ Src: d.URL, Desc: d.Desc, } msg.Attachments = append(msg.Attachments, a) } return msg } func (self *HonkAdapter) Fetch(etype string, ids []string) error { // honk API is limited, we fall back to the anonymous adapter for fetch ops aaa := anonAPAdapter{} aaa.Init(self.data, self.server, "honk", self.nickname) return aaa.Fetch(etype, ids) } func (self *HonkAdapter) Do(action string, data map[string]string) error { switch action { case "post": res, err := http.PostForm(self.server+"/api", url.Values{ "action": []string{"honk"}, "token": []string{self.token}, "noise": []string{data["content"]}, }) if err != nil { return err } var buf [256]byte _, err = res.Body.Read(buf[:]) if err != nil { return err } fmt.Println(string(buf[:])) default: return errors.New("Do: unknown action") } return nil } func (self *HonkAdapter) DefaultSubscriptionFilter() string { return "home" }