all repos — underbbs @ 9195bba7eda7c68dc578581ace51da60a5967529

decentralized social media client

adapter/mastodon.go (raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package adapter

import (
	"fmt"
	. "forge.lightcrystal.systems/lightcrystal/underbbs/models"
	madon "github.com/McKael/madon"
	"log"
	"os"
)

type MastoAdapter struct {
	data     *chan SocketData
	nickname string
	server   string
	apiKey   string

	masto *madon.Client

	events chan madon.StreamEvent
	stop   chan bool
	done   chan bool
}

var scopes = []string{"read", "write", "follow"}

func (self *MastoAdapter) send(data SocketData) {
	if self.data != nil {
		*self.data <- data
	} else {
		fmt.Println(os.Stdout, string(data.ToDatagram()))
	}
}

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
	self.apiKey = *settings.ApiKey
	self.data = data

	masto, err := madon.NewApp("underbbs", "https://lightcrystal.systems", scopes, madon.NoRedirect, self.server)
	if err != nil {
		return err
	}
	self.masto = masto
	err = self.masto.SetUserToken(self.apiKey, "", "", []string{})
	return err
}

func (self *MastoAdapter) Subscribe(filter string) []error {
	// TODO: decode separate timelines and hashtags
	// for now, the filter is just the timeline

	if self.events != nil {
		close(self.events)
	}
	self.events = make(chan madon.StreamEvent)

	if self.stop != nil {
		close(self.stop)
	}
	self.stop = make(chan bool)

	self.done = make(chan bool)

	err := self.masto.StreamListener(filter, "", self.events, self.stop, self.done)
	if err != nil {
		return []error{err}
	}
	go func() {
		for e := range self.events {
			log.Printf("event: %s !!!", e.Event)
			switch e.Event {
			case "error":
			case "update":
				var msg *Message
				switch v := e.Data.(type) {
				case int64:
					s, _ := self.masto.GetStatus(v)
					if s != nil {
						msg = self.mastoUpdateToMessage(*s)
					}
				case madon.Status:
					msg = self.mastoUpdateToMessage(v)
				}
				if msg != nil {
					self.send(msg)
				}
			case "notification":
			case "delete":
			}
		}
	}()
	// in the background, read and translate events from the stream
	// and check for doneCh closing
	// the stopCh will be closed by a subsequent call to subscribe
	return nil
}

func (self *MastoAdapter) Fetch(etype string, ids []string) error {
	return nil
}

func (self *MastoAdapter) Do(action string, data []string) error {
	return nil
}

func (self *MastoAdapter) DefaultSubscriptionFilter() string {
	return "user"
}

func (self *MastoAdapter) mastoUpdateToMessage(status madon.Status) *Message {
	msg := Message{
		Datagram: Datagram{
			Protocol: "mastodon",
			Adapter:  self.nickname,
			Id:       fmt.Sprintf("%d", status.ID),
			Uri:      status.URI,
			Type:     "message",
			Created:  status.CreatedAt.UnixMilli(),
		},
		Content:    status.Content,
		Author:     status.Account.Acct,
		Visibility: status.Visibility,
	}
	if status.InReplyToID != nil {
		idStr := fmt.Sprintf("%d", *status.InReplyToID)
		msg.ReplyTo = &idStr
	}
	// TODO: mentions and replies

	return &msg
}