Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

Viewing file on branch master

1package nctalk
2
3import (
4 "context"
5 "crypto/tls"
6 "strconv"
7 "strings"
8
9 "github.com/matterbridge-org/matterbridge/bridge"
10 "github.com/matterbridge-org/matterbridge/bridge/config"
11
12 "gomod.garykim.dev/nc-talk/ocs"
13 "gomod.garykim.dev/nc-talk/room"
14 "gomod.garykim.dev/nc-talk/user"
15)
16
17type Btalk struct {
18 user *user.TalkUser
19 rooms []Broom
20 *bridge.Config
21}
22
23func New(cfg *bridge.Config) bridge.Bridger {
24 return &Btalk{Config: cfg}
25}
26
27type Broom struct {
28 room *room.TalkRoom
29 ctx context.Context
30 ctxCancel context.CancelFunc
31}
32
33func (b *Btalk) Connect() error {
34 b.Log.Info("Connecting")
35 tconfig := &user.TalkUserConfig{
36 TLSConfig: &tls.Config{
37 InsecureSkipVerify: b.GetBool("SkipTLSVerify"), //nolint:gosec
38 },
39 }
40 var err error
41 b.user, err = user.NewUser(b.GetString("Server"), b.GetString("Login"), b.GetString("Password"), tconfig)
42 if err != nil {
43 b.Log.Error("Config could not be used")
44 return err
45 }
46 _, err = b.user.Capabilities()
47 if err != nil {
48 b.Log.Error("Cannot Connect")
49 return err
50 }
51 b.Log.Info("Connected")
52 return nil
53}
54
55func (b *Btalk) Disconnect() error {
56 for _, r := range b.rooms {
57 r.ctxCancel()
58 }
59 return nil
60}
61
62func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
63 tr, err := room.NewTalkRoom(b.user, channel.Name)
64 if err != nil {
65 return err
66 }
67 newRoom := Broom{
68 room: tr,
69 }
70 newRoom.ctx, newRoom.ctxCancel = context.WithCancel(context.Background())
71 c, err := newRoom.room.ReceiveMessages(newRoom.ctx)
72 if err != nil {
73 return err
74 }
75 b.rooms = append(b.rooms, newRoom)
76
77 go func() {
78 for msg := range c {
79 msg := msg
80
81 if msg.Error != nil {
82 b.Log.Errorf("Fatal message poll error: %s\n", msg.Error)
83
84 return
85 }
86
87 // Ignore messages that are from the bot user
88 if msg.ActorID == b.user.User || msg.ActorType == "bridged" {
89 continue
90 }
91
92 // Handle deleting messages
93 if msg.MessageType == ocs.MessageSystem && msg.Parent != nil && msg.Parent.MessageType == ocs.MessageDelete {
94 b.handleDeletingMessage(&msg, &newRoom)
95 continue
96 }
97
98 // Handle sending messages
99 if msg.MessageType == ocs.MessageComment {
100 b.handleSendingMessage(&msg, &newRoom)
101 continue
102 }
103
104 }
105 }()
106 return nil
107}
108
109func (b *Btalk) Send(msg config.Message) (string, error) {
110 r := b.getRoom(msg.Channel)
111 if r == nil {
112 b.Log.Errorf("Could not find room for %v", msg.Channel)
113 return "", nil
114 }
115
116 // Standard Message Send
117 if msg.Event == "" {
118 // Handle sending files if they are included
119 err := b.handleSendingFile(&msg, r)
120 if err != nil {
121 b.Log.Errorf("Could not send files in message to room %v from %v: %v", msg.Channel, msg.Username, err)
122
123 return "", nil
124 }
125
126 sentMessage, err := b.sendText(r, &msg, msg.Text)
127 if err != nil {
128 b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
129
130 return "", nil
131 }
132 return strconv.Itoa(sentMessage.ID), nil
133 }
134
135 // Message Deletion
136 if msg.Event == config.EventMsgDelete {
137 messageID, err := strconv.Atoi(msg.ID)
138 if err != nil {
139 return "", err
140 }
141 data, err := r.room.DeleteMessage(messageID)
142 if err != nil {
143 return "", err
144 }
145 return strconv.Itoa(data.ID), nil
146 }
147
148 // Message is not a type that is currently supported
149 return "", nil
150}
151
152func (b *Btalk) getRoom(token string) *Broom {
153 for _, r := range b.rooms {
154 if r.room.Token == token {
155 return &r
156 }
157 }
158 return nil
159}
160
161func (b *Btalk) sendText(r *Broom, msg *config.Message, text string) (*ocs.TalkRoomMessageData, error) {
162 messageToSend := &room.Message{Message: msg.Username + text}
163
164 if b.GetBool("SeparateDisplayName") {
165 messageToSend.Message = text
166 messageToSend.ActorDisplayName = msg.Username
167 }
168
169 return r.room.SendComplexMessage(messageToSend)
170}
171
172func (b *Btalk) handleFiles(mmsg *config.Message, message *ocs.TalkRoomMessageData) error {
173 for _, parameter := range message.MessageParameters {
174 if parameter.Type == ocs.ROSTypeFile {
175 // Get the file
176 file, err := b.user.DownloadFile(parameter.Path)
177 if err != nil {
178 return err
179 }
180
181 if mmsg.Extra == nil {
182 mmsg.Extra = make(map[string][]interface{})
183 }
184
185 mmsg.Extra["file"] = append(mmsg.Extra["file"], config.FileInfo{
186 Name: parameter.Name,
187 Data: file,
188 Size: int64(len(*file)),
189 Avatar: false,
190 })
191 }
192 }
193
194 return nil
195}
196
197func (b *Btalk) handleSendingFile(msg *config.Message, r *Broom) error {
198 for _, f := range msg.Extra["file"] {
199 fi := f.(config.FileInfo)
200 if fi.URL == "" {
201 continue
202 }
203
204 message := ""
205 if fi.Comment != "" {
206 message += fi.Comment + " "
207 }
208 message += fi.URL
209 _, err := b.sendText(r, msg, message)
210 if err != nil {
211 return err
212 }
213 }
214
215 return nil
216}
217
218func (b *Btalk) handleSendingMessage(msg *ocs.TalkRoomMessageData, r *Broom) {
219 remoteMessage := config.Message{
220 Text: formatRichObjectString(msg.Message, msg.MessageParameters),
221 Channel: r.room.Token,
222 Username: DisplayName(msg, b.guestSuffix()),
223 UserID: msg.ActorID,
224 Account: b.Account,
225 }
226 // It is possible for the ID to not be set on older versions of Talk so we only set it if
227 // the ID is not blank
228 if msg.ID != 0 {
229 remoteMessage.ID = strconv.Itoa(msg.ID)
230 }
231
232 // Handle Files
233 err := b.handleFiles(&remoteMessage, msg)
234 if err != nil {
235 b.Log.Errorf("Error handling file: %#v", msg)
236
237 return
238 }
239
240 b.Log.Debugf("<= Message is %#v", remoteMessage)
241 b.Remote <- remoteMessage
242}
243
244func (b *Btalk) handleDeletingMessage(msg *ocs.TalkRoomMessageData, r *Broom) {
245 remoteMessage := config.Message{
246 Event: config.EventMsgDelete,
247 Text: config.EventMsgDelete,
248 Channel: r.room.Token,
249 ID: strconv.Itoa(msg.Parent.ID),
250 Account: b.Account,
251 }
252 b.Log.Debugf("<= Message being deleted is %#v", remoteMessage)
253 b.Remote <- remoteMessage
254}
255
256func (b *Btalk) guestSuffix() string {
257 guestSuffix := " (Guest)"
258 if b.IsKeySet("GuestSuffix") {
259 guestSuffix = b.GetString("GuestSuffix")
260 }
261
262 return guestSuffix
263}
264
265// Spec: https://github.com/nextcloud/server/issues/1706#issue-182308785
266func formatRichObjectString(message string, parameters map[string]ocs.RichObjectString) string {
267 for id, parameter := range parameters {
268 text := parameter.Name
269
270 switch parameter.Type {
271 case ocs.ROSTypeUser, ocs.ROSTypeGroup:
272 text = "@" + text
273 case ocs.ROSTypeFile:
274 if parameter.Link != "" {
275 text = parameter.Name
276 }
277 }
278
279 message = strings.ReplaceAll(message, "{"+id+"}", text)
280 }
281
282 return message
283}
284
285func DisplayName(msg *ocs.TalkRoomMessageData, suffix string) string {
286 if msg.ActorType == ocs.ActorGuest {
287 if msg.ActorDisplayName == "" {
288 return "Guest"
289 }
290
291 return msg.ActorDisplayName + suffix
292 }
293
294 return msg.ActorDisplayName
295}
296