diff --git a/pkg/connector/client.go b/pkg/connector/client.go
index b65cdb4..b4a2099 100644
--- a/pkg/connector/client.go
+++ b/pkg/connector/client.go
@@ -25,12 +25,16 @@ import (
"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog"
"maunium.net/go/mautrix/bridgev2"
+ "maunium.net/go/mautrix/bridgev2/database"
+ "maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/bridgev2/status"
)
type DiscordClient struct {
- UserLogin *bridgev2.UserLogin
- Session *discordgo.Session
+ connector *DiscordConnector
+ usersFromReady map[string]*discordgo.User
+ UserLogin *bridgev2.UserLogin
+ Session *discordgo.Session
}
func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.UserLogin) error {
@@ -54,6 +58,7 @@ func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.Us
session.EventHandler = func(evt any) {}
login.Client = &DiscordClient{
+ connector: d,
UserLogin: login,
Session: session,
}
@@ -126,6 +131,15 @@ func (cl *DiscordClient) connect(ctx context.Context) error {
}
}
+ // Stash all of the users we received in READY so we can perform quick lookups
+ // keyed by user ID.
+ cl.usersFromReady = make(map[string]*discordgo.User)
+ for _, user := range cl.Session.State.Ready.Users {
+ cl.usersFromReady[user.ID] = user
+ }
+
+ go cl.syncChannels(ctx)
+
return nil
}
@@ -143,3 +157,46 @@ func (d *DiscordClient) LogoutRemote(ctx context.Context) {
// FIXME(skip): Implement.
d.Disconnect()
}
+
+func (d *DiscordClient) syncChannels(ctx context.Context) {
+ for _, dm := range d.Session.State.PrivateChannels {
+ d.UserLogin.Log.Debug().Str("channel_id", dm.ID).Msg("Syncing private channel")
+ d.syncChannel(ctx, dm)
+ }
+}
+
+func (d *DiscordClient) syncChannel(ctx context.Context, ch *discordgo.Channel) {
+ isGroup := len(ch.RecipientIDs) > 1
+
+ var roomType database.RoomType
+ if isGroup {
+ roomType = database.RoomTypeGroupDM
+ } else {
+ roomType = database.RoomTypeDM
+ }
+
+ var members bridgev2.ChatMemberList
+ members.IsFull = true
+ members.MemberMap = make(bridgev2.ChatMemberMap, len(ch.Recipients))
+ if len(ch.Recipients) > 0 {
+ for _, recipient := range ch.Recipients {
+ sender := bridgev2.EventSender{
+ IsFromMe: recipient.ID == d.Session.State.User.ID,
+ SenderLogin: d.UserLogin.ID,
+ Sender: networkid.UserID(recipient.ID),
+ }
+ members.MemberMap[sender.Sender] = bridgev2.ChatMember{EventSender: sender}
+ }
+ members.TotalMemberCount = len(ch.Recipients)
+ }
+
+ d.connector.bridge.QueueRemoteEvent(d.UserLogin, &DiscordChatResync{
+ channel: ch,
+ portalKey: d.makePortalKey(ch, d.UserLogin.ID, true),
+ info: &bridgev2.ChatInfo{
+ Name: &ch.Name,
+ Members: &members,
+ Type: &roomType,
+ },
+ })
+}
diff --git a/pkg/connector/events.go b/pkg/connector/events.go
new file mode 100644
index 0000000..ad4cca9
--- /dev/null
+++ b/pkg/connector/events.go
@@ -0,0 +1,65 @@
+// mautrix-discord - A Matrix-Discord puppeting bridge.
+// Copyright (C) 2024 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package connector
+
+import (
+ "context"
+
+ "github.com/bwmarrin/discordgo"
+ "github.com/rs/zerolog"
+ "maunium.net/go/mautrix/bridgev2"
+ "maunium.net/go/mautrix/bridgev2/networkid"
+)
+
+type DiscordChatResync struct {
+ channel *discordgo.Channel
+ portalKey networkid.PortalKey
+ info *bridgev2.ChatInfo
+}
+
+var (
+ _ bridgev2.RemoteChatResyncWithInfo = (*DiscordChatResync)(nil)
+ _ bridgev2.RemoteEventThatMayCreatePortal = (*DiscordChatResync)(nil)
+)
+
+func (d *DiscordChatResync) AddLogContext(c zerolog.Context) zerolog.Context {
+ c = c.Str("channel_id", d.channel.ID).Int("channel_type", int(d.channel.Type))
+ return c
+}
+
+func (d *DiscordChatResync) GetPortalKey() networkid.PortalKey {
+ return d.portalKey
+}
+
+func (d *DiscordChatResync) GetSender() bridgev2.EventSender {
+ return bridgev2.EventSender{}
+}
+
+func (d *DiscordChatResync) GetType() bridgev2.RemoteEventType {
+ return bridgev2.RemoteEventChatResync
+}
+
+func (d *DiscordChatResync) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) {
+ if d.info == nil {
+ return nil, nil
+ }
+ return d.info, nil
+}
+
+func (d *DiscordChatResync) ShouldCreatePortal() bool {
+ return true
+}
diff --git a/pkg/connector/id.go b/pkg/connector/id.go
new file mode 100644
index 0000000..7376061
--- /dev/null
+++ b/pkg/connector/id.go
@@ -0,0 +1,30 @@
+// mautrix-discord - A Matrix-Discord puppeting bridge.
+// Copyright (C) 2024 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package connector
+
+import (
+ "github.com/bwmarrin/discordgo"
+ "maunium.net/go/mautrix/bridgev2/networkid"
+)
+
+func (d *DiscordClient) makePortalKey(ch *discordgo.Channel, userLoginID networkid.UserLoginID, wantReceiver bool) (key networkid.PortalKey) {
+ key.ID = networkid.PortalID(ch.ID)
+ if wantReceiver {
+ key.Receiver = userLoginID
+ }
+ return
+}
diff --git a/pkg/connector/userinfo.go b/pkg/connector/userinfo.go
index bedb53f..bd09c1c 100644
--- a/pkg/connector/userinfo.go
+++ b/pkg/connector/userinfo.go
@@ -18,7 +18,10 @@ package connector
import (
"context"
+ "fmt"
+ "github.com/rs/zerolog"
+ "go.mau.fi/util/ptr"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/networkid"
)
@@ -30,6 +33,24 @@ func (d *DiscordClient) IsThisUser(ctx context.Context, userID networkid.UserID)
}
func (d *DiscordClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) {
- //TODO implement me
- panic("implement me")
+ log := zerolog.Ctx(ctx)
+
+ if ghost.ID == "" {
+ log.Warn().Msg("Tried to get user info for ghost with no ID")
+ return nil, nil
+ }
+
+ // FIXME(skip): This won't work for users in guilds.
+
+ user, ok := d.usersFromReady[string(ghost.ID)]
+ if !ok {
+ log.Error().Str("ghost_id", string(ghost.ID)).Msg("Couldn't find corresponding user from READY for ghost")
+ return nil, nil
+ }
+
+ return &bridgev2.UserInfo{
+ Identifiers: []string{fmt.Sprintf("discord:%s", user.ID)},
+ Name: ptr.Ptr(user.DisplayName()),
+ IsBot: &user.Bot,
+ }, nil
}