From 4519906d3206927a0bca6be2d2d7c78c42b0af55 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 15:57:27 +0100 Subject: [PATCH 1/6] Add uploadContent --- src/components/intent.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/intent.ts b/src/components/intent.ts index 9929eb53..f346b7ca 100644 --- a/src/components/intent.ts +++ b/src/components/intent.ts @@ -25,6 +25,7 @@ import { unstable } from "../errors"; import BridgeErrorReason = unstable.BridgeErrorReason; import { APPSERVICE_LOGIN_TYPE, ClientEncryptionSession } from "./encryption"; import Logging from "./logging"; +import { ReadStream } from "fs"; const log = Logging.get("Intent"); @@ -58,6 +59,12 @@ export interface RoomCreationOpts { options: Record; } +export interface FileUploadOpts { + name?: string; + includeFilename?: boolean; + type?: string; +} + /** * Returns the first parameter that is a number or 0. */ @@ -698,6 +705,15 @@ export class Intent { } /** + * Upload a file to the homeserver. + * @param content The file contents + * @param opts Additional options for the upload. + * @returns A MXC URL pointing to the uploaded data. + */ + public async uploadContent(content: Buffer|string|ReadStream, opts: FileUploadOpts = {}): Promise { + await this.ensureRegistered(); + return this.client.uploadContent(content, {...opts, onlyContentUri: true}); + } * Inform this Intent class of an incoming event. Various optimisations will be * done if this is provided. For example, a /join request won't be sent out if * it knows you've already been joined to the room. This function does nothing From 490c2b5babb16c5c821894c49881aad0b1c7bdbe Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 15:57:43 +0100 Subject: [PATCH 2/6] We don't need power for typing --- src/components/intent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/intent.ts b/src/components/intent.ts index f346b7ca..d68d7993 100644 --- a/src/components/intent.ts +++ b/src/components/intent.ts @@ -323,7 +323,6 @@ export class Intent { */ public async sendTyping(roomId: string, isTyping: boolean) { await this._ensureJoined(roomId); - await this._ensureHasPowerLevelFor(roomId, "m.typing"); return this.client.sendTyping(roomId, isTyping); } From d0a2d538dd8e83b7d39748939cf3fc4a8572412d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 15:57:59 +0100 Subject: [PATCH 3/6] Add room visibility functions --- src/components/intent.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/components/intent.ts b/src/components/intent.ts index d68d7993..0804e672 100644 --- a/src/components/intent.ts +++ b/src/components/intent.ts @@ -713,6 +713,31 @@ export class Intent { await this.ensureRegistered(); return this.client.uploadContent(content, {...opts, onlyContentUri: true}); } + + /** + * Set the visibility of a room in the homeserver's room directory. + * @param roomId The room + * @param visibility Should the room be visible + */ + public async setRoomDirectoryVisibility(roomId: string, visibility: "public"|"private") { + await this.ensureRegistered(); + return this.client.setRoomDirectoryVisibility(roomId, visibility); + } + + /** + * Set the visibility of a room in the appservice's room directory. + * This only works if you have defined the `protocol` in the registration file. + * @param roomId The room + * @param networkId The network (not protocol) that owns this room. E.g. "freenode" (for an IRC bridge) + * @param visibility Should the room be visible + */ + public async setRoomDirectoryVisibilityAppService(roomId: string, networkId: string, + visibility: "public"|"private") { + await this.ensureRegistered(); + return this.client.setRoomDirectoryVisibilityAppService(roomId, visibility, networkId); + } + + /** * Inform this Intent class of an incoming event. Various optimisations will be * done if this is provided. For example, a /join request won't be sent out if * it knows you've already been joined to the room. This function does nothing From 3d34130b575ffcbc9b15546d81806e4c68fb9a22 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 15:58:07 +0100 Subject: [PATCH 4/6] Extra: Allow you to either provide a client or a intent --- src/components/state-lookup.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/state-lookup.ts b/src/components/state-lookup.ts index b4cdaa71..43b205f5 100644 --- a/src/components/state-lookup.ts +++ b/src/components/state-lookup.ts @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import PQueue from "p-queue"; +import { Intent } from ".."; interface StateLookupOpts { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -48,7 +49,7 @@ const DEFAULT_STATE_CONCURRENCY = 4; export class StateLookup { // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _client: any; + private _client: any|Intent; private eventTypes: {[eventType: string]: boolean} = {}; private dict: { [roomId: string]: StateLookupRoom } = {}; private lookupQueue: PQueue; @@ -81,7 +82,7 @@ export class StateLookup { this.retryStateIn = opts.retryStateInMs || RETRY_STATE_IN_MS; - this._client = opts.client; + this._client = opts.client instanceof Intent ? opts.client.client : opts.client; (opts.eventTypes || []).forEach((t) => { this.eventTypes[t] = true; }); From 50fe5f6ac26d003d05353cde4305e61d9810975a Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 16:00:14 +0100 Subject: [PATCH 5/6] changelog --- changelog.d/254.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/254.feature diff --git a/changelog.d/254.feature b/changelog.d/254.feature new file mode 100644 index 00000000..f173a6dc --- /dev/null +++ b/changelog.d/254.feature @@ -0,0 +1 @@ +Add `uploadContent()`, and `setRoomDirectoryVisibility()` intent functions From 290fb953776c7fd2bf2a88c530c49f9c69a57d7b Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 20 Oct 2020 16:06:43 +0100 Subject: [PATCH 6/6] Don't guess at state-ness, use isState --- src/components/intent.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/intent.ts b/src/components/intent.ts index 0804e672..9108c610 100644 --- a/src/components/intent.ts +++ b/src/components/intent.ts @@ -77,10 +77,6 @@ const returnFirstNumber = (...args: unknown[]) => { return 0; } -const STATE_EVENT_TYPES = [ - "m.room.name", "m.room.topic", "m.room.power_levels", "m.room.member", - "m.room.join_rules", "m.room.history_visibility" -]; const DEFAULT_CACHE_TTL = 90000; const DEFAULT_CACHE_SIZE = 1024; @@ -350,7 +346,7 @@ export class Intent { */ public async setPowerLevel(roomId: string, target: string, level: number|undefined) { await this._ensureJoined(roomId); - const event = await this._ensureHasPowerLevelFor(roomId, "m.room.power_levels"); + const event = await this._ensureHasPowerLevelFor(roomId, "m.room.power_levels", true); return this.client.setPowerLevel(roomId, target, level, event); } @@ -384,7 +380,7 @@ export class Intent { await this.encryption.ensureClientSyncingCallback(); } await this._ensureJoined(roomId); - await this._ensureHasPowerLevelFor(roomId, type); + await this._ensureHasPowerLevelFor(roomId, type, false); return this._joinGuard(roomId, async() => // eslint-disable-next-line camelcase this.client.sendEvent(roomId, type, content) as Promise<{event_id: string}> @@ -405,7 +401,7 @@ export class Intent { // eslint-disable-next-line camelcase ): Promise<{event_id: string}> { await this._ensureJoined(roomId); - await this._ensureHasPowerLevelFor(roomId, type); + await this._ensureHasPowerLevelFor(roomId, type, true); return this._joinGuard(roomId, async() => // eslint-disable-next-line camelcase this.client.sendStateEvent(roomId, type, content, skey) as Promise<{event_id: string}> @@ -878,9 +874,10 @@ export class Intent { * Ensures that the client has the required power level to post the event type. * @param roomId Required as power levels exist inside a room. * @param eventTypes The event type to check the permissions for. + * @param isState Are we checking for state permissions or regular event permissions. * @return If found, the power level event */ - private async _ensureHasPowerLevelFor(roomId: string, eventType: string) { + private async _ensureHasPowerLevelFor(roomId: string, eventType: string, isState: boolean) { if (this.opts.dontCheckPowerLevel && eventType !== "m.room.power_levels") { return undefined; } @@ -899,7 +896,7 @@ export class Intent { } const powerLevelEvent = new MatrixEvent(event); // What level do we need for this event type? - const defaultLevel = STATE_EVENT_TYPES.includes(eventType) + const defaultLevel = isState ? event.content.state_default : event.content.events_default; const requiredLevel = returnFirstNumber(