diff --git a/changelog.d/1019.bugfix b/changelog.d/1019.bugfix new file mode 100644 index 00000000..46a9366f --- /dev/null +++ b/changelog.d/1019.bugfix @@ -0,0 +1 @@ +Fix hookshot failing to handle incoming webhooks when it is unable to change a user's displayname. diff --git a/package.json b/package.json index a28129ee..a2fbc479 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "jira-client": "^8.2.2", "markdown-it": "^14.0.0", "matrix-appservice-bridge": "^9.0.1", - "matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@0.7.1-element.7", + "matrix-bot-sdk": "npm:@vector-im/matrix-bot-sdk@0.7.1-element.8", "matrix-widget-api": "^1.10.0", "micromatch": "^4.0.8", "mime": "^4.0.4", diff --git a/src/Connections/GenericHook.ts b/src/Connections/GenericHook.ts index 2f0e831f..77abb096 100644 --- a/src/Connections/GenericHook.ts +++ b/src/Connections/GenericHook.ts @@ -349,6 +349,9 @@ export class GenericHookConnection extends BaseConnection implements IConnection return; } await intent.ensureRegistered(); + if ((await intent.underlyingClient.getCapabilities())["m.set_displayname"]?.enabled === false) { + return; + } const expectedDisplayname = `${this.state.name} (Webhook)`; try { diff --git a/src/IntentUtils.ts b/src/IntentUtils.ts index 31d2b5ba..f5660ba7 100644 --- a/src/IntentUtils.ts +++ b/src/IntentUtils.ts @@ -38,23 +38,29 @@ export async function ensureUserIsInRoom(targetIntent: Intent, botClient: Matrix export async function getIntentForUser(user: {avatarUrl?: string, login: string}, as: Appservice, prefix?: string) { const domain = as.botUserId.split(":")[1]; const intent = as.getIntentForUserId(`@${prefix ?? ''}${user.login}:${domain}`); - const displayName = `${user.login}`; + const displayName = user.login; + const capabilites = await intent.underlyingClient.getCapabilities(); + + // Verify up-to-date profile let profile; await intent.ensureRegistered(); + if (capabilites["m.set_displayname"]?.enabled === false && capabilites["m.set_avatar_url"]?.enabled === false) { + // No ability to change profile. + return intent; + } try { profile = await intent.underlyingClient.getUserProfile(intent.userId); } catch (ex) { profile = {}; } - if (profile.displayname !== displayName) { + if (capabilites["m.set_displayname"]?.enabled !== false && profile.displayname !== displayName) { log.debug(`Updating ${intent.userId}'s displayname`); - log.info(`${intent.userId}'s profile is out of date`); await intent.underlyingClient.setDisplayName(displayName); } - if (!profile.avatar_url && user.avatarUrl) { + if (capabilites["m.set_avatar_url"]?.enabled !== false && !profile.avatar_url && user.avatarUrl) { log.debug(`Updating ${intent.userId}'s avatar`); const buffer = await axios.get(user.avatarUrl, { responseType: "arraybuffer", diff --git a/src/Managers/BotUsersManager.ts b/src/Managers/BotUsersManager.ts index 5927cd11..1a78b574 100644 --- a/src/Managers/BotUsersManager.ts +++ b/src/Managers/BotUsersManager.ts @@ -97,6 +97,9 @@ export default class BotUsersManager { * @returns Promise resolving when the user profile has been ensured. */ private async ensureProfile(botUser: BotUser): Promise { + const capabilites = await botUser.intent.underlyingClient.getCapabilities(); + const canSetDisplayname = capabilites["m.set_displayname"]?.enabled !== false; + const canSetAvatarUrl = capabilites["m.set_avatar_url"]?.enabled !== false; log.debug(`Ensuring profile for ${botUser.userId} is updated`); let profile: { @@ -111,7 +114,7 @@ export default class BotUsersManager { } // Update display name if necessary - if (botUser.displayname && profile.displayname !== botUser.displayname) { + if (canSetDisplayname && botUser.displayname && profile.displayname !== botUser.displayname) { try { await botUser.intent.underlyingClient.setDisplayName(botUser.displayname); log.info(`Updated displayname for "${botUser.userId}" to ${botUser.displayname}`); @@ -120,6 +123,10 @@ export default class BotUsersManager { } } + if (!canSetAvatarUrl) { + return; + } + if (!botUser.avatar) { // Unset any avatar if (profile.avatar_url) { diff --git a/tests/utils/IntentMock.ts b/tests/utils/IntentMock.ts index 078091b1..d93fc68a 100644 --- a/tests/utils/IntentMock.ts +++ b/tests/utils/IntentMock.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import { MatrixError } from "matrix-bot-sdk"; +import { MatrixCapabilities } from "matrix-bot-sdk/lib/models/Capabilities"; export class MatrixClientMock { static create(){ @@ -16,6 +17,17 @@ export class MatrixClientMock { return; } + async getCapabilities(): Promise { + return { + "m.set_displayname": { + enabled: true + }, + "m.set_avatar_url": { + enabled: true + }, + } + } + async getJoinedRoomMembers(roomId: string): Promise { return this.joinedMembers.get(roomId) || []; } diff --git a/yarn.lock b/yarn.lock index 4aad9947..70c6593e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7034,10 +7034,10 @@ matrix-appservice@^2.0.0: js-yaml "^4.1.0" morgan "^1.10.0" -"matrix-bot-sdk@npm:@vector-im/matrix-bot-sdk@0.7.1-element.7": - version "0.7.1-element.7" - resolved "https://registry.yarnpkg.com/@vector-im/matrix-bot-sdk/-/matrix-bot-sdk-0.7.1-element.7.tgz#577b5c0060af1c9a8b3eb6f4373f692d57b786ac" - integrity sha512-L9t22ODzSf/6D7H7yZ4ygTsuG00xvX4UcSNoz4AN+q2drEyYPNzpE5PjkxmsuSOMip/3bEf3G0oy/QVSvJ+pFA== +"matrix-bot-sdk@npm:@vector-im/matrix-bot-sdk@0.7.1-element.8": + version "0.7.1-element.8" + resolved "https://registry.yarnpkg.com/@vector-im/matrix-bot-sdk/-/matrix-bot-sdk-0.7.1-element.8.tgz#fffbbd7044834ba1e35778200203977b6ee6f07d" + integrity sha512-dvQpkeit+RhbyiSUgOVLwqCaS1rVi0ScWt5JbqM0NrOkR0xWaMUF+Lu8/PdsV7vRgfgh09mprBSjIFvHdoQgcg== dependencies: "@matrix-org/matrix-sdk-crypto-nodejs" "0.3.0-beta.1" "@types/express" "^4.17.21"