fix(m2d): implement MSC4144 avatar clearing algorithm

- Empty string "" -> undefined (Discord uses default avatar)
- Valid MXC URI -> convert to public URL
- Omitted/null -> keep member avatar
This commit is contained in:
Bea
2026-03-24 16:45:40 +00:00
committed by cadence
parent 07ec9832b2
commit 0b513b7ee0
2 changed files with 43 additions and 2 deletions

View File

@@ -557,10 +557,18 @@ async function eventToMessage(event, guild, channel, di) {
const member = await getMemberFromCacheOrHomeserver(event.room_id, event.sender, di?.api)
if (member.displayname) displayName = member.displayname
if (member.avatar_url) avatarURL = mxUtils.getPublicUrlForMxc(member.avatar_url)
// Override display name and avatar from MSC4144 per-message profile if present
// MSC4144: Override display name and avatar from per-message profile if present
const perMessageProfile = event.content["com.beeper.per_message_profile"]
if (perMessageProfile?.displayname) displayName = perMessageProfile.displayname
if (perMessageProfile?.avatar_url) avatarURL = mxUtils.getPublicUrlForMxc(perMessageProfile.avatar_url)
if (perMessageProfile && "avatar_url" in perMessageProfile) {
if (perMessageProfile.avatar_url === "") {
// empty string avatar_url clears the avatar (use default)
avatarURL = undefined
} else if (perMessageProfile.avatar_url) {
// omitted/null falls back to member avatar
avatarURL = mxUtils.getPublicUrlForMxc(perMessageProfile.avatar_url)
}
}
// If the display name is too long to be put into the webhook (80 characters is the maximum),
// put the excess characters into displayNameRunoff, later to be put at the top of the message
let [displayNameShortened, displayNameRunoff] = splitDisplayName(displayName)

View File

@@ -5559,6 +5559,39 @@ test("event2message: com.beeper.per_message_profile overrides displayname and av
)
})
test("event2message: com.beeper.per_message_profile empty avatar_url clears avatar", async t => {
t.deepEqual(
await eventToMessage({
type: "m.room.message",
sender: "@cadence:cadence.moe",
content: {
msgtype: "m.text",
body: "hello with cleared avatar",
"com.beeper.per_message_profile": {
id: "no-avatar",
displayname: "No Avatar User",
avatar_url: ""
}
},
event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU",
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
}),
{
ensureJoined: [],
messagesToDelete: [],
messagesToEdit: [],
messagesToSend: [{
username: "No Avatar User",
content: "hello with cleared avatar",
avatar_url: undefined,
allowed_mentions: {
parse: ["users", "roles"]
}
}]
}
)
})
test("event2message: data-mx-profile-fallback element is stripped from formatted_body when per-message profile is present", async t => {
t.deepEqual(
await eventToMessage({