Reupload webhook avatars to fill custom metadata

This commit is contained in:
Tulir Asokan
2023-05-27 13:35:37 +03:00
parent 4393772ccc
commit 52fa4da8b2
5 changed files with 58 additions and 71 deletions

View File

@@ -1,40 +0,0 @@
package main
import (
"fmt"
"io"
"net/http"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/id"
"github.com/bwmarrin/discordgo"
)
func uploadAvatar(intent *appservice.IntentAPI, url string) (id.ContentURI, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return id.ContentURI{}, fmt.Errorf("failed to prepare request: %w", err)
}
for key, value := range discordgo.DroidImageHeaders {
req.Header.Set(key, value)
}
getResp, err := http.DefaultClient.Do(req)
if err != nil {
return id.ContentURI{}, fmt.Errorf("failed to download avatar: %w", err)
}
data, err := io.ReadAll(getResp.Body)
_ = getResp.Body.Close()
if err != nil {
return id.ContentURI{}, fmt.Errorf("failed to read avatar data: %w", err)
}
mime := http.DetectContentType(data)
resp, err := intent.UploadBytes(data, mime)
if err != nil {
return id.ContentURI{}, fmt.Errorf("failed to upload avatar to Matrix: %w", err)
}
return resp.ContentURI, nil
}

View File

@@ -272,12 +272,15 @@ func (guild *Guild) UpdateAvatar(iconID string) bool {
guild.Avatar = iconID
guild.AvatarURL = id.ContentURI{}
if guild.Avatar != "" {
var err error
guild.AvatarURL, err = uploadAvatar(guild.bridge.Bot, discordgo.EndpointGuildIcon(guild.ID, iconID))
// TODO direct media support
copied, err := guild.bridge.copyAttachmentToMatrix(guild.bridge.Bot, discordgo.EndpointGuildIcon(guild.ID, iconID), false, AttachmentMeta{
AttachmentID: fmt.Sprintf("guild_avatar/%s/%s", guild.ID, iconID),
})
if err != nil {
guild.log.Warnfln("Failed to reupload guild avatar %s: %v", guild.Avatar, err)
guild.log.Warnfln("Failed to reupload guild avatar %s: %v", iconID, err)
return true
}
guild.AvatarURL = copied.MXC
}
if guild.MXID != "" {
_, err := guild.bridge.Bot.SetRoomAvatar(guild.MXID, guild.AvatarURL)

View File

@@ -898,7 +898,7 @@ func (portal *Portal) handleDiscordMessageUpdate(user *User, msg *discordgo.Mess
return
}
if msg.WebhookID != "" {
addWebhookMeta(converted, msg)
portal.addWebhookMeta(converted, msg)
}
converted.Content.Mentions = portal.convertDiscordMentions(msg, "", false)
converted.Content.SetEdit(existing[0].MXID)
@@ -2152,13 +2152,15 @@ func (portal *Portal) UpdateGroupDMAvatar(iconID string) bool {
portal.AvatarSet = false
portal.AvatarURL = id.ContentURI{}
if portal.Avatar != "" {
uri, err := uploadAvatar(portal.MainIntent(), discordgo.EndpointGroupIcon(portal.Key.ChannelID, portal.Avatar))
// TODO direct media support
copied, err := portal.bridge.copyAttachmentToMatrix(portal.MainIntent(), discordgo.EndpointGroupIcon(portal.Key.ChannelID, portal.Avatar), false, AttachmentMeta{
AttachmentID: fmt.Sprintf("private_channel_avatar/%s/%s", portal.Key.ChannelID, iconID),
})
if err != nil {
portal.log.Err(err).Str("avatar_id", portal.Avatar).Msg("Failed to reupload channel avatar")
portal.log.Err(err).Str("avatar_id", iconID).Msg("Failed to reupload channel avatar")
return true
} else {
portal.AvatarURL = uri
}
portal.AvatarURL = copied.MXC
}
portal.updateRoomAvatar()
return true

View File

@@ -310,27 +310,38 @@ func (portal *Portal) convertDiscordMessage(ctx context.Context, intent *appserv
}
if msg.WebhookID != "" {
for _, part := range parts {
addWebhookMeta(part, msg)
portal.addWebhookMeta(part, msg)
}
}
return parts
}
func addWebhookMeta(part *ConvertedMessage, msg *discordgo.Message) {
func (portal *Portal) addWebhookMeta(part *ConvertedMessage, msg *discordgo.Message) {
if msg.WebhookID == "" {
return
}
if part.Extra == nil {
part.Extra = make(map[string]any)
}
var avatarURL id.ContentURI
if msg.Author.Avatar != "" {
var err error
avatarURL, err = portal.bridge.reuploadUserAvatar(portal.MainIntent(), msg.Author.ID, msg.Author.Avatar)
if err != nil {
portal.log.Warn().Err(err).
Str("avatar_id", msg.Author.Avatar).
Msg("Failed to reupload webhook avatar")
}
}
part.Extra["fi.mau.discord.webhook_metadata"] = map[string]any{
"id": msg.WebhookID,
"name": msg.Author.Username,
"avatar_id": msg.Author.Avatar,
"avatar_url": msg.Author.AvatarURL(""),
"avatar_mxc": avatarURL.String(),
}
part.Extra["com.beeper.per_message_profile"] = map[string]any{
"avatar_url": msg.Author.AvatarURL(""),
"avatar_url": avatarURL.String(),
"displayname": msg.Author.Username,
}
}

View File

@@ -228,34 +228,45 @@ func (puppet *Puppet) UpdateName(info *discordgo.User) bool {
return true
}
func (puppet *Puppet) UpdateAvatar(info *discordgo.User) bool {
if puppet.IsWebhook && !puppet.bridge.Config.Bridge.EnableWebhookAvatars {
info.Avatar = ""
func (br *DiscordBridge) reuploadUserAvatar(intent *appservice.IntentAPI, userID, avatarID string) (id.ContentURI, error) {
downloadURL := discordgo.EndpointUserAvatar(userID, avatarID)
ext := "png"
if strings.HasPrefix(avatarID, "a_") {
downloadURL = discordgo.EndpointUserAvatarAnimated(userID, avatarID)
ext = "gif"
}
if puppet.Avatar == info.Avatar && puppet.AvatarSet {
url := br.Config.Bridge.MediaPatterns.Avatar(userID, avatarID, ext)
if !url.IsEmpty() {
return url, nil
}
copied, err := br.copyAttachmentToMatrix(intent, downloadURL, false, AttachmentMeta{
AttachmentID: fmt.Sprintf("avatar/%s/%s", userID, avatarID),
})
if err != nil {
return url, err
}
return copied.MXC, nil
}
func (puppet *Puppet) UpdateAvatar(info *discordgo.User) bool {
avatarID := info.Avatar
if puppet.IsWebhook && !puppet.bridge.Config.Bridge.EnableWebhookAvatars {
avatarID = ""
}
if puppet.Avatar == avatarID && puppet.AvatarSet {
return false
}
avatarChanged := info.Avatar != puppet.Avatar
puppet.Avatar = info.Avatar
avatarChanged := avatarID != puppet.Avatar
puppet.Avatar = avatarID
puppet.AvatarSet = false
puppet.AvatarURL = id.ContentURI{}
if puppet.Avatar != "" && (puppet.AvatarURL.IsEmpty() || avatarChanged) {
downloadURL := discordgo.EndpointUserAvatar(info.ID, info.Avatar)
ext := "png"
if strings.HasPrefix(info.Avatar, "a_") {
downloadURL = discordgo.EndpointUserAvatarAnimated(info.ID, info.Avatar)
ext = "gif"
}
url := puppet.bridge.Config.Bridge.MediaPatterns.Avatar(info.ID, info.Avatar, ext)
if url.IsEmpty() {
var err error
url, err = uploadAvatar(puppet.DefaultIntent(), downloadURL)
url, err := puppet.bridge.reuploadUserAvatar(puppet.DefaultIntent(), info.ID, puppet.Avatar)
if err != nil {
puppet.log.Warn().Err(err).Str("avatar_id", puppet.Avatar).Msg("Failed to reupload user avatar")
return true
}
}
puppet.AvatarURL = url
}