1
1
package irckit
2
2
3
3
import (
4
+ "encoding/binary"
4
5
"errors"
5
6
"fmt"
6
7
"sort"
@@ -9,6 +10,8 @@ import (
9
10
"time"
10
11
"unicode"
11
12
13
+ bolt "go.etcd.io/bbolt"
14
+
12
15
"github.com/42wim/matterircd/bridge"
13
16
"github.com/mattermost/mattermost-server/v6/model"
14
17
)
@@ -209,6 +212,174 @@ func login(u *User, toUser *User, args []string, service string) {
209
212
u .MsgUser (toUser , "login OK" )
210
213
}
211
214
215
+ func createSpoof (u * User , mmchannel * bridge.ChannelInfo ) func (string , string , ... int ) {
216
+ if strings .Contains (mmchannel .Name , "__" ) {
217
+ return func (nick string , msg string , maxlen ... int ) {
218
+ if usr , ok := u .Srv .HasUser (nick ); ok {
219
+ u .MsgSpoofUser (usr , u .Nick , msg )
220
+ } else {
221
+ logger .Errorf ("%s not found for replay msg" , nick )
222
+ }
223
+ }
224
+ }
225
+
226
+ channelName := mmchannel .Name
227
+
228
+ if mmchannel .TeamID != u .br .GetMe ().TeamID || u .v .GetBool (u .br .Protocol ()+ ".prefixmainteam" ) {
229
+ channelName = u .br .GetTeamName (mmchannel .TeamID ) + "/" + mmchannel .Name
230
+ }
231
+
232
+ u .syncChannel (mmchannel .ID , "#" + channelName )
233
+ ch := u .Srv .Channel (mmchannel .ID )
234
+
235
+ return ch .SpoofMessage
236
+ }
237
+
238
+ //nolint:funlen,gocognit,gocyclo,cyclop
239
+ func replay (u * User , toUser * User , args []string , service string ) {
240
+ if len (args ) == 0 || len (args ) > 2 {
241
+ u .MsgUser (toUser , "need REPLAY (#<channel>|<user>)" )
242
+ u .MsgUser (toUser , "e.g. REPLAY #bugs" )
243
+ return
244
+ }
245
+
246
+ channelName := args [0 ]
247
+ channelTeamID := u .br .GetMe ().TeamID
248
+ if len (args ) == 2 {
249
+ channelTeamID = args [1 ]
250
+ }
251
+ channelID := u .br .GetChannelID (channelName , channelTeamID )
252
+ brchannel , err := u .br .GetChannel (channelID )
253
+ if err != nil {
254
+ logger .Errorf ("%s not found" , channelName )
255
+ return
256
+ }
257
+
258
+ // exclude direct messages
259
+ spoof := createSpoof (u , brchannel )
260
+
261
+ since := u .br .GetLastViewedAt (brchannel .ID )
262
+ // ignore invalid/deleted/old channels
263
+ if since == 0 {
264
+ return
265
+ }
266
+
267
+ logSince := "server"
268
+ channame := brchannel .Name
269
+ if ! brchannel .DM {
270
+ channame = fmt .Sprintf ("#%s" , brchannel .Name )
271
+ }
272
+
273
+ // We used to stored last viewed at if present.
274
+ var lastViewedAt int64
275
+ key := brchannel .ID
276
+ err = u .lastViewedAtDB .View (func (tx * bolt.Tx ) error {
277
+ b := tx .Bucket ([]byte (u .User ))
278
+ if v := b .Get ([]byte (key )); v != nil {
279
+ lastViewedAt = int64 (binary .LittleEndian .Uint64 (v ))
280
+ }
281
+ return nil
282
+ })
283
+ if err != nil {
284
+ logger .Errorf ("something wrong with u.lastViewedAtDB.View for %s for channel %s (%s)" , u .Nick , channame , brchannel .ID )
285
+ lastViewedAt = since
286
+ }
287
+
288
+ // But only use the stored last viewed if it's later than what the server knows.
289
+ if lastViewedAt > since {
290
+ since = lastViewedAt + 1
291
+ logSince = "stored"
292
+ }
293
+
294
+ // post everything to the channel you haven't seen yet
295
+ postlist := u .br .GetPostsSince (brchannel .ID , since )
296
+ if postlist == nil {
297
+ // if the channel is not from the primary team id, we can't get posts
298
+ if brchannel .TeamID == u .br .GetMe ().TeamID {
299
+ logger .Errorf ("something wrong with getPostsSince for %s for channel %s (%s)" , u .Nick , channame , brchannel .ID )
300
+ }
301
+ return
302
+ }
303
+
304
+ showReplayHdr := true
305
+
306
+ mmPostList , _ := postlist .(* model.PostList )
307
+ if mmPostList == nil {
308
+ return
309
+ }
310
+ // traverse the order in reverse
311
+ for i := len (mmPostList .Order ) - 1 ; i >= 0 ; i -- {
312
+ p := mmPostList.Posts [mmPostList.Order [i ]]
313
+ if p .Type == model .PostTypeJoinLeave {
314
+ continue
315
+ }
316
+
317
+ if p .DeleteAt > p .CreateAt {
318
+ continue
319
+ }
320
+
321
+ // GetPostsSince will return older messages with reaction
322
+ // changes since LastViewedAt. This will be confusing as
323
+ // the user will think it's a duplicate, or a post out of
324
+ // order. Plus, we don't show reaction changes when
325
+ // relaying messages/logs so let's skip these.
326
+ if p .CreateAt < since {
327
+ continue
328
+ }
329
+
330
+ ts := time .Unix (0 , p .CreateAt * int64 (time .Millisecond ))
331
+
332
+ props := p .GetProps ()
333
+ botname , override := props ["override_username" ].(string )
334
+ user := u .br .GetUser (p .UserId )
335
+ nick := user .Nick
336
+ if override {
337
+ nick = botname
338
+ }
339
+
340
+ if p .Type == model .PostTypeAddToTeam || p .Type == model .PostTypeRemoveFromTeam {
341
+ nick = systemUser
342
+ }
343
+
344
+ for _ , post := range strings .Split (p .Message , "\n " ) {
345
+ if showReplayHdr {
346
+ date := ts .Format ("2006-01-02 15:04:05" )
347
+ if brchannel .DM {
348
+ spoof (nick , fmt .Sprintf ("\x02 Replaying msgs since %s\x0f (%s)" , date , logSince ))
349
+ } else {
350
+ spoof ("matterircd" , fmt .Sprintf ("\x02 Replaying msgs since %s\x0f (%s)" , date , logSince ))
351
+ }
352
+ logger .Infof ("Replaying msgs for %s for %s (%s) since %s (%s)" , u .Nick , channame , brchannel .ID , date , logSince )
353
+ showReplayHdr = false
354
+ }
355
+
356
+ if nick == systemUser {
357
+ post = "\x1d " + post + "\x1d "
358
+ }
359
+
360
+ replayMsg := fmt .Sprintf ("[%s] %s" , ts .Format ("15:04" ), post )
361
+ if (u .v .GetBool (u .br .Protocol ()+ ".prefixcontext" ) || u .v .GetBool (u .br .Protocol ()+ ".suffixcontext" )) && nick != systemUser {
362
+ threadMsgID := u .prefixContext (brchannel .ID , p .Id , p .RootId , "replay" )
363
+ replayMsg = u .formatContextMessage (ts .Format ("15:04" ), threadMsgID , post )
364
+ }
365
+ spoof (nick , replayMsg )
366
+ }
367
+
368
+ if len (p .FileIds ) == 0 {
369
+ continue
370
+ }
371
+
372
+ for _ , fname := range u .br .GetFileLinks (p .FileIds ) {
373
+ fileMsg := "\x1d download file - " + fname + "\x1d "
374
+ if u .v .GetBool (u .br .Protocol ()+ ".prefixcontext" ) || u .v .GetBool (u .br .Protocol ()+ ".suffixcontext" ) {
375
+ threadMsgID := u .prefixContext (brchannel .ID , p .Id , p .RootId , "replay_file" )
376
+ fileMsg = u .formatContextMessage (ts .Format ("15:04" ), threadMsgID , fileMsg )
377
+ }
378
+ spoof (nick , fileMsg )
379
+ }
380
+ }
381
+ }
382
+
212
383
//nolint:cyclop
213
384
func search (u * User , toUser * User , args []string , service string ) {
214
385
if service == "slack" {
@@ -472,6 +643,7 @@ var cmds = map[string]Command{
472
643
"lastsent" : {handler : lastsent , login : true , minParams : 0 , maxParams : 0 },
473
644
"logout" : {handler : logout , login : true , minParams : 0 , maxParams : 0 },
474
645
"login" : {handler : login , minParams : 2 , maxParams : 5 },
646
+ "replay" : {handler : replay , login : true , minParams : 1 , maxParams : 2 },
475
647
"search" : {handler : search , login : true , minParams : 1 , maxParams : - 1 },
476
648
"searchusers" : {handler : searchUsers , login : true , minParams : 1 , maxParams : - 1 },
477
649
"scrollback" : {handler : scrollback , login : true , minParams : 2 , maxParams : 2 },
0 commit comments