mirror of
https://github.com/mautrix/discord.git
synced 2026-07-06 22:33:18 -04:00
118 lines
3.5 KiB
Go
118 lines
3.5 KiB
Go
// mautrix-discord - A Matrix-Discord puppeting bridge.
|
|
// Copyright (C) 2026 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 <https://www.gnu.org/licenses/>.
|
|
|
|
package connector
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
"github.com/rs/zerolog"
|
|
"go.mau.fi/util/ptr"
|
|
"maunium.net/go/mautrix/bridgev2"
|
|
"maunium.net/go/mautrix/bridgev2/networkid"
|
|
"maunium.net/go/mautrix/bridgev2/status"
|
|
|
|
"go.mau.fi/mautrix-discord/pkg/discordid"
|
|
)
|
|
|
|
func readableRelationshipType(rel discordgo.RelationshipType) (desc string) {
|
|
desc = "unknown"
|
|
|
|
switch rel {
|
|
case discordgo.RelationshipBlocked:
|
|
desc = "blocked"
|
|
case discordgo.RelationshipFriend:
|
|
desc = "friend"
|
|
case discordgo.RelationshipIncomingFriendRequest:
|
|
desc = "recipient wants to be friends"
|
|
case discordgo.RelationshipOutgoingFriendRequest:
|
|
desc = "sender wants to be friends"
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// makeRemoteName computes an appropriate value for the RemoteName field on
|
|
// [bridgev2.UserLogin].
|
|
func makeRemoteName(u *discordgo.User) string {
|
|
return u.String()
|
|
}
|
|
|
|
// makeRemoteProfile creates a [status.makeRemoteProfile] from a
|
|
// [discordgo.User]. A [bridgev2.Ghost] may optionally be passed to provide an
|
|
// avatar.
|
|
func makeRemoteProfile(u *discordgo.User, ghost *bridgev2.Ghost) (p status.RemoteProfile) {
|
|
p.Phone = u.Phone
|
|
p.Email = u.Email
|
|
p.Username = u.String()
|
|
p.Name = u.GlobalName
|
|
if ghost != nil {
|
|
p.Avatar = ghost.AvatarMXC
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *DiscordClient) IsThisUser(ctx context.Context, userID networkid.UserID) bool {
|
|
// We define `UserID`s and `UserLoginID`s to be interchangeable, i.e. they map
|
|
// directly to Discord user IDs ("snowflakes"), so we can perform a direct comparison.
|
|
return userID == discordid.UserLoginIDToUserID(d.UserLogin.ID)
|
|
}
|
|
|
|
func (d *DiscordClient) makeUserAvatar(u *discordgo.User) *bridgev2.Avatar {
|
|
url := u.AvatarURL("256")
|
|
|
|
return &bridgev2.Avatar{
|
|
ID: discordid.MakeAvatarID(url),
|
|
Get: func(ctx context.Context) ([]byte, error) {
|
|
return httpGet(ctx, d.httpClient, url, "user avatar")
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *DiscordClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) {
|
|
if !d.IsLoggedIn() {
|
|
return nil, bridgev2.ErrNotLoggedIn
|
|
}
|
|
|
|
log := zerolog.Ctx(ctx)
|
|
|
|
if ghost.ID == "" {
|
|
log.Warn().Msg("Tried to get user info for ghost with no ID")
|
|
return nil, nil
|
|
}
|
|
|
|
discordUserID := discordid.ParseUserID(ghost.ID)
|
|
discordUser := d.userCache.Resolve(ctx, discordUserID)
|
|
if discordUser == nil {
|
|
log.Error().Str("discord_user_id", discordUserID).
|
|
Msg("Failed to resolve user")
|
|
return nil, nil
|
|
}
|
|
|
|
return d.getUserInfo(ctx, discordUser), nil
|
|
}
|
|
|
|
func (d *DiscordClient) getUserInfo(ctx context.Context, user *discordgo.User) *bridgev2.UserInfo {
|
|
return &bridgev2.UserInfo{
|
|
// FIXME clear this for webhooks (stash in ghost metadata)
|
|
Identifiers: []string{fmt.Sprintf("discord:%s", user.String())},
|
|
Name: ptr.Ptr(user.DisplayName()),
|
|
Avatar: d.makeUserAvatar(user),
|
|
IsBot: &user.Bot,
|
|
}
|
|
}
|