|
@@ -2,9 +2,12 @@ package bridge
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
+ "sync"
|
|
|
|
|
|
- log "maunium.net/go/maulogger/v2"
|
|
|
+ "github.com/bwmarrin/discordgo"
|
|
|
|
|
|
+ log "maunium.net/go/maulogger/v2"
|
|
|
+ "maunium.net/go/mautrix"
|
|
|
"maunium.net/go/mautrix/appservice"
|
|
|
"maunium.net/go/mautrix/event"
|
|
|
"maunium.net/go/mautrix/id"
|
|
@@ -12,7 +15,12 @@ import (
|
|
|
"gitlab.com/beeper/discord/database"
|
|
|
)
|
|
|
|
|
|
-type PortalMatrixMessage struct {
|
|
|
+type portalDiscordMessage struct {
|
|
|
+ msg interface{}
|
|
|
+ user *User
|
|
|
+}
|
|
|
+
|
|
|
+type portalMatrixMessage struct {
|
|
|
evt *event.Event
|
|
|
user *User
|
|
|
}
|
|
@@ -23,9 +31,18 @@ type Portal struct {
|
|
|
bridge *Bridge
|
|
|
log log.Logger
|
|
|
|
|
|
- matrixMessages chan PortalMatrixMessage
|
|
|
+ channelType discordgo.ChannelType
|
|
|
+
|
|
|
+ roomCreateLock sync.Mutex
|
|
|
+
|
|
|
+ discordMessages chan portalDiscordMessage
|
|
|
+ matrixMessages chan portalMatrixMessage
|
|
|
}
|
|
|
|
|
|
+var (
|
|
|
+ portalCreationDummyEvent = event.Type{Type: "fi.mau.dummy.portal_created", Class: event.MessageEventType}
|
|
|
+)
|
|
|
+
|
|
|
func (b *Bridge) loadPortal(dbPortal *database.Portal, key *database.PortalKey) *Portal {
|
|
|
// If we weren't given a portal we'll attempt to create it if a key was
|
|
|
// provided.
|
|
@@ -63,13 +80,26 @@ func (b *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal {
|
|
|
return portal
|
|
|
}
|
|
|
|
|
|
+func (b *Bridge) GetPortalByID(key database.PortalKey) *Portal {
|
|
|
+ b.portalsLock.Lock()
|
|
|
+ defer b.portalsLock.Unlock()
|
|
|
+
|
|
|
+ portal, ok := b.portalsByID[key]
|
|
|
+ if !ok {
|
|
|
+ return b.loadPortal(b.db.Portal.GetByID(key), &key)
|
|
|
+ }
|
|
|
+
|
|
|
+ return portal
|
|
|
+}
|
|
|
+
|
|
|
func (b *Bridge) NewPortal(dbPortal *database.Portal) *Portal {
|
|
|
portal := &Portal{
|
|
|
Portal: dbPortal,
|
|
|
bridge: b,
|
|
|
log: b.log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
|
|
|
|
|
|
- matrixMessages: make(chan PortalMatrixMessage, b.config.Bridge.PortalMessageBuffer),
|
|
|
+ discordMessages: make(chan portalDiscordMessage, b.Config.Bridge.PortalMessageBuffer),
|
|
|
+ matrixMessages: make(chan portalMatrixMessage, b.Config.Bridge.PortalMessageBuffer),
|
|
|
}
|
|
|
|
|
|
go portal.messageLoop()
|
|
@@ -91,13 +121,15 @@ func (p *Portal) messageLoop() {
|
|
|
for {
|
|
|
select {
|
|
|
case msg := <-p.matrixMessages:
|
|
|
- p.log.Infoln("got message", msg)
|
|
|
+ p.log.Infoln("got matrix message", msg)
|
|
|
+ case msg := <-p.discordMessages:
|
|
|
+ p.handleDiscordMessage(msg)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (p *Portal) IsPrivateChat() bool {
|
|
|
- return false
|
|
|
+ return (p.channelType == discordgo.ChannelTypeDM || p.channelType == discordgo.ChannelTypeGroupDM)
|
|
|
}
|
|
|
|
|
|
func (p *Portal) MainIntent() *appservice.IntentAPI {
|
|
@@ -107,3 +139,142 @@ func (p *Portal) MainIntent() *appservice.IntentAPI {
|
|
|
|
|
|
return p.bridge.bot
|
|
|
}
|
|
|
+
|
|
|
+func (p *Portal) createMatrixRoom(user *User, channel *discordgo.Channel) error {
|
|
|
+ p.roomCreateLock.Lock()
|
|
|
+ defer p.roomCreateLock.Unlock()
|
|
|
+
|
|
|
+ // If we have a matrix id the room should exist so we have nothing to do.
|
|
|
+ if p.MXID != "" {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ p.channelType = channel.Type
|
|
|
+
|
|
|
+ intent := p.MainIntent()
|
|
|
+ if err := intent.EnsureRegistered(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if p.IsPrivateChat() {
|
|
|
+ puppet := p.bridge.GetPuppetByID(p.Key.ID)
|
|
|
+ puppet.SyncContact(user)
|
|
|
+
|
|
|
+ p.Name = puppet.DisplayName
|
|
|
+ p.Avatar = puppet.Avatar
|
|
|
+ p.AvatarURL = puppet.AvatarURL
|
|
|
+ }
|
|
|
+
|
|
|
+ p.log.Infoln("Creating Matrix room. Info source:", p.Portal.Key.ID)
|
|
|
+
|
|
|
+ initialState := []*event.Event{}
|
|
|
+
|
|
|
+ creationContent := make(map[string]interface{})
|
|
|
+ // if !portal.bridge.Config.Bridge.FederateRooms {
|
|
|
+ creationContent["m.federate"] = false
|
|
|
+ // }
|
|
|
+
|
|
|
+ var invite []id.UserID
|
|
|
+
|
|
|
+ if p.IsPrivateChat() {
|
|
|
+ invite = append(invite, p.bridge.bot.UserID)
|
|
|
+ }
|
|
|
+
|
|
|
+ resp, err := intent.CreateRoom(&mautrix.ReqCreateRoom{
|
|
|
+ Visibility: "private",
|
|
|
+ Name: p.Name,
|
|
|
+ Topic: p.Topic,
|
|
|
+ Preset: "private_chat",
|
|
|
+ IsDirect: p.IsPrivateChat(),
|
|
|
+ InitialState: initialState,
|
|
|
+ CreationContent: creationContent,
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ p.MXID = resp.RoomID
|
|
|
+ p.Update()
|
|
|
+ p.bridge.portalsLock.Lock()
|
|
|
+ p.bridge.portalsByMXID[p.MXID] = p
|
|
|
+ p.bridge.portalsLock.Unlock()
|
|
|
+
|
|
|
+ p.ensureUserInvited(user)
|
|
|
+
|
|
|
+ // if p.IsPrivateChat() {
|
|
|
+ // puppet := user.bridge.GetPuppetByID(p.Key.ID)
|
|
|
+
|
|
|
+ // if p.bridge.Config.Bridge.Encryption.Default {
|
|
|
+ // err = portal.bridge.Bot.EnsureJoined(portal.MXID)
|
|
|
+ // if err != nil {
|
|
|
+ // portal.log.Errorln("Failed to join created portal with bridge bot for e2be:", err)
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // user.UpdateDirectChats(map[id.UserID][]id.RoomID{puppet.MXID: {portal.MXID}})
|
|
|
+ // }
|
|
|
+
|
|
|
+ firstEventResp, err := p.MainIntent().SendMessageEvent(p.MXID, portalCreationDummyEvent, struct{}{})
|
|
|
+ if err != nil {
|
|
|
+ p.log.Errorln("Failed to send dummy event to mark portal creation:", err)
|
|
|
+ } else {
|
|
|
+ p.FirstEventID = firstEventResp.EventID
|
|
|
+ p.Update()
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Portal) handleDiscordMessage(msg portalDiscordMessage) {
|
|
|
+ if p.MXID == "" {
|
|
|
+ p.log.Debugln("Creating Matrix room from incoming message")
|
|
|
+
|
|
|
+ discordMsg := msg.msg.(*discordgo.MessageCreate)
|
|
|
+ channel, err := msg.user.Session.Channel(discordMsg.ChannelID)
|
|
|
+ if err != nil {
|
|
|
+ p.log.Errorln("Failed to find channel for message:", err)
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := p.createMatrixRoom(msg.user, channel); err != nil {
|
|
|
+ p.log.Errorln("Failed to create portal room:", err)
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ switch msg.msg.(type) {
|
|
|
+ case *discordgo.MessageCreate:
|
|
|
+ p.handleMessage(msg.msg.(*discordgo.MessageCreate).Message)
|
|
|
+ default:
|
|
|
+ p.log.Warnln("unknown message type")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Portal) ensureUserInvited(user *User) bool {
|
|
|
+ return user.ensureInvited(p.MainIntent(), p.MXID, p.IsPrivateChat())
|
|
|
+}
|
|
|
+
|
|
|
+func (p *Portal) handleMessage(msg *discordgo.Message) {
|
|
|
+ if p.MXID == "" {
|
|
|
+ p.log.Warnln("handle message called without a valid portal")
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Check if we already got the message
|
|
|
+
|
|
|
+ p.log.Debugln("content", msg.Content)
|
|
|
+ p.log.Debugln("embeds", msg.Embeds)
|
|
|
+ p.log.Debugln("msg", msg)
|
|
|
+
|
|
|
+ content := &event.MessageEventContent{
|
|
|
+ Body: msg.Content,
|
|
|
+ MsgType: event.MsgText,
|
|
|
+ }
|
|
|
+
|
|
|
+ resp, err := p.MainIntent().SendMessageEvent(p.MXID, event.EventMessage, content)
|
|
|
+ p.log.Warnln("response:", resp)
|
|
|
+ p.log.Warnln("error:", err)
|
|
|
+}
|