all repos — underbbs @ 678e5e62584a2edc9f909c0b02247fa7ba95f71c

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
package adapter

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

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) 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 {
			fmt.Println("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.data <- 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(query string) error {
	return nil
}

func (self *MastoAdapter) Do(action string) error {
	return nil
}

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

func (self *MastoAdapter) mastoUpdateToMessage(status madon.Status) *Message {
	var parent *madon.Status

	if status.InReplyToID != nil {
		parent, _ = self.masto.GetStatus(*status.InReplyToID)
	}
	msg := Message{
		Protocol: "mastodon",
		Content:  status.Content,
		Uri:      status.URI,
		Author: Author{
			Id:   fmt.Sprintf("%d", status.Account.ID),
			Name: status.Account.Username,
			// TODO: we can add the fields to the profiledata as well
			ProfileData: status.Account.Note,
			ProfileUri:  status.Account.URL,
			ProfilePic:  status.Account.Avatar,
		},
		Created: status.CreatedAt,
	}
	if parent != nil {
		msg.ReplyTo = &parent.URI
	}
	// TODO: mentions and replies
	msg.Aux = make(map[string]string)
	msg.Aux["visibility"] = status.Visibility
	return &msg
}