Matrix notifications failed for anyone who entered a room alias. The send
endpoint only accepts a room ID, and an unresolved alias broke the request
outright: the leading "#" opens a URI fragment, so the PUT landed on
/_matrix/client/v3/rooms/ instead of the send endpoint. Element shows users
the alias, so this is the value most people paste in.
- Resolve "#alias:server" through GET /_matrix/client/v3/directory/room/{alias}
before sending, per the client-server spec
- Percent-encode the room ID in the send path; ":" was previously sent raw
- Strip trailing slashes from the homeserver URL to avoid "//_matrix/..."
Alias resolution failures return false and log, matching how the provider
already handles send failures.
Closes#3478
sendEmail caught transport failures internally and resolved to false or
undefined, so every caller's .catch() was dead code and send failures were
silently suppressed. Password recovery in particular reported "email sent
successfully" while the user received nothing.
sendEmail now throws an AppError carrying the underlying cause, and returns
Promise<string> instead of Promise<string | false | undefined>.
Call sites updated for the new contract:
- UserService.registerUser: the existing .catch() now actually fires
- UserService.requestRecovery: the failure reaches the client instead of a
misleading success response
- InviteService and the test-email endpoint: keep their user-facing messages
and attach the SMTP cause
- EmailProvider: catches and returns false, because NotificationsService fans
providers out through Promise.all and a rejection there would abort the
other channels' alerts
Closes#3757