Skip to content

Commit

Permalink
Allow passing impersonated device ID to Appservice and Intent
Browse files Browse the repository at this point in the history
When creating an impersonating client for an appservice, allow passing in a device ID to use. This will make the requests have `org.matrix.msc3202.device_id` in the query string for the client requests.

Appservice `intentsConfig` can be provided with a `botDeviceId`, which will be passed into creating the `Intent` for the bot user only.
  • Loading branch information
jaywink committed Jul 25, 2024
1 parent e521c33 commit 2a4f9ce
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 3 deletions.
19 changes: 16 additions & 3 deletions src/appservice/Appservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export interface IAppserviceOptions {
* Note that the appservice bot account is considered an intent.
*/
encryption?: boolean;

/**
* Device ID for the bot user.
*/
botDeviceId?: string;
};
}

Expand Down Expand Up @@ -362,12 +367,19 @@ export class Appservice extends EventEmitter {
return this.getUserId(this.registration.sender_localpart);
}

/**
* Get the application service's "bot" user device ID.
*/
public get botDeviceId(): string {
return this.options.intentOptions?.botDeviceId;
}

/**
* Get the application service's "bot" Intent (the sender_localpart).
* @returns {Intent} The intent for the application service itself.
*/
public get botIntent(): Intent {
return this.getIntentForUserId(this.botUserId);
return this.getIntentForUserId(this.botUserId, this.botDeviceId);
}

/**
Expand Down Expand Up @@ -457,12 +469,13 @@ export class Appservice extends EventEmitter {
/**
* Gets an Intent for a given user ID.
* @param {string} userId The user ID to get an Intent for.
* @param {string=} deviceId The user device ID to get an Intent for.
* @returns {Intent} An Intent for the user.
*/
public getIntentForUserId(userId: string): Intent {
public getIntentForUserId(userId: string, deviceId?: string): Intent {
let intent: Intent = this.intentsCache.get(userId);
if (!intent) {
intent = new Intent(this.options, userId, this);
intent = new Intent(this.options, userId, this, deviceId);
this.intentsCache.set(userId, intent);
this.emit("intent.new", intent);
if (this.options.intentOptions.encryption) {
Expand Down
7 changes: 7 additions & 0 deletions src/appservice/Intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ export class Intent {
return this.impersonateUserId;
}

/**
* Gets the device ID this intent is for, if any.
*/
public get deviceId(): string|undefined {
return this.impersonateDeviceId;
}

/**
* Gets the underlying MatrixClient that powers this Intent.
*/
Expand Down
35 changes: 35 additions & 0 deletions test/appservice/AppserviceTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,34 @@ describe('Appservice', () => {
const intent = appservice.botIntent;
expect(intent).toBeDefined();
expect(intent.userId).toEqual(appservice.botUserId);
expect(intent.deviceId).toBeUndefined();
});

it('should return an intent for the bot user with specific device', async () => {
const appservice = new Appservice({
port: 0,
bindAddress: '',
homeserverName: 'example.org',
homeserverUrl: 'https://localhost',
registration: {
as_token: "",
hs_token: "",
sender_localpart: "_bot_",
namespaces: {
users: [{ exclusive: true, regex: "@_prefix_.*:.+" }],
rooms: [],
aliases: [],
},
},
intentOptions: {
botDeviceId: 'DEVICE',
},
});

const intent = appservice.botIntent;
expect(intent).toBeDefined();
expect(intent.userId).toEqual(appservice.botUserId);
expect(intent.deviceId).toEqual(appservice.botDeviceId);
});

it('should return a client for the bot user', async () => {
Expand Down Expand Up @@ -318,6 +346,7 @@ describe('Appservice', () => {
const intent = appservice.getIntent("_prefix_testing");
expect(intent).toBeDefined();
expect(intent.userId).toEqual("@_prefix_testing:example.org");
expect(intent.deviceId).toBeUndefined();
});

it('should return an intent for any namespaced suffix', async () => {
Expand All @@ -341,6 +370,7 @@ describe('Appservice', () => {
const intent = appservice.getIntentForSuffix("testing");
expect(intent).toBeDefined();
expect(intent.userId).toEqual("@_prefix_testing:example.org");
expect(intent.deviceId).toBeUndefined();
});

it('should return an intent for any user ID', async () => {
Expand Down Expand Up @@ -368,21 +398,25 @@ describe('Appservice', () => {
intent = appservice.getIntentForUserId(userId);
expect(intent).toBeDefined();
expect(intent.userId).toEqual(userId);
expect(intent.deviceId).toBeUndefined();

userId = "@_prefix_testing:example.org";
intent = appservice.getIntentForUserId(userId);
expect(intent).toBeDefined();
expect(intent.userId).toEqual(userId);
expect(intent.deviceId).toBeUndefined();

userId = "@_bot_:example.org";
intent = appservice.getIntentForUserId(userId);
expect(intent).toBeDefined();
expect(intent.userId).toEqual(userId);
expect(intent.deviceId).toBeUndefined();

userId = "@test_prefix_:example.org";
intent = appservice.getIntentForUserId(userId);
expect(intent).toBeDefined();
expect(intent.userId).toEqual(userId);
expect(intent.deviceId).toBeUndefined();
});

it('should emit an event for a created intent', async () => {
Expand Down Expand Up @@ -422,6 +456,7 @@ describe('Appservice', () => {
expect(intentSpy.callCount).toBe(index+1);
expect(intent).toBeDefined();
expect(intent.userId).toEqual(userId);
expect(intent.deviceId).toBeUndefined();
expect(intent).toBe(newIntent);
});
});
Expand Down

0 comments on commit 2a4f9ce

Please sign in to comment.