Add options to improve handling of webhook messages sent by other bridges

This commit is contained in:
Tulir Asokan
2023-05-27 13:01:24 +03:00
parent 824dea4745
commit 4393772ccc
6 changed files with 58 additions and 4 deletions

View File

@@ -51,6 +51,8 @@ type BridgeConfig struct {
DeletePortalOnChannelDelete bool `yaml:"delete_portal_on_channel_delete"`
DeleteGuildOnLeave bool `yaml:"delete_guild_on_leave"`
FederateRooms bool `yaml:"federate_rooms"`
PrefixWebhookMessages bool `yaml:"prefix_webhook_messages"`
EnableWebhookAvatars bool `yaml:"enable_webhook_avatars"`
UseDiscordCDNUpload bool `yaml:"use_discord_cdn_upload"`
CacheMedia string `yaml:"cache_media"`
@@ -287,9 +289,14 @@ func (bc BridgeConfig) FormatUsername(userID string) string {
return buffer.String()
}
func (bc BridgeConfig) FormatDisplayname(user *discordgo.User) string {
type DisplaynameParams struct {
*discordgo.User
Webhook bool
}
func (bc BridgeConfig) FormatDisplayname(user *discordgo.User, webhook bool) string {
var buffer strings.Builder
_ = bc.displaynameTemplate.Execute(&buffer, user)
_ = bc.displaynameTemplate.Execute(&buffer, &DisplaynameParams{user, webhook})
return buffer.String()
}

View File

@@ -55,6 +55,8 @@ func DoUpgrade(helper *up.Helper) {
helper.Copy(up.Bool, "bridge", "delete_portal_on_channel_delete")
helper.Copy(up.Bool, "bridge", "delete_guild_on_leave")
helper.Copy(up.Bool, "bridge", "federate_rooms")
helper.Copy(up.Bool, "bridge", "prefix_webhook_messages")
helper.Copy(up.Bool, "bridge", "enable_webhook_avatars")
helper.Copy(up.Bool, "bridge", "use_discord_cdn_upload")
helper.Copy(up.Bool, "bridge", "media_patterns", "enabled")
helper.Copy(up.Str, "bridge", "cache_media")

View File

@@ -85,6 +85,7 @@ bridge:
# .Discriminator - The 4 numbers after the name on Discord
# .Bot - Whether the user is a bot
# .System - Whether the user is an official system user
# .Webhook - Whether the user is a webhook
displayname_template: '{{or .GlobalName .Username}}{{if .Bot}} (bot){{end}}'
# Displayname template for Discord channels (bridged as rooms, or spaces when type=4).
# Available variables:
@@ -146,6 +147,11 @@ bridge:
# Whether or not created rooms should have federation enabled.
# If false, created portal rooms will never be federated.
federate_rooms: true
# Prefix messages from webhooks with the profile info? This can be used along with a custom displayname_template
# to better handle webhooks that change their name all the time (like ones used by bridges).
prefix_webhook_messages: false
# Bridge webhook avatars?
enable_webhook_avatars: true
# Should the bridge upload media to the Discord CDN directly before sending the message when using a user token,
# like the official client does? The other option is sending the media in the message send request as a form part
# (which is always used by bots and webhooks).

View File

@@ -897,6 +897,9 @@ func (portal *Portal) handleDiscordMessageUpdate(user *User, msg *discordgo.Mess
Msg("Dropping non-text edit")
return
}
if msg.WebhookID != "" {
addWebhookMeta(converted, msg)
}
converted.Content.Mentions = portal.convertDiscordMentions(msg, "", false)
converted.Content.SetEdit(existing[0].MXID)
// Never actually mention new users of edits, only include mentions inside m.new_content

View File

@@ -308,9 +308,33 @@ func (portal *Portal) convertDiscordMessage(ctx context.Context, intent *appserv
parts = append(parts, part)
}
}
if msg.WebhookID != "" {
for _, part := range parts {
addWebhookMeta(part, msg)
}
}
return parts
}
func addWebhookMeta(part *ConvertedMessage, msg *discordgo.Message) {
if msg.WebhookID == "" {
return
}
if part.Extra == nil {
part.Extra = make(map[string]any)
}
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(""),
}
part.Extra["com.beeper.per_message_profile"] = map[string]any{
"avatar_url": msg.Author.AvatarURL(""),
"displayname": msg.Author.Username,
}
}
const (
embedHTMLWrapper = `<blockquote class="discord-embed">%s</blockquote>`
embedHTMLWrapperColor = `<blockquote class="discord-embed" background-color="#%06X">%s</blockquote>`
@@ -628,5 +652,11 @@ func (portal *Portal) convertDiscordTextMessage(ctx context.Context, intent *app
"com.beeper.linkpreviews": previews,
}
if msg.WebhookID != "" && portal.bridge.Config.Bridge.PrefixWebhookMessages {
content.EnsureHasHTML()
content.Body = fmt.Sprintf("%s: %s", msg.Author.Username, content.Body)
content.FormattedBody = fmt.Sprintf("<strong>%s</strong>: %s", html.EscapeString(msg.Author.Username), content.FormattedBody)
}
return &ConvertedMessage{Type: event.EventMessage, Content: &content, Extra: extraContent}
}

View File

@@ -207,7 +207,7 @@ func (puppet *Puppet) updatePortalMeta(meta func(portal *Portal)) {
}
func (puppet *Puppet) UpdateName(info *discordgo.User) bool {
newName := puppet.bridge.Config.Bridge.FormatDisplayname(info)
newName := puppet.bridge.Config.Bridge.FormatDisplayname(info, puppet.IsWebhook)
if puppet.Name == newName && puppet.NameSet {
return false
}
@@ -229,6 +229,9 @@ func (puppet *Puppet) UpdateName(info *discordgo.User) bool {
}
func (puppet *Puppet) UpdateAvatar(info *discordgo.User) bool {
if puppet.IsWebhook && !puppet.bridge.Config.Bridge.EnableWebhookAvatars {
info.Avatar = ""
}
if puppet.Avatar == info.Avatar && puppet.AvatarSet {
return false
}
@@ -324,7 +327,7 @@ func (puppet *Puppet) UpdateContactInfo(info *discordgo.User) bool {
puppet.IsBot = info.Bot
changed = true
}
if changed {
if (changed && !puppet.IsWebhook) || !puppet.ContactInfoSet {
puppet.ContactInfoSet = false
puppet.ResendContactInfo()
return true
@@ -345,6 +348,9 @@ func (puppet *Puppet) ResendContactInfo() {
"com.beeper.bridge.network": puppet.bridge.BeeperNetworkName,
"com.beeper.bridge.is_network_bot": puppet.IsBot,
}
if puppet.IsWebhook {
contactInfo["com.beeper.bridge.identifiers"] = []string{}
}
err := puppet.DefaultIntent().BeeperUpdateProfile(contactInfo)
if err != nil {
puppet.log.Warn().Err(err).Msg("Failed to store custom contact info in profile")