Ver código fonte

Allow starting threads from Matrix and fix some things

Tulir Asokan 3 anos atrás
pai
commit
5892f7049e
4 arquivos alterados com 119 adições e 20 exclusões
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 90 7
      portal.go
  4. 26 10
      user.go

+ 1 - 1
go.mod

@@ -27,4 +27,4 @@ require (
 	maunium.net/go/mauflag v1.0.0 // indirect
 )
 
-replace github.com/bwmarrin/discordgo => gitlab.com/beeper/discordgo v0.23.3-0.20220528185832-6fcb85e150f7
+replace github.com/bwmarrin/discordgo => gitlab.com/beeper/discordgo v0.23.3-0.20220528212118-5e6370d356e6

+ 2 - 2
go.sum

@@ -28,8 +28,8 @@ github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
 github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
 github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
 github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-gitlab.com/beeper/discordgo v0.23.3-0.20220528185832-6fcb85e150f7 h1:S8hbrkgKGU4aU5kXW4d8CA/9ayi8ymI3QU6yg/aWfUw=
-gitlab.com/beeper/discordgo v0.23.3-0.20220528185832-6fcb85e150f7/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
+gitlab.com/beeper/discordgo v0.23.3-0.20220528212118-5e6370d356e6 h1:JegmFzU6WlZ0vW28fBFkKaZbMgVE/laetJlQJO3wQsk=
+gitlab.com/beeper/discordgo v0.23.3-0.20220528212118-5e6370d356e6/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c=
 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=

+ 90 - 7
portal.go

@@ -465,9 +465,9 @@ func (portal *Portal) sendMediaFailedMessage(intent *appservice.IntentAPI, bridg
 		MsgType: event.MsgNotice,
 	}
 
-	_, err := portal.sendMatrixMessage(intent, event.EventMessage, content, nil, time.Now().UTC().UnixMilli())
+	_, err := portal.sendMatrixMessage(intent, event.EventMessage, content, nil, 0)
 	if err != nil {
-		portal.log.Warnfln("failed to send error message to matrix: %v", err)
+		portal.log.Warnfln("Failed to send media error message to matrix: %v", err)
 	}
 }
 
@@ -713,10 +713,14 @@ func (portal *Portal) handleDiscordMessageUpdate(user *User, msg *discordgo.Mess
 		Body:    msg.Content,
 		MsgType: event.MsgText,
 	}
-
 	content.SetEdit(existing.MXID)
 
-	_, err := portal.sendMatrixMessage(intent, event.EventMessage, content, nil, time.Now().UTC().UnixMilli())
+	var editTS int64
+	if msg.EditedTimestamp != nil {
+		editTS = msg.EditedTimestamp.UnixMilli()
+	}
+	// TODO figure out some way to deduplicate outgoing edits
+	_, err := portal.sendMatrixMessage(intent, event.EventMessage, content, nil, editTS)
 	if err != nil {
 		portal.log.Warnfln("failed to send message %q to matrix: %v", msg.ID, err)
 
@@ -846,6 +850,74 @@ func generateNonce() string {
 	return strconv.FormatInt(snowflake, 10)
 }
 
+func (portal *Portal) getEvent(mxid id.EventID) (*event.Event, error) {
+	evt, err := portal.MainIntent().GetEvent(portal.MXID, mxid)
+	if err != nil {
+		return nil, err
+	}
+	_ = evt.Content.ParseRaw(evt.Type)
+	if evt.Type == event.EventEncrypted {
+		decryptedEvt, err := portal.bridge.Crypto.Decrypt(evt)
+		if err != nil {
+			return nil, err
+		} else {
+			evt = decryptedEvt
+		}
+	}
+	return evt, nil
+}
+
+func genThreadName(evt *event.Event) string {
+	body := evt.Content.AsMessage().Body
+	if len(body) == 0 {
+		return "thread"
+	}
+	fields := strings.Fields(body)
+	var title string
+	for _, field := range fields {
+		if len(title)+len(field) < 40 {
+			title += field
+			title += " "
+			continue
+		}
+		if len(title) == 0 {
+			title = field[:40]
+		}
+		break
+	}
+	return title
+}
+
+func (portal *Portal) startThreadFromMatrix(sender *User, threadRoot id.EventID) (string, error) {
+	rootEvt, err := portal.getEvent(threadRoot)
+	if err != nil {
+		return "", fmt.Errorf("failed to get root event: %w", err)
+	}
+	threadName := genThreadName(rootEvt)
+
+	existingMsg := portal.bridge.DB.Message.GetByMXID(portal.Key, threadRoot)
+	if existingMsg == nil {
+		return "", fmt.Errorf("unknown root event")
+	} else if existingMsg.ThreadID != "" {
+		return "", fmt.Errorf("root event is already in a thread")
+	} else {
+		var ch *discordgo.Channel
+		ch, err = sender.Session.MessageThreadStartComplex(portal.Key.ChannelID, existingMsg.DiscordID, &discordgo.ThreadStart{
+			Name:                threadName,
+			AutoArchiveDuration: 24 * 60,
+			Type:                discordgo.ChannelTypeGuildPublicThread,
+			Location:            "Message",
+		})
+		if err != nil {
+			return "", fmt.Errorf("error starting thread: %v", err)
+		}
+		portal.log.Debugfln("Created Discord thread from %s/%s", threadRoot, ch.ID)
+		fmt.Printf("Created thread %+v\n", ch)
+		portal.bridge.GetThreadByID(existingMsg.DiscordID, existingMsg)
+		return ch.ID, nil
+	}
+}
+
 func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
 	if portal.IsPrivateChat() && sender.DiscordID != portal.Key.Receiver {
 		return
@@ -874,12 +946,18 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
 	} else if threadRoot := content.GetRelatesTo().GetThreadParent(); threadRoot != "" {
 		existingThread := portal.bridge.DB.Thread.GetByMatrixRootMsg(threadRoot)
 		if existingThread != nil {
-			channelID = existingThread.ID
 			threadID = existingThread.ID
 		} else {
-			// TODO create new thread
+			var err error
+			threadID, err = portal.startThreadFromMatrix(sender, threadRoot)
+			if err != nil {
+				portal.log.Warnfln("Failed to start thread from %s: %v", threadRoot, err)
+			}
 		}
 	}
+	if threadID != "" {
+		channelID = threadID
+	}
 
 	var sendReq discordgo.MessageSend
 
@@ -1190,8 +1268,13 @@ func (portal *Portal) handleDiscordReaction(user *User, reaction *discordgo.Mess
 			Key:     matrixReaction,
 		},
 	}}
+	if intent.IsCustomPuppet {
+		content.Raw = map[string]interface{}{
+			bridge.DoublePuppetKey: doublePuppetValue,
+		}
+	}
 
-	resp, err := intent.Client.SendMessageEvent(portal.MXID, event.EventReaction, &content)
+	resp, err := intent.SendMessageEvent(portal.MXID, event.EventReaction, &content)
 	if err != nil {
 		portal.log.Errorfln("failed to send reaction from %s: %v", reaction.MessageID, err)
 

+ 26 - 10
user.go

@@ -543,6 +543,20 @@ func (user *User) handlePrivateChannel(portal *Portal, meta *discordgo.Channel,
 	})
 }
 
+func (user *User) addGuildToSpace(guild *Guild) bool {
+	if len(guild.MXID) > 0 {
+		_, err := user.bridge.Bot.SendStateEvent(user.GetSpaceRoom(), event.StateSpaceChild, guild.MXID.String(), &event.SpaceChildEventContent{
+			Via: []string{user.bridge.AS.HomeserverDomain},
+		})
+		if err != nil {
+			user.log.Errorfln("Failed to add guild space %s to user space: %v", guild.MXID, err)
+		} else {
+			return true
+		}
+	}
+	return false
+}
+
 func (user *User) handleGuild(meta *discordgo.Guild, timestamp time.Time, isInSpace bool) {
 	guild := user.bridge.GetGuildByID(meta.ID, true)
 	guild.UpdateInfo(user, meta)
@@ -559,15 +573,8 @@ func (user *User) handleGuild(meta *discordgo.Guild, timestamp time.Time, isInSp
 			}
 		}
 	}
-	if len(guild.MXID) > 0 && !isInSpace {
-		_, err := user.bridge.Bot.SendStateEvent(user.GetSpaceRoom(), event.StateSpaceChild, guild.MXID.String(), &event.SpaceChildEventContent{
-			Via: []string{user.bridge.AS.HomeserverDomain},
-		})
-		if err != nil {
-			user.log.Errorfln("Failed to add guild space %s to user space: %v", guild.MXID, err)
-		} else {
-			isInSpace = true
-		}
+	if !isInSpace {
+		isInSpace = user.addGuildToSpace(guild)
 	}
 	user.MarkInPortal(database.UserPortal{
 		DiscordID: meta.ID,
@@ -640,7 +647,9 @@ func (user *User) channelUpdateHandler(_ *discordgo.Session, c *discordgo.Channe
 }
 
 func (user *User) pushPortalMessage(msg interface{}, typeName, channelID, guildID string) {
-	fmt.Printf("%+v\n", msg)
+	if user.Session.LogLevel == discordgo.LogDebug {
+		fmt.Printf("%+v\n", msg)
+	}
 	if !user.bridgeMessage(guildID) {
 		return
 	}
@@ -812,6 +821,13 @@ func (user *User) bridgeGuild(guildID string, everything bool) error {
 	if err != nil {
 		return err
 	}
+	user.addGuildToSpace(guild)
+	user.MarkInPortal(database.UserPortal{
+		DiscordID: meta.ID,
+		Type:      database.UserPortalTypeGuild,
+		Timestamp: time.Now(),
+		InSpace:   true,
+	})
 	for _, ch := range meta.Channels {
 		portal := user.GetPortalByMeta(ch)
 		if (everything && channelIsBridgeable(ch)) || ch.Type == discordgo.ChannelTypeGuildCategory {