first commit

This commit is contained in:
Myk
2025-07-31 23:47:20 +03:00
commit 2186b278a0
5149 changed files with 537218 additions and 0 deletions

22
node_modules/@vector-im/matrix-bot-sdk/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2022 - 2023 New Vector Ltd
Copyright (c) 2018 - 2023 Travis Ralston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

22
node_modules/@vector-im/matrix-bot-sdk/README.md generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# matrix-bot-sdk
[![npm version](https://badge.fury.io/js/@vector-im%2Fmatrix-bot-sdk.svg)](https://www.npmjs.com/package/@vector-im/matrix-bot-sdk)
TypeScript/JavaScript SDK for Matrix bots. For help and support, visit [#matrix-bot-sdk:t2bot.io](https://matrix.to/#/#matrix-bot-sdk:t2bot.io)
# Documentation
Documentation for the project is available [here](https://turt2live.github.io/matrix-bot-sdk/index.html).
# Matrix version support
The Matrix protocol is [versioned](https://spec.matrix.org/latest/#specification-versions) to ensure endpoints and
functionality can safely rotate in and out of the ecosystem. The bot-sdk will assume it is connected to a homeserver
with support for at least one of the last 2 versions, at the time of the bot-sdk's release. This means that if you
connect the bot-sdk to a homeserver which is 3 or more Matrix versions out of date, things might not work for you.
It is recommended to update the bot-sdk as frequently as spec releases themselves (or faster) to avoid this situation,
and watch the repo for updates in the event a release is delayed.
**Note**: Currently the bot-sdk does not throw an error if the server appears to be incompatible, however this might
change in the future.

View File

@@ -0,0 +1,52 @@
import { MatrixClient } from "./MatrixClient";
import { SynapseAdminApis } from "./SynapseAdminApis";
/**
* Whois information about a user.
* See https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-admin-whois-userid for more information.
* @category Admin APIs
*/
export interface WhoisInfo {
user_id: string;
devices: {
[device_id: string]: {
sessions: [
{
connections: WhoisConnectionInfo[];
}
];
};
};
}
interface WhoisConnectionInfo {
/**
* Most recently seen IP address of the session.
*/
ip: string;
/**
* Unix timestamp that the session was last active.
*/
last_seen: number;
/**
* User agent string last seen in the session.
*/
user_agent: string;
}
/**
* Access to various administrative APIs.
* @category Admin APIs
*/
export declare class AdminApis {
private client;
constructor(client: MatrixClient);
/**
* Gets access to the Synapse administrative APIs object.
*/
get synapse(): SynapseAdminApis;
/**
* Gets information about a particular user.
* @param {string} userId the user ID to lookup
* @returns {Promise<WhoisInfo>} resolves to the whois information
*/
whoisUser(userId: string): Promise<WhoisInfo>;
}
export {};

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminApis = void 0;
const SynapseAdminApis_1 = require("./SynapseAdminApis");
/**
* Access to various administrative APIs.
* @category Admin APIs
*/
class AdminApis {
client;
constructor(client) {
this.client = client;
}
/**
* Gets access to the Synapse administrative APIs object.
*/
get synapse() {
return new SynapseAdminApis_1.SynapseAdminApis(this.client);
}
/**
* Gets information about a particular user.
* @param {string} userId the user ID to lookup
* @returns {Promise<WhoisInfo>} resolves to the whois information
*/
whoisUser(userId) {
return this.client.doRequest("GET", "/_matrix/client/v3/admin/whois/" + encodeURIComponent(userId));
}
}
exports.AdminApis = AdminApis;
//# sourceMappingURL=AdminApis.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AdminApis.js","sourceRoot":"","sources":["../src/AdminApis.ts"],"names":[],"mappings":";;;AACA,yDAAsD;AAmCtD;;;GAGG;AACH,MAAa,SAAS;IACE;IAApB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IACxC,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QACd,OAAO,IAAI,mCAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,MAAc;QAC3B,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,iCAAiC,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxG,CAAC;CACJ;AAnBD,8BAmBC"}

48
node_modules/@vector-im/matrix-bot-sdk/lib/DMs.d.ts generated vendored Normal file
View File

@@ -0,0 +1,48 @@
import { MatrixClient } from "./MatrixClient";
/**
* Handles DM (direct messages) matching between users. Note that bots which
* existed prior to this might not have DM rooms populated correctly - the
* account data can be populated externally and that will be reflected here.
*
* Note that DM status is persisted across all access tokens for a user and
* is not persisted with the regular stores. The DM map is instead tracked
* on the homeserver as account data and thus survives the bot's own storage
* being wiped.
* @category Utilities
*/
export declare class DMs {
private client;
private cached;
private ready;
/**
* Creates a new DM map.
* @param {MatrixClient} client The client the DM map is for.
*/
constructor(client: MatrixClient);
private updateFromAccountData;
private handleInvite;
private persistCache;
private fixDms;
/**
* Forces an update of the DM cache.
* @returns {Promise<void>} Resolves when complete.
*/
update(): Promise<void>;
/**
* Gets or creates a DM with a given user. If a DM needs to be created, it will
* be created as an encrypted DM (if both the MatrixClient and target user support
* crypto). Otherwise, the createFn can be used to override the call. Note that
* when creating a DM room the room should have `is_direct: true` set.
* @param {string} userId The user ID to get/create a DM for.
* @param {Function} createFn Optional function to use to create the room. Resolves
* to the created room ID.
* @returns {Promise<string>} Resolves to the DM room ID.
*/
getOrCreateDm(userId: string, createFn?: (targetUserId: string) => Promise<string>): Promise<string>;
/**
* Determines if a given room is a DM according to the cache.
* @param {string} roomId The room ID.
* @returns {boolean} True if the room ID is a cached DM room ID.
*/
isDm(roomId: string): boolean;
}

162
node_modules/@vector-im/matrix-bot-sdk/lib/DMs.js generated vendored Normal file
View File

@@ -0,0 +1,162 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DMs = void 0;
const Crypto_1 = require("./models/Crypto");
const LogService_1 = require("./logging/LogService");
/**
* Handles DM (direct messages) matching between users. Note that bots which
* existed prior to this might not have DM rooms populated correctly - the
* account data can be populated externally and that will be reflected here.
*
* Note that DM status is persisted across all access tokens for a user and
* is not persisted with the regular stores. The DM map is instead tracked
* on the homeserver as account data and thus survives the bot's own storage
* being wiped.
* @category Utilities
*/
class DMs {
client;
cached = new Map();
ready;
/**
* Creates a new DM map.
* @param {MatrixClient} client The client the DM map is for.
*/
constructor(client) {
this.client = client;
this.client.on("account_data", (ev) => {
if (ev['type'] !== 'm.direct')
return;
// noinspection JSIgnoredPromiseFromCall
this.updateFromAccountData();
});
this.client.on("room.invite", (rid, ev) => this.handleInvite(rid, ev));
}
async updateFromAccountData() {
// Don't trust the sync update
let map = {};
try {
map = await this.client.getAccountData("m.direct");
}
catch (e) {
if (e.body?.errcode !== "M_NOT_FOUND" && e.statusCode !== 404) {
LogService_1.LogService.warn("DMs", "Error getting m.direct account data: ", e);
}
}
this.cached = new Map();
for (const [userId, roomIds] of Object.entries(map)) {
this.cached.set(userId, roomIds);
}
}
async handleInvite(roomId, ev) {
if (ev['content']?.['is_direct'] === true) {
const userId = ev['sender'];
if (!this.cached.has(userId))
this.cached.set(userId, []);
this.cached.set(userId, [roomId, ...this.cached.get(userId)]);
await this.persistCache();
}
}
async persistCache() {
const obj = {};
for (const [uid, rids] of this.cached.entries()) {
obj[uid] = rids;
}
await this.client.setAccountData("m.direct", obj);
}
async fixDms(userId) {
const currentRooms = this.cached.get(userId);
if (!currentRooms)
return;
const toKeep = [];
for (const roomId of currentRooms) {
try {
const members = await this.client.getAllRoomMembers(roomId);
const joined = members.filter(m => m.effectiveMembership === "join" || m.effectiveMembership === "invite");
if (joined.some(m => m.membershipFor === userId)) {
toKeep.push(roomId);
}
}
catch (e) {
LogService_1.LogService.warn("DMs", `Unable to check ${roomId} for room members - assuming invalid DM`);
}
}
if (toKeep.length === currentRooms.length)
return; // no change
if (toKeep.length > 0) {
this.cached.set(userId, toKeep);
}
else {
this.cached.delete(userId);
}
await this.persistCache();
}
/**
* Forces an update of the DM cache.
* @returns {Promise<void>} Resolves when complete.
*/
async update() {
await this.ready; // finish the existing call if present
this.ready = this.updateFromAccountData();
return this.ready;
}
/**
* Gets or creates a DM with a given user. If a DM needs to be created, it will
* be created as an encrypted DM (if both the MatrixClient and target user support
* crypto). Otherwise, the createFn can be used to override the call. Note that
* when creating a DM room the room should have `is_direct: true` set.
* @param {string} userId The user ID to get/create a DM for.
* @param {Function} createFn Optional function to use to create the room. Resolves
* to the created room ID.
* @returns {Promise<string>} Resolves to the DM room ID.
*/
async getOrCreateDm(userId, createFn) {
await this.ready;
await this.fixDms(userId);
const rooms = this.cached.get(userId);
if (rooms?.length)
return rooms[0];
let roomId;
if (createFn) {
roomId = await createFn(userId);
}
else {
let hasKeys = false;
if (!!this.client.crypto) {
const keys = await this.client.getUserDevices([userId]);
const userKeys = keys?.device_keys?.[userId] ?? {};
hasKeys = Object.values(userKeys).filter(device => Object.values(device).length > 0).length > 0;
}
roomId = await this.client.createRoom({
invite: [userId],
is_direct: true,
preset: "trusted_private_chat",
initial_state: hasKeys ? [{
type: "m.room.encryption",
state_key: "",
content: { algorithm: Crypto_1.EncryptionAlgorithm.MegolmV1AesSha2 },
}] : [],
});
}
if (!this.cached.has(userId))
this.cached.set(userId, []);
this.cached.set(userId, [roomId, ...this.cached.get(userId)]);
await this.persistCache();
return roomId;
}
/**
* Determines if a given room is a DM according to the cache.
* @param {string} roomId The room ID.
* @returns {boolean} True if the room ID is a cached DM room ID.
*/
isDm(roomId) {
for (const val of this.cached.values()) {
if (val.includes(roomId)) {
return true;
}
}
return false;
}
}
exports.DMs = DMs;
//# sourceMappingURL=DMs.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DMs.js","sourceRoot":"","sources":["../src/DMs.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,7 @@
/**
* Sync filter information.
*/
export interface IFilterInfo {
id: number;
filter: any;
}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IFilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"IFilter.js","sourceRoot":"","sources":["../src/IFilter.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,45 @@
import { MatrixClient } from "./MatrixClient";
/**
* Functions for interacting with Matrix prior to having an access token. Intended
* to be used for logging in/registering to get a MatrixClient instance.
*
* By design, this limits the options used to create the MatrixClient. To specify
* custom elements to the client, get the access token from the returned client
* and create a new MatrixClient instance. Due to the nature of Matrix, it is
* also recommended to use the homeserverUrl from the generated MatrixClient as
* it may be different from that given to the MatrixAuth class.
*/
export declare class MatrixAuth {
private homeserverUrl;
/**
* Creates a new MatrixAuth class for creating a MatrixClient
* @param {string} homeserverUrl The homeserver URL to authenticate against.
*/
constructor(homeserverUrl: string);
/**
* Generate a client with no access token so we can reuse the doRequest
* logic already written.
*/
private createTemplateClient;
/**
* Performs simple registration using a password for the account. This will
* assume the server supports the m.login.password flow for registration, and
* will attempt to complete only that stage. The caller is expected to determine
* if the homeserver supports registration prior to invocation.
* @param {string} localpart The localpart (username) to register
* @param {string} password The password to register with
* @param {string} deviceName The name of the newly created device. Optional.
* @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
*/
passwordRegister(localpart: string, password: string, deviceName?: string): Promise<MatrixClient>;
/**
* Performs simple password login with the homeserver. The caller is
* expected to confirm if the homeserver supports this login flow prior
* to invocation.
* @param {string} username The username (localpart or user ID) to log in with
* @param {string} password The password for the account
* @param {string} deviceName The name of the newly created device. Optional.
* @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
*/
passwordLogin(username: string, password: string, deviceName?: string): Promise<MatrixClient>;
}

View File

@@ -0,0 +1,126 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixAuth = void 0;
const MatrixClient_1 = require("./MatrixClient");
/**
* Functions for interacting with Matrix prior to having an access token. Intended
* to be used for logging in/registering to get a MatrixClient instance.
*
* By design, this limits the options used to create the MatrixClient. To specify
* custom elements to the client, get the access token from the returned client
* and create a new MatrixClient instance. Due to the nature of Matrix, it is
* also recommended to use the homeserverUrl from the generated MatrixClient as
* it may be different from that given to the MatrixAuth class.
*/
class MatrixAuth {
homeserverUrl;
/**
* Creates a new MatrixAuth class for creating a MatrixClient
* @param {string} homeserverUrl The homeserver URL to authenticate against.
*/
constructor(homeserverUrl) {
this.homeserverUrl = homeserverUrl;
// nothing to do
}
/**
* Generate a client with no access token so we can reuse the doRequest
* logic already written.
*/
createTemplateClient() {
return new MatrixClient_1.MatrixClient(this.homeserverUrl, "");
}
/**
* Performs simple registration using a password for the account. This will
* assume the server supports the m.login.password flow for registration, and
* will attempt to complete only that stage. The caller is expected to determine
* if the homeserver supports registration prior to invocation.
* @param {string} localpart The localpart (username) to register
* @param {string} password The password to register with
* @param {string} deviceName The name of the newly created device. Optional.
* @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
*/
async passwordRegister(localpart, password, deviceName) {
// First try and complete the stage without UIA in hopes the server is kind to us:
const body = {
username: localpart,
password: password,
initial_device_display_name: deviceName,
};
let response;
try {
response = await this.createTemplateClient().doRequest("POST", "/_matrix/client/v3/register", null, body);
}
catch (e) {
if (e.statusCode === 401) {
if (typeof (e.body) === "string")
e.body = JSON.parse(e.body);
if (!e.body)
throw new Error(JSON.stringify(Object.keys(e)));
// 401 means we need to do UIA, so try and complete a stage
const sessionId = e.body['session'];
const expectedFlow = ["m.login.dummy"];
let hasFlow = false;
for (const flow of e.body['flows']) {
const stages = flow['stages'];
if (stages.length !== expectedFlow.length)
continue;
let stagesMatch = true;
for (let i = 0; i < stages.length; i++) {
if (stages[i] !== expectedFlow[i]) {
stagesMatch = false;
break;
}
}
if (stagesMatch) {
hasFlow = true;
break;
}
}
if (!hasFlow)
throw new Error("Failed to find appropriate login flow in User-Interactive Authentication");
body['auth'] = {
type: expectedFlow[0],
session: sessionId,
};
response = await this.createTemplateClient().doRequest("POST", "/_matrix/client/v3/register", null, body);
}
}
if (!response)
throw new Error("Failed to register");
const accessToken = response['access_token'];
if (!accessToken)
throw new Error("No access token returned");
return new MatrixClient_1.MatrixClient(this.homeserverUrl, accessToken);
}
/**
* Performs simple password login with the homeserver. The caller is
* expected to confirm if the homeserver supports this login flow prior
* to invocation.
* @param {string} username The username (localpart or user ID) to log in with
* @param {string} password The password for the account
* @param {string} deviceName The name of the newly created device. Optional.
* @returns {Promise<MatrixClient>} Resolves to a logged-in MatrixClient
*/
async passwordLogin(username, password, deviceName) {
const body = {
type: "m.login.password",
identifier: {
type: "m.id.user",
user: username,
},
password: password,
initial_device_display_name: deviceName,
};
const response = await this.createTemplateClient().doRequest("POST", "/_matrix/client/v3/login", null, body);
const accessToken = response["access_token"];
if (!accessToken)
throw new Error("Expected access token in response - got nothing");
let homeserverUrl = this.homeserverUrl;
if (response['well_known'] && response['well_known']['m.homeserver'] && response['well_known']['m.homeserver']['base_url']) {
homeserverUrl = response['well_known']['m.homeserver']['base_url'];
}
return new MatrixClient_1.MatrixClient(homeserverUrl, accessToken);
}
}
exports.MatrixAuth = MatrixAuth;
//# sourceMappingURL=MatrixAuth.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MatrixAuth.js","sourceRoot":"","sources":["../src/MatrixAuth.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,906 @@
/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from "events";
import { IStorageProvider } from "./storage/IStorageProvider";
import { IJoinRoomStrategy } from "./strategies/JoinRoomStrategy";
import { UnstableApis } from "./UnstableApis";
import { IPreprocessor } from "./preprocessors/IPreprocessor";
import { Metrics } from "./metrics/Metrics";
import { AdminApis } from "./AdminApis";
import { Presence } from "./models/Presence";
import { Membership, MembershipEvent } from "./models/events/MembershipEvent";
import { EventContext } from "./models/EventContext";
import { PowerLevelBounds } from "./models/PowerLevelBounds";
import { IdentityClient } from "./identity/IdentityClient";
import { OpenIDConnectToken } from "./models/OpenIDConnect";
import { DoHttpRequestOpts } from "./http";
import { Space, SpaceCreateOptions } from "./models/Spaces";
import { PowerLevelAction } from "./models/PowerLevelAction";
import { CryptoClient } from "./e2ee/CryptoClient";
import { FallbackKey, MultiUserDeviceListResponse, OTKAlgorithm, OTKClaimResponse, OTKCounts, OTKs, OwnUserDevice } from "./models/Crypto";
import { ICryptoStorageProvider } from "./storage/ICryptoStorageProvider";
import { IWhoAmI } from "./models/Account";
import { DMs } from "./DMs";
import { ServerVersions } from "./models/ServerVersions";
import { RoomCreateOptions } from "./models/CreateRoom";
import { PresenceState } from './models/events/PresenceEvent';
import { IKeyBackupInfoRetrieved, IKeyBackupInfoUnsigned, IKeyBackupInfoUpdate, IKeyBackupVersion, KeyBackupVersion } from "./models/KeyBackup";
import { MatrixContentScannerClient } from "./MatrixContentScannerClient";
import { MatrixCapabilities } from "./models/Capabilities";
/**
* A client that is capable of interacting with a matrix homeserver.
*/
export declare class MatrixClient extends EventEmitter {
readonly homeserverUrl: string;
readonly accessToken: string;
private storage;
readonly cryptoStore: ICryptoStorageProvider;
/**
* The presence status to use while syncing. The valid values are "online" to set the account as online,
* "offline" to set the user as offline, "unavailable" for marking the user away, and null for not setting
* an explicit presence (the default).
*
* Has no effect if the client is not syncing. Does not apply until the next sync request.
*/
syncingPresence: PresenceState | null;
/**
* The number of milliseconds to wait for new events for on the next sync.
*
* Has no effect if the client is not syncing. Does not apply until the next sync request.
*/
syncingTimeout: number;
/**
* The crypto manager instance for this client. Generally speaking, this shouldn't
* need to be accessed but is made available.
*
* Will be null/undefined if crypto is not possible.
*/
readonly crypto: CryptoClient;
/**
* The Content Scanner API instance for this client. This is set if `opts.enableContentScanner`
* is true. The `downloadContent` and `crypto.decryptMedia` methods automatically go via
* the content scanner when this is set.
*/
readonly contentScannerInstance?: MatrixContentScannerClient;
/**
* The DM manager instance for this client.
*/
readonly dms: DMs;
private userId;
private requestId;
private lastJoinedRoomIds;
private impersonatedUserId;
private impersonatedDeviceId;
private joinStrategy;
private eventProcessors;
private filterId;
private stopSyncing;
private metricsInstance;
private readonly unstableApisInstance;
private cachedVersions;
private versionsLastFetched;
private cachedCapabilites;
private capabilitesLastFetched;
/**
* Set this to true to have the client only persist the sync token after the sync
* has been processed successfully. Note that if this is true then when the sync
* loop throws an error the client will not persist a token.
*/
protected persistTokenAfterSync: boolean;
/**
* Creates a new matrix client
* @param {string} homeserverUrl The homeserver's client-server API URL
* @param {string} accessToken The access token for the homeserver
* @param {IStorageProvider} storage The storage provider to use. Defaults to MemoryStorageProvider.
* @param {ICryptoStorageProvider} cryptoStore Optional crypto storage provider to use. If not supplied,
* end-to-end encryption will not be functional in this client.
*/
constructor(homeserverUrl: string, accessToken: string, storage?: IStorageProvider, cryptoStore?: ICryptoStorageProvider, opts?: {
enableContentScanner?: boolean;
});
/**
* The storage provider for this client. Direct access is usually not required.
*/
get storageProvider(): IStorageProvider;
/**
* The metrics instance for this client
*/
get metrics(): Metrics;
/**
* Assigns a new metrics instance, overwriting the old one.
* @param {Metrics} metrics The new metrics instance.
*/
set metrics(metrics: Metrics);
/**
* Gets the unstable API access class. This is generally not recommended to be
* used by clients.
* @return {UnstableApis} The unstable API access class.
*/
get unstableApis(): UnstableApis;
/**
* Gets the admin API access class.
* @return {AdminApis} The admin API access class.
*/
get adminApis(): AdminApis;
/**
* Sets a user ID to impersonate as. This will assume that the access token for this client
* is for an application service, and that the userId given is within the reach of the
* application service. Setting this to null will stop future impersonation. The user ID is
* assumed to already be valid
* @param {string} userId The user ID to masquerade as, or `null` to clear masquerading.
* @param {string} deviceId Optional device ID to impersonate under the given user, if supported
* by the server. Check the whoami response after setting.
*/
impersonateUserId(userId: string | null, deviceId?: string): void;
/**
* Acquires an identity server client for communicating with an identity server. Note that
* this will automatically do the login portion to establish a usable token with the identity
* server provided, but it will not automatically accept any terms of service.
*
* The identity server name provided will in future be resolved to a server address - for now
* that resolution is assumed to be prefixing the name with `https://`.
* @param {string} identityServerName The domain of the identity server to connect to.
* @returns {Promise<IdentityClient>} Resolves to a prepared identity client.
*/
getIdentityServerClient(identityServerName: string): Promise<IdentityClient>;
/**
* Sets the strategy to use for when joinRoom is called on this client
* @param {IJoinRoomStrategy} strategy The strategy to use, or null to use none
*/
setJoinStrategy(strategy: IJoinRoomStrategy): void;
/**
* Adds a preprocessor to the event pipeline. When this client encounters an event, it
* will try to run it through the preprocessors it can in the order they were added.
* @param {IPreprocessor} preprocessor the preprocessor to add
*/
addPreprocessor(preprocessor: IPreprocessor): void;
private processEvent;
/**
* Get the set of capabilites for the authenticated client.
* @returns {Promise<MatrixCapabilities>} Resolves to the server's supported versions.
*/
getCapabilities(): Promise<MatrixCapabilities>;
/**
* Retrieves the server's supported specification versions and unstable features.
* @returns {Promise<ServerVersions>} Resolves to the server's supported versions.
*/
getServerVersions(): Promise<ServerVersions>;
/**
* Determines if the server supports a given unstable feature flag. Useful for determining
* if the server can support an unstable MSC.
* @param {string} feature The feature name to look for.
* @returns {Promise<boolean>} Resolves to true if the server supports the flag, false otherwise.
*/
doesServerSupportUnstableFeature(feature: string): Promise<boolean>;
/**
* Determines if the server supports a given version of the specification or not.
* @param {string} version The version to look for. Eg: "v1.1"
* @returns {Promise<boolean>} Resolves to true if the server supports the version, false otherwise.
*/
doesServerSupportVersion(version: string): Promise<boolean>;
/**
* Determines if the server supports at least one of the given specification versions or not.
* @param {string[]} versions The versions to look for. Eg: ["v1.1"]
* @returns {Promise<boolean>} Resolves to true if the server supports any of the versions, false otherwise.
*/
doesServerSupportAnyOneVersion(versions: string[]): Promise<boolean>;
/**
* Retrieves an OpenID Connect token from the homeserver for the current user.
* @returns {Promise<OpenIDConnectToken>} Resolves to the token.
*/
getOpenIDConnectToken(): Promise<OpenIDConnectToken>;
/**
* Retrieves content from account data.
* @param {string} eventType The type of account data to retrieve.
* @returns {Promise<any>} Resolves to the content of that account data.
*/
getAccountData<T>(eventType: string): Promise<T>;
/**
* Retrieves content from room account data.
* @param {string} eventType The type of room account data to retrieve.
* @param {string} roomId The room to read the account data from.
* @returns {Promise<any>} Resolves to the content of that account data.
*/
getRoomAccountData<T>(eventType: string, roomId: string): Promise<T>;
/**
* Retrieves content from account data. If the account data request throws an error,
* this simply returns the default provided.
* @param {string} eventType The type of account data to retrieve.
* @param {any} defaultContent The default value. Defaults to null.
* @returns {Promise<any>} Resolves to the content of that account data, or the default.
*/
getSafeAccountData<T>(eventType: string, defaultContent?: T): Promise<T>;
/**
* Retrieves content from room account data. If the account data request throws an error,
* this simply returns the default provided.
* @param {string} eventType The type of room account data to retrieve.
* @param {string} roomId The room to read the account data from.
* @param {any} defaultContent The default value. Defaults to null.
* @returns {Promise<any>} Resolves to the content of that room account data, or the default.
*/
getSafeRoomAccountData<T>(eventType: string, roomId: string, defaultContent?: T): Promise<T>;
/**
* Sets account data.
* @param {string} eventType The type of account data to set
* @param {any} content The content to set
* @returns {Promise<any>} Resolves when updated
*/
setAccountData(eventType: string, content: any): Promise<any>;
/**
* Sets room account data.
* @param {string} eventType The type of room account data to set
* @param {string} roomId The room to set account data in
* @param {any} content The content to set
* @returns {Promise<any>} Resolves when updated
*/
setRoomAccountData(eventType: string, roomId: string, content: any): Promise<any>;
/**
* Gets the presence information for the current user.
* @returns {Promise<Presence>} Resolves to the presence status of the user.
*/
getPresenceStatus(): Promise<Presence>;
/**
* Gets the presence information for a given user.
* @param {string} userId The user ID to look up the presence of.
* @returns {Promise<Presence>} Resolves to the presence status of the user.
*/
getPresenceStatusFor(userId: string): Promise<Presence>;
/**
* Sets the presence status for the current user.
* @param {PresenceState} presence The new presence state for the user.
* @param {string?} statusMessage Optional status message to include with the presence.
* @returns {Promise<any>} Resolves when complete.
*/
setPresenceStatus(presence: PresenceState, statusMessage?: string | undefined): Promise<any>;
/**
* Gets a published alias for the given room. These are supplied by the room admins
* and should point to the room, but may not. This is primarily intended to be used
* in the context of rendering a mention (pill) for a room.
* @param {string} roomIdOrAlias The room ID or alias to get an alias for.
* @returns {Promise<string>} Resolves to a published room alias, or falsey if none found.
*/
getPublishedAlias(roomIdOrAlias: string): Promise<string>;
/**
* Adds a new room alias to the room directory
* @param {string} alias The alias to add (eg: "#my-room:matrix.org")
* @param {string} roomId The room ID to add the alias to
* @returns {Promise} resolves when the alias has been added
*/
createRoomAlias(alias: string, roomId: string): Promise<any>;
/**
* Removes a room alias from the room directory
* @param {string} alias The alias to remove
* @returns {Promise} resolves when the alias has been deleted
*/
deleteRoomAlias(alias: string): Promise<any>;
/**
* Sets the visibility of a room in the directory.
* @param {string} roomId The room ID to manipulate the visibility of
* @param {"public" | "private"} visibility The visibility to set for the room
* @return {Promise} resolves when the visibility has been updated
*/
setDirectoryVisibility(roomId: string, visibility: "public" | "private"): Promise<any>;
/**
* Gets the visibility of a room in the directory.
* @param {string} roomId The room ID to query the visibility of
* @return {Promise<"public"|"private">} The visibility of the room
*/
getDirectoryVisibility(roomId: string): Promise<"public" | "private">;
/**
* Resolves a room ID or alias to a room ID. If the given ID or alias looks like a room ID
* already, it will be returned as-is. If the room ID or alias looks like a room alias, it
* will be resolved to a room ID if possible. If the room ID or alias is neither, an error
* will be raised.
* @param {string} roomIdOrAlias the room ID or alias to resolve to a room ID
* @returns {Promise<string>} resolves to the room ID
*/
resolveRoom(roomIdOrAlias: string): Promise<string>;
/**
* Does a room directory lookup for a given room alias
* @param {string} roomAlias the room alias to look up in the room directory
* @returns {Promise<RoomDirectoryLookupResponse>} resolves to the room's information
*/
lookupRoomAlias(roomAlias: string): Promise<RoomDirectoryLookupResponse>;
/**
* Invites a user to a room.
* @param {string} userId the user ID to invite
* @param {string} roomId the room ID to invite the user to
* @returns {Promise<any>} resolves when completed
*/
inviteUser(userId: any, roomId: any): Promise<any>;
/**
* Kicks a user from a room.
* @param {string} userId the user ID to kick
* @param {string} roomId the room ID to kick the user in
* @param {string?} reason optional reason for the kick
* @returns {Promise<any>} resolves when completed
*/
kickUser(userId: any, roomId: any, reason?: any): Promise<any>;
/**
* Bans a user from a room.
* @param {string} userId the user ID to ban
* @param {string} roomId the room ID to set the ban in
* @param {string?} reason optional reason for the ban
* @returns {Promise<any>} resolves when completed
*/
banUser(userId: any, roomId: any, reason?: any): Promise<any>;
/**
* Unbans a user in a room.
* @param {string} userId the user ID to unban
* @param {string} roomId the room ID to lift the ban in
* @returns {Promise<any>} resolves when completed
*/
unbanUser(userId: any, roomId: any): Promise<any>;
/**
* Gets the current user ID for this client
* @returns {Promise<string>} The user ID of this client
*/
getUserId(): Promise<string>;
/**
* Gets the user's information from the server directly.
* @returns {Promise<IWhoAmI>} The "who am I" response.
*/
getWhoAmI(): Promise<IWhoAmI>;
/**
* Stops the client from syncing.
*/
stop(): void;
/**
* Starts syncing the client with an optional filter
* @param {any} filter The filter to use, or null for none
* @returns {Promise<any>} Resolves when the client has started syncing
*/
start(filter?: any): Promise<any>;
protected startSyncInternal(): Promise<any>;
protected startSync(emitFn?: (emitEventType: string, ...payload: any[]) => Promise<any>): Promise<void>;
protected doSync(token: string): Promise<any>;
protected processSync(raw: any, emitFn?: (emitEventType: string, ...payload: any[]) => Promise<any>): Promise<any>;
/**
* Gets an event for a room. If the event is encrypted, and the client supports encryption,
* and the room is encrypted, then this will return a decrypted event.
* @param {string} roomId the room ID to get the event in
* @param {string} eventId the event ID to look up
* @returns {Promise<any>} resolves to the found event
*/
getEvent(roomId: string, eventId: string): Promise<any>;
/**
* Gets an event for a room. Returned as a raw event.
* @param {string} roomId the room ID to get the event in
* @param {string} eventId the event ID to look up
* @returns {Promise<any>} resolves to the found event
*/
getRawEvent(roomId: string, eventId: string): Promise<any>;
/**
* Gets the room state for the given room. Returned as raw events.
* @param {string} roomId the room ID to get state for
* @returns {Promise<any[]>} resolves to the room's state
*/
getRoomState(roomId: string): Promise<any[]>;
/**
* Gets the state events for a given room of a given type under the given state key.
* @param {string} roomId the room ID
* @param {string} type the event type
* @param {String} stateKey the state key, falsey if not needed
* @returns {Promise<any|any[]>} resolves to the state event(s)
* @deprecated It is not possible to get an array of events - use getRoomStateEvent instead
*/
getRoomStateEvents(roomId: any, type: any, stateKey: any): Promise<any | any[]>;
/**
* Gets a state event for a given room of a given type under the given state key.
* @param {string} roomId the room ID
* @param {string} type the event type
* @param {String} stateKey the state key
* @returns {Promise<any>} resolves to the state event
*/
getRoomStateEvent(roomId: any, type: any, stateKey: any): Promise<any>;
/**
* Gets the context surrounding an event.
* @param {string} roomId The room ID to get the context in.
* @param {string} eventId The event ID to get the context of.
* @param {number} limit The maximum number of events to return on either side of the event.
* @returns {Promise<EventContext>} The context of the event
*/
getEventContext(roomId: string, eventId: string, limit?: number): Promise<EventContext>;
/**
* Get the nearest event to a given timestamp, either forwards or backwards.
* @param roomId The room ID to get the context in.
* @param ts The event ID to get the context of.
* @param dir The maximum number of events to return on either side of the event.
* @returns The ID and origin server timestamp of the event.
*/
getEventNearestToTimestamp(roomId: string, ts: number, dir: "f" | "b"): Promise<{
event_id: string;
origin_server_ts: number;
}>;
/**
* Gets the profile for a given user
* @param {string} userId the user ID to lookup
* @returns {Promise<any>} the profile of the user
*/
getUserProfile(userId: string): Promise<any>;
/**
* Sets a new display name for the user.
* @param {string} displayName the new display name for the user, or null to clear
* @returns {Promise<any>} resolves when complete
*/
setDisplayName(displayName: string): Promise<any>;
/**
* Sets a new avatar url for the user.
* @param {string} avatarUrl the new avatar URL for the user, in the form of a Matrix Content URI
* @returns {Promise<any>} resolves when complete
*/
setAvatarUrl(avatarUrl: string): Promise<any>;
/**
* Joins the given room
* @param {string} roomIdOrAlias the room ID or alias to join
* @param {string[]} viaServers the server names to try and join through
* @returns {Promise<string>} resolves to the joined room ID
*/
joinRoom(roomIdOrAlias: string, viaServers?: string[]): Promise<string>;
/**
* Gets a list of joined room IDs
* @returns {Promise<string[]>} resolves to a list of room IDs the client participates in
*/
getJoinedRooms(): Promise<string[]>;
/**
* Gets the joined members in a room. The client must be in the room to make this request.
* @param {string} roomId The room ID to get the joined members of.
* @returns {Promise<string>} The joined user IDs in the room
*/
getJoinedRoomMembers(roomId: string): Promise<string[]>;
/**
* Gets the joined members in a room, as an object mapping userIds to profiles. The client must be in the room to make this request.
* @param {string} roomId The room ID to get the joined members of.
* @returns {Object} The joined user IDs in the room as an object mapped to a set of profiles.
*/
getJoinedRoomMembersWithProfiles(roomId: string): Promise<{
[userId: string]: {
display_name?: string;
avatar_url?: string;
};
}>;
/**
* Gets the membership events of users in the room. Defaults to all membership
* types, though this can be controlled with the membership and notMembership
* arguments. To change the point in time, use the batchToken.
* @param {string} roomId The room ID to get members in.
* @param {string} batchToken The point in time to get members at (or null for 'now')
* @param {string[]} membership The membership kinds to search for.
* @param {string[]} notMembership The membership kinds to not search for.
* @returns {Promise<MembershipEvent[]>} Resolves to the membership events of the users in the room.
* @see getRoomMembersByMembership
* @see getRoomMembersWithoutMembership
* @see getAllRoomMembers
*/
getRoomMembers(roomId: string, batchToken?: string, membership?: Membership[], notMembership?: Membership[]): Promise<MembershipEvent[]>;
/**
* Gets all room members in the room, optionally at a given point in time.
* @param {string} roomId The room ID to get members of.
* @param {string} atToken Optional batch token to get members at. Leave falsy for "now".
* @returns {Promise<MembershipEvent[]>} Resolves to the member events in the room.
*/
getAllRoomMembers(roomId: string, atToken?: string): Promise<MembershipEvent[]>;
/**
* Gets the membership events of users in the room which have a particular membership type. To change
* the point in time the server should return membership events at, use `atToken`.
* @param {string} roomId The room ID to get members in.
* @param {Membership} membership The membership to search for.
* @param {string?} atToken Optional batch token to use, or null for "now".
* @returns {Promise<MembershipEvent[]>} Resolves to the membership events of the users in the room.
*/
getRoomMembersByMembership(roomId: string, membership: Membership, atToken?: string): Promise<MembershipEvent[]>;
/**
* Gets the membership events of users in the room which lack a particular membership type. To change
* the point in time the server should return membership events at, use `atToken`.
* @param {string} roomId The room ID to get members in.
* @param {Membership} notMembership The membership to NOT search for.
* @param {string?} atToken Optional batch token to use, or null for "now".
* @returns {Promise<MembershipEvent[]>} Resolves to the membership events of the users in the room.
*/
getRoomMembersWithoutMembership(roomId: string, notMembership: Membership, atToken?: string): Promise<MembershipEvent[]>;
private getRoomMembersAt;
/**
* Leaves the given room
* @param {string} roomId the room ID to leave
* @param {string=} reason Optional reason to be included as the reason for leaving the room.
* @returns {Promise<any>} resolves when left
*/
leaveRoom(roomId: string, reason?: string): Promise<any>;
/**
* Forgets the given room
* @param {string} roomId the room ID to forget
* @returns {Promise<{}>} Resolves when forgotten
*/
forgetRoom(roomId: string): Promise<{}>;
/**
* Sends a read receipt for an event in a room
* @param {string} roomId the room ID to send the receipt to
* @param {string} eventId the event ID to set the receipt at
* @returns {Promise<any>} resolves when the receipt has been sent
*/
sendReadReceipt(roomId: string, eventId: string): Promise<any>;
/**
* Sets the typing status of the current user in a room
* @param {string} roomId the room ID the user is typing in
* @param {boolean} typing is the user currently typing
* @param {number} timeout how long should the server preserve the typing state, in milliseconds
* @returns {Promise<any>} resolves when the typing state has been set
*/
setTyping(roomId: string, typing: boolean, timeout?: number): Promise<any>;
/**
* Replies to a given event with the given text. The event is sent with a msgtype of m.text.
* The message will be encrypted if the client supports encryption and the room is encrypted.
* @param {string} roomId the room ID to reply in
* @param {any} event the event to reply to
* @param {string} text the text to reply with
* @param {string} html the HTML to reply with, or falsey to use the `text`
* @returns {Promise<string>} resolves to the event ID which was sent
*/
replyText(roomId: string, event: any, text: string, html?: string): Promise<string>;
/**
* Replies to a given event with the given HTML. The event is sent with a msgtype of m.text.
* The message will be encrypted if the client supports encryption and the room is encrypted.
* @param {string} roomId the room ID to reply in
* @param {any} event the event to reply to
* @param {string} html the HTML to reply with.
* @returns {Promise<string>} resolves to the event ID which was sent
*/
replyHtmlText(roomId: string, event: any, html: string): Promise<string>;
/**
* Replies to a given event with the given text. The event is sent with a msgtype of m.notice.
* The message will be encrypted if the client supports encryption and the room is encrypted.
* @param {string} roomId the room ID to reply in
* @param {any} event the event to reply to
* @param {string} text the text to reply with
* @param {string} html the HTML to reply with, or falsey to use the `text`
* @returns {Promise<string>} resolves to the event ID which was sent
*/
replyNotice(roomId: string, event: any, text: string, html?: string): Promise<string>;
/**
* Replies to a given event with the given HTML. The event is sent with a msgtype of m.notice.
* The message will be encrypted if the client supports encryption and the room is encrypted.
* @param {string} roomId the room ID to reply in
* @param {any} event the event to reply to
* @param {string} html the HTML to reply with.
* @returns {Promise<string>} resolves to the event ID which was sent
*/
replyHtmlNotice(roomId: string, event: any, html: string): Promise<string>;
/**
* Sends a notice to the given room. The message will be encrypted if the client supports
* encryption and the room is encrypted.
* @param {string} roomId the room ID to send the notice to
* @param {string} text the text to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendNotice(roomId: string, text: string): Promise<string>;
/**
* Sends a notice to the given room with HTML content. The message will be encrypted if the client supports
* encryption and the room is encrypted.
* @param {string} roomId the room ID to send the notice to
* @param {string} html the HTML to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendHtmlNotice(roomId: string, html: string): Promise<string>;
/**
* Sends a text message to the given room. The message will be encrypted if the client supports
* encryption and the room is encrypted.
* @param {string} roomId the room ID to send the text to
* @param {string} text the text to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendText(roomId: string, text: string): Promise<string>;
/**
* Sends a text message to the given room with HTML content. The message will be encrypted if the client supports
* encryption and the room is encrypted.
* @param {string} roomId the room ID to send the text to
* @param {string} html the HTML to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendHtmlText(roomId: string, html: string): Promise<string>;
/**
* Sends a message to the given room. The message will be encrypted if the client supports
* encryption and the room is encrypted.
* @param {string} roomId the room ID to send the message to
* @param {object} content the event content to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendMessage(roomId: string, content: any): Promise<string>;
/**
* Sends an event to the given room. This will encrypt the event before sending if the room is
* encrypted and the client supports encryption. Use sendRawEvent() to avoid this behaviour.
* @param {string} roomId the room ID to send the event to
* @param {string} eventType the type of event to send
* @param {string} content the event body to send
* @returns {Promise<string>} resolves to the event ID that represents the event
*/
sendEvent(roomId: string, eventType: string, content: any): Promise<string>;
/**
* Sends an event to the given room.
* @param {string} roomId the room ID to send the event to
* @param {string} eventType the type of event to send
* @param {string} content the event body to send
* @returns {Promise<string>} resolves to the event ID that represents the event
*/
sendRawEvent(roomId: string, eventType: string, content: any): Promise<string>;
/**
* Sends a state event to the given room
* @param {string} roomId the room ID to send the event to
* @param {string} type the event type to send
* @param {string} stateKey the state key to send, should not be null
* @param {string} content the event body to send
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendStateEvent(roomId: string, type: string, stateKey: string, content: any): Promise<string>;
/**
* Redact an event in a given room
* @param {string} roomId the room ID to send the redaction to
* @param {string} eventId the event ID to redact
* @param {String} reason an optional reason for redacting the event
* @returns {Promise<string>} resolves to the event ID that represents the redaction
*/
redactEvent(roomId: string, eventId: string, reason?: string | null): Promise<string>;
/**
* Creates a room. See the RoomCreateOptions interface
* for more information on what to provide for `properties`. Note that creating
* a room may cause the bot/appservice to raise a join event.
* @param {RoomCreateOptions} properties the properties of the room.
* @returns {Promise<string>} resolves to the room ID that represents the room
*/
createRoom(properties?: RoomCreateOptions): Promise<string>;
/**
* Checks if a given user has a required power level required to send the given event.
* @param {string} userId the user ID to check the power level of
* @param {string} roomId the room ID to check the power level in
* @param {string} eventType the event type to look for in the `events` property of the power levels
* @param {boolean} isState true to indicate the event is intended to be a state event
* @returns {Promise<boolean>} resolves to true if the user has the required power level, resolves to false otherwise
*/
userHasPowerLevelFor(userId: string, roomId: string, eventType: string, isState: boolean): Promise<boolean>;
/**
* Checks if a given user has a required power level to perform the given action
* @param {string} userId the user ID to check the power level of
* @param {string} roomId the room ID to check the power level in
* @param {PowerLevelAction} action the action to check power level for
* @returns {Promise<boolean>} resolves to true if the user has the required power level, resolves to false otherwise
*/
userHasPowerLevelForAction(userId: string, roomId: string, action: PowerLevelAction): Promise<boolean>;
/**
* Determines the boundary conditions for this client's ability to change another user's power level
* in a given room. This will identify the maximum possible level this client can change the user to,
* and if that change could even be possible. If the returned object indicates that the client can
* change the power level of the user, the client is able to set the power level to any value equal
* to or less than the maximum value.
* @param {string} targetUserId The user ID to compare against.
* @param {string} roomId The room ID to compare within.
* @returns {Promise<PowerLevelBounds>} The bounds of the client's ability to change the user's power level.
*/
calculatePowerLevelChangeBoundsOn(targetUserId: string, roomId: string): Promise<PowerLevelBounds>;
/**
* Sets the power level for a given user ID in the given room. Note that this is not safe to
* call multiple times concurrently as changes are not atomic. This will throw an error if
* the user lacks enough permission to change the power level, or if a power level event is
* missing from the room.
* @param {string} userId The user ID to change
* @param {string} roomId The room ID to change the power level in
* @param {number} newLevel The integer power level to set the user to.
* @returns {Promise<any>} Resolves when complete.
*/
setUserPowerLevel(userId: string, roomId: string, newLevel: number): Promise<any>;
private getMediaEndpointPrefix;
/**
* Converts a MXC URI to an HTTP URL.
* @param {string} mxc The MXC URI to convert
* @returns {string} The HTTP URL for the content.
*/
mxcToHttp(mxc: string): Promise<string>;
/**
* Converts a MXC URI to an HTTP URL for downsizing the content.
* @param {string} mxc The MXC URI to convert and downsize.
* @param {number} width The width, as an integer, for the thumbnail.
* @param {number} height The height, as an intenger, for the thumbnail.
* @param {"crop"|"scale"} method Whether to crop or scale (preserve aspect ratio) the content.
* @returns {string} The HTTP URL for the downsized content.
*/
mxcToHttpThumbnail(mxc: string, width: number, height: number, method: "crop" | "scale"): Promise<string>;
/**
* Uploads data to the homeserver's media repository. Note that this will <b>not</b> automatically encrypt
* media as it cannot determine if the media should be encrypted.
* @param {Buffer} data the content to upload.
* @param {string} contentType the content type of the file. Defaults to application/octet-stream
* @param {string} filename the name of the file. Optional.
* @returns {Promise<string>} resolves to the MXC URI of the content
*/
uploadContent(data: Buffer, contentType?: string, filename?: string): Promise<string>;
/**
* Download content from the homeserver's media repository. Note that this will <b>not</b> automatically decrypt
* media as it cannot determine if the media is encrypted.
* @param {string} mxcUrl The MXC URI for the content.
* @param {string} allowRemote Indicates to the server that it should not attempt to fetch the
* media if it is deemed remote. This is to prevent routing loops where the server contacts itself.
* Defaults to true if not provided.
* This is IGNORED if the content scanner is configured, as the API has no compatible option.
* @returns {Promise<{data: Buffer, contentType: string}>} Resolves to the downloaded content.
*/
downloadContent(mxcUrl: string, allowRemote?: boolean): Promise<{
data: Buffer;
contentType: string;
}>;
/**
* Uploads data to the homeserver's media repository after downloading it from the
* provided URL.
* @param {string} url The URL to download content from.
* @returns {Promise<string>} Resolves to the MXC URI of the content
*/
uploadContentFromUrl(url: string): Promise<string>;
/**
* Determines the upgrade history for a given room as a doubly-linked list styled structure. Given
* a room ID in the history of upgrades, the resulting `previous` array will hold any rooms which
* are older than the given room. The resulting `newer` array will hold any rooms which are newer
* versions of the room. Both arrays will be defined, but may be empty individually. Element zero
* of each will always be the nearest to the given room ID and the last element will be the furthest
* from the room. The given room will never be in either array.
* @param {string} roomId the room ID to get the history of
* @returns {Promise<{previous: RoomReference[], newer: RoomReference[]}>} Resolves to the room's
* upgrade history
*/
getRoomUpgradeHistory(roomId: string): Promise<{
previous: RoomReference[];
newer: RoomReference[];
current: RoomReference;
}>;
/**
* Creates a Space room.
* @param {SpaceCreateOptions} opts The creation options.
* @returns {Promise<Space>} Resolves to the created space.
*/
createSpace(opts: SpaceCreateOptions): Promise<Space>;
/**
* Gets a Space.
* This API does not work with unstable spaces (e.g. org.matrix.msc.1772.space)
*
* @throws If the room is not a space or there was an error
* @returns {Promise<Space>} Resolves to the space.
*/
getSpace(roomIdOrAlias: string): Promise<Space>;
/**
* Uploads One Time Keys for the current device.
* @param {OTKs} keys The keys to upload.
* @returns {Promise<OTKCounts>} Resolves to the current One Time Key counts when complete.
*/
uploadDeviceOneTimeKeys(keys: OTKs): Promise<OTKCounts>;
/**
* Gets the current One Time Key counts.
* @returns {Promise<OTKCounts>} Resolves to the One Time Key counts.
*/
checkOneTimeKeyCounts(): Promise<OTKCounts>;
/**
* Uploads a fallback One Time Key to the server for usage. This will replace the existing fallback
* key.
* @param {FallbackKey} fallbackKey The fallback key.
* @returns {Promise<OTKCounts>} Resolves to the One Time Key counts.
*/
uploadFallbackKey(fallbackKey: FallbackKey): Promise<OTKCounts>;
/**
* Gets <b>unverified</b> device lists for the given users. The caller is expected to validate
* and verify the device lists, including that the returned devices belong to the claimed users.
*
* Failures with federation are reported in the returned object. Users which did not fail a federation
* lookup but have no devices will not appear in either the failures or in the returned devices.
*
* See https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-query for more
* information.
* @param {string[]} userIds The user IDs to query.
* @param {number} federationTimeoutMs The default timeout for requesting devices over federation. Defaults to
* 10 seconds.
* @returns {Promise<MultiUserDeviceListResponse>} Resolves to the device list/errors for the requested user IDs.
*/
getUserDevices(userIds: string[], federationTimeoutMs?: number): Promise<MultiUserDeviceListResponse>;
/**
* Gets a device list for the client's own account, with metadata. The devices are not verified
* in this response, but should be active on the account.
* @returns {Promise<OwnUserDevice[]>} Resolves to the active devices on the account.
*/
getOwnDevices(): Promise<OwnUserDevice[]>;
/**
* Claims One Time Keys for a set of user devices, returning those keys. The caller is expected to verify
* and validate the returned keys.
*
* Failures with federation are reported in the returned object.
* @param {Record<string, Record<string, OTKAlgorithm>>} userDeviceMap The map of user IDs to device IDs to
* OTKAlgorithm to request a claim for.
* @param {number} federationTimeoutMs The default timeout for claiming keys over federation. Defaults to
* 10 seconds.
*/
claimOneTimeKeys(userDeviceMap: Record<string, Record<string, OTKAlgorithm>>, federationTimeoutMs?: number): Promise<OTKClaimResponse>;
/**
* Sends to-device messages to the respective users/devices.
* @param {string} type The message type being sent.
* @param {Record<string, Record<string, any>>} messages The messages to send, mapped as user ID to
* device ID (or "*" to denote all of the user's devices) to message payload (content).
* @returns {Promise<void>} Resolves when complete.
*/
sendToDevices(type: string, messages: Record<string, Record<string, any>>): Promise<void>;
/**
* Get information about the latest room key backup version.
* @returns {Promise<IKeyBackupInfoRetrieved|null>} Resolves to the retrieved key backup info,
* or null if there is no existing backup.
*/
getKeyBackupVersion(): Promise<IKeyBackupInfoRetrieved | null>;
/**
* Create a new room key backup.
* @param {IKeyBackupInfoUnsigned} info The properties of the key backup to create,
* with its auth_data left unsigned.
* @returns {Promise<IKeyBackupVersion>} Resolves to the version id of the new backup.
*/
signAndCreateKeyBackupVersion(info: IKeyBackupInfoUnsigned): Promise<IKeyBackupVersion>;
/**
* Update an existing room key backup.
* @param {KeyBackupVersion} version The key backup version to update.
* @param {IKeyBackupInfoUpdate} info The properties of the key backup to be applied.
* @returns {Promise<void>} Resolves when complete.
*/
updateKeyBackupVersion(version: KeyBackupVersion, info: IKeyBackupInfoUpdate): Promise<void>;
/**
* Enable backing up of room keys.
* @param {IKeyBackupInfoRetrieved} info The configuration for key backup behaviour,
* as returned by {@link getKeyBackupVersion}.
* @returns {Promise<void>} Resolves when complete.
*/
enableKeyBackup(info: IKeyBackupInfoRetrieved): Promise<void>;
/**
* Disable backing up of room keys.
*/
disableKeyBackup(): Promise<void>;
/**
* Exports a set of keys for a given session.
* @param roomId The room ID for the session.
* @param sessionId The session ID.
* @returns An array of session keys.
*/
exportRoomKeysForSession(roomId: string, sessionId: string): Promise<import("./models/KeyBackup").IMegolmSessionDataExport[]>;
/**
* Get relations for a given event.
* @param {string} roomId The room ID to for the given event.
* @param {string} eventId The event ID to list relations for.
* @param {string?} relationType The type of relations (e.g. `m.room.member`) to filter for. Optional.
* @param {string?} eventType The type of event to look for (e.g. `m.room.member`). Optional.
* @returns {Promise<{chunk: any[]}>} Resolves to an object containing the chunk of relations
*/
getRelationsForEvent(roomId: string, eventId: string, relationType?: string, eventType?: string): Promise<{
chunk: any[];
}>;
/**
* Performs a web request to the homeserver, applying appropriate authorization headers for
* this client.
* @param {"GET"|"POST"|"PUT"|"DELETE"} method The HTTP method to use in the request
* @param {string} endpoint The endpoint to call. For example: "/_matrix/client/v3/account/whoami"
* @param {any} qs The query string to send. Optional.
* @param {any} body The request body to send. Optional. Will be converted to JSON unless the type is a Buffer.
* @param {number} timeout The number of milliseconds to wait before timing out.
* @param {boolean} raw If true, the raw response will be returned instead of the response body.
* @param {string} contentType The content type to send. Only used if the `body` is a Buffer.
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
doRequest(method: any, endpoint: any, qs?: any, body?: any, timeout?: number, raw?: boolean, contentType?: string, noEncoding?: boolean, opts?: DoHttpRequestOpts): Promise<any>;
}
export interface RoomDirectoryLookupResponse {
roomId: string;
residentServers: string[];
}
export interface RoomReference {
/**
* The room ID being referenced
*/
roomId: string;
/**
* The version of the room at the time
*/
version: string;
/**
* If going backwards, the tombstone event ID, otherwise the creation
* event. If the room can't be verified, this will be null. Will be
* null if this reference is to the current room.
*/
refEventId: string;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
/// <reference types="node" />
import { EncryptedFile, MatrixClient } from ".";
export interface ContentScannerResult {
info: string;
clean: boolean;
}
export interface ContentScannerErrorResult {
info: string;
reason: string;
}
export declare class MatrixContentScannerError extends Error {
readonly body: ContentScannerErrorResult;
constructor(body: ContentScannerErrorResult);
}
/**
* API client for https://github.com/element-hq/matrix-content-scanner-python.
*/
export declare class MatrixContentScannerClient {
readonly client: MatrixClient;
constructor(client: MatrixClient);
scanContent(mxcUrl: string): Promise<ContentScannerResult>;
scanContentEncrypted(file: EncryptedFile): Promise<ContentScannerResult>;
downloadContent(mxcUrl: string): ReturnType<MatrixClient["downloadContent"]>;
downloadEncryptedContent(file: EncryptedFile): Promise<Buffer>;
}

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixContentScannerClient = exports.MatrixContentScannerError = void 0;
const MXCUrl_1 = require("./models/MXCUrl");
class MatrixContentScannerError extends Error {
body;
constructor(body) {
super(`Encountered error scanning content (${body.reason}): ${body.info}`);
this.body = body;
}
}
exports.MatrixContentScannerError = MatrixContentScannerError;
const errorHandler = (_response, errBody) => {
return typeof (errBody) === "object" && 'reason' in errBody ?
new MatrixContentScannerError(errBody) : undefined;
};
/**
* API client for https://github.com/element-hq/matrix-content-scanner-python.
*/
class MatrixContentScannerClient {
client;
constructor(client) {
this.client = client;
}
async scanContent(mxcUrl) {
const { domain, mediaId } = MXCUrl_1.MXCUrl.parse(mxcUrl);
const path = `/_matrix/media_proxy/unstable/scan/${domain}/${mediaId}`;
const res = await this.client.doRequest("GET", path, null, null, null, false, null, false, { errorHandler });
return res;
}
async scanContentEncrypted(file) {
// Sanity check.
MXCUrl_1.MXCUrl.parse(file.url);
const path = `/_matrix/media_proxy/unstable/scan_encrypted`;
const res = await this.client.doRequest("POST", path, null, { file }, null, false, null, false, { errorHandler });
return res;
}
async downloadContent(mxcUrl) {
const { domain, mediaId } = MXCUrl_1.MXCUrl.parse(mxcUrl);
const path = `/_matrix/media_proxy/unstable/download/${encodeURIComponent(domain)}/${encodeURIComponent(mediaId)}`;
const res = await this.client.doRequest("GET", path, null, null, null, true, null, true, { errorHandler });
return {
data: res.body,
contentType: res.headers["content-type"],
};
}
async downloadEncryptedContent(file) {
// Sanity check.
MXCUrl_1.MXCUrl.parse(file.url);
const path = `/_matrix/media_proxy/unstable/download_encrypted`;
const res = await this.client.doRequest("POST", path, undefined, {
file,
}, null, true, null, true, { errorHandler });
return res.data;
}
}
exports.MatrixContentScannerClient = MatrixContentScannerClient;
//# sourceMappingURL=MatrixContentScannerClient.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MatrixContentScannerClient.js","sourceRoot":"","sources":["../src/MatrixContentScannerClient.ts"],"names":[],"mappings":";;;AACA,4CAAyC;AAWzC,MAAa,yBAA0B,SAAQ,KAAK;IACpB;IAA5B,YAA4B,IAA+B;QACvD,KAAK,CAAC,uCAAuC,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QADnD,SAAI,GAAJ,IAAI,CAA2B;IAE3D,CAAC;CACJ;AAJD,8DAIC;AAED,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;IACxC,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,QAAQ,IAAI,OAAO,CAAC,CAAC;QACzD,IAAI,yBAAyB,CAAC,OAAoC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxF,CAAC,CAAC;AAEF;;GAEG;AACH,MAAa,0BAA0B;IACP;IAA5B,YAA4B,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAEhD,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,MAAc;QACnC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,eAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,sCAAsC,MAAM,IAAI,OAAO,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7G,OAAO,GAAG,CAAC;IACf,CAAC;IAEM,KAAK,CAAC,oBAAoB,CAAC,IAAmB;QACjD,gBAAgB;QAChB,eAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,8CAA8C,CAAC;QAC5D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAClH,OAAO,GAAG,CAAC;IACf,CAAC;IAEM,KAAK,CAAC,eAAe,CAAC,MAAc;QACvC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,eAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,0CAA0C,kBAAkB,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QACnH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3G,OAAO;YACH,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC;SAC3C,CAAC;IACN,CAAC;IAEM,KAAK,CAAC,wBAAwB,CAAC,IAAmB;QACrD,gBAAgB;QAChB,eAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,kDAAkD,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;YAC7D,IAAI;SACP,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,CAAC,IAAI,CAAC;IACpB,CAAC;CACJ;AAvCD,gEAuCC"}

View File

@@ -0,0 +1,42 @@
/**
* A client specifically designed to interact with Pantalaimon instead of
* a Matrix homeserver. The key part of this is managing the access token
* and username/password for interacting with Pantalaimon.
*
* If the storage provider given claims to have an access token for
* this client, it will be used even if Pantalaimon considers it invalid.
*
* Expected usage:
* <code>
* const storage = new SimpleFsStorageProvider("storage/bot.json");
* const pantalaimon = new PantalaimonClient("http://localhost:8008", storage);
*
* // Note that the credentials will only be used if there is no available access token.
* const client = await pantalaimon.createClientWithCredentials("username", "password");
* </code>
*/
import { IStorageProvider } from "./storage/IStorageProvider";
import { MatrixClient } from "./MatrixClient";
/**
* Supporting functions for interacting with a Pantalaimon instance.
* @category Encryption
*/
export declare class PantalaimonClient {
private homeserverUrl;
private storageProvider;
/**
* Creates a new PantalaimonClient class for interacting with Pantalaimon. The storage
* provider given will also be used in the client.
* @param {string} homeserverUrl The homeserver (Pantalaimon) URL to interact with.
* @param {IStorageProvider} storageProvider The storage provider to back the client with.
*/
constructor(homeserverUrl: string, storageProvider: IStorageProvider);
/**
* Authenticates and creates the Pantalaimon-capable client. The username and password given
* are only used if the storage provider does not reveal an access token.
* @param {string} username The username to log in with, if needed.
* @param {string} password The password to log in with, if needed.
* @returns {Promise<MatrixClient>} Resolves to a MatrixClient ready for interacting with Pantalaimon.
*/
createClientWithCredentials(username: string, password: string): Promise<MatrixClient>;
}

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PantalaimonClient = void 0;
const MatrixClient_1 = require("./MatrixClient");
const MatrixAuth_1 = require("./MatrixAuth");
const ACCESS_TOKEN_STORAGE_KEY = "pantalaimon_access_token";
// TODO: Write a test for this (it's hard because of the many interactions with different parts)
/**
* Supporting functions for interacting with a Pantalaimon instance.
* @category Encryption
*/
class PantalaimonClient {
homeserverUrl;
storageProvider;
/**
* Creates a new PantalaimonClient class for interacting with Pantalaimon. The storage
* provider given will also be used in the client.
* @param {string} homeserverUrl The homeserver (Pantalaimon) URL to interact with.
* @param {IStorageProvider} storageProvider The storage provider to back the client with.
*/
constructor(homeserverUrl, storageProvider) {
this.homeserverUrl = homeserverUrl;
this.storageProvider = storageProvider;
// nothing to do
}
/**
* Authenticates and creates the Pantalaimon-capable client. The username and password given
* are only used if the storage provider does not reveal an access token.
* @param {string} username The username to log in with, if needed.
* @param {string} password The password to log in with, if needed.
* @returns {Promise<MatrixClient>} Resolves to a MatrixClient ready for interacting with Pantalaimon.
*/
async createClientWithCredentials(username, password) {
const accessToken = await Promise.resolve(this.storageProvider.readValue(ACCESS_TOKEN_STORAGE_KEY));
if (accessToken) {
return new MatrixClient_1.MatrixClient(this.homeserverUrl, accessToken, this.storageProvider);
}
const auth = new MatrixAuth_1.MatrixAuth(this.homeserverUrl);
const authedClient = await auth.passwordLogin(username, password);
await Promise.resolve(this.storageProvider.storeValue(ACCESS_TOKEN_STORAGE_KEY, authedClient.accessToken));
// We recreate the client to ensure we set it up with the right storage provider.
return new MatrixClient_1.MatrixClient(this.homeserverUrl, authedClient.accessToken, this.storageProvider);
}
}
exports.PantalaimonClient = PantalaimonClient;
//# sourceMappingURL=PantalaimonClient.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PantalaimonClient.js","sourceRoot":"","sources":["../src/PantalaimonClient.ts"],"names":[],"mappings":";;;AAkBA,iDAA8C;AAC9C,6CAA0C;AAE1C,MAAM,wBAAwB,GAAG,0BAA0B,CAAC;AAE5D,gGAAgG;AAEhG;;;GAGG;AACH,MAAa,iBAAiB;IAOC;IAA+B;IAN1D;;;;;OAKG;IACH,YAA2B,aAAqB,EAAU,eAAiC;QAAhE,kBAAa,GAAb,aAAa,CAAQ;QAAU,oBAAe,GAAf,eAAe,CAAkB;QACvF,gBAAgB;IACpB,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,2BAA2B,CAAC,QAAgB,EAAE,QAAgB;QACvE,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACpG,IAAI,WAAW,EAAE;YACb,OAAO,IAAI,2BAAY,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;SAClF;QAED,MAAM,IAAI,GAAG,IAAI,uBAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAElE,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,wBAAwB,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC;QAE3G,iFAAiF;QACjF,OAAO,IAAI,2BAAY,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAChG,CAAC;CACJ;AAhCD,8CAgCC"}

View File

@@ -0,0 +1,366 @@
import { MatrixClient } from "./MatrixClient";
/**
* Information about a user on Synapse.
* @category Admin APIs
*/
export interface SynapseUser {
/***
* The display name of the user, if set.
*/
displayname?: string;
/**
* External IDs for the user.
*/
external_ids?: {
auth_provider: string;
external_id: string;
}[];
/**
* A set of 3PIDs for the user.
*/
threepids?: {
medium: string;
address: string;
}[];
/**
* The avatar URL (usually MXC URI) for the user, if set.
*/
avatar_url?: string;
/**
* Whether or not the user is a Synapse administrator.
*/
admin?: boolean;
/**
* Whether or not the user is deactivated.
*/
deactivated?: boolean;
/**
* Whether or not the user can log in. Available since Synapse v1.93.0
*/
locked?: boolean;
}
/**
* Added information to include when updating/creating a user.
* @category Admin APIs
*/
export interface SynapseUserProperties extends SynapseUser {
/**
* The password for the user. Leave undefined to leave unchanged.
*/
password?: string;
}
/**
* Information about a user on Synapse.
* @category Admin APIs
*/
export interface SynapseUserListing {
/**
* User ID.
*/
name: string;
/**
* Whether or not the user is a guest. 1 is true, 0 is false.
*/
is_guest: number;
/**
* Whether or not the user is an admin. 1 is true, 0 is false.
*/
admin: number;
/**
* Whether or not the user is deactivated. 1 is true, 0 is false.
*/
deactivated: number;
/**
* The type of user, if relevant.
*/
user_type: string | null;
/**
* The hash of the user's password, if relevant.
*/
password_hash: string | null;
/**
* The display name of the user, if set.
*/
displayname: string | null;
/**
* The avatar for the user, if set.
*/
avatar_url: string | null;
}
/**
* A resulting list of users on Synapse.
* @category Admin APIs
*/
export interface SynapseUserList {
/**
* A set of users matching the criteria.
*/
users: SynapseUserListing[];
/**
* The token to use to get the next set of users.
*/
next_token: string;
/**
* The total number of users on the Synapse instance.
*/
total: number;
}
/**
* A registration token on Synapse
* @category Admin APIs
*/
export interface SynapseRegistrationToken {
token: string;
uses_allowed: null | number;
pending: number;
completed: number;
expiry_time: null | number;
}
export interface SynapseRegistrationTokenUpdateOptions {
/**
* The integer number of times the token can be used to complete a registration before it becomes invalid.
* If null the token will have an unlimited number of uses.
* Default: unlimited uses.
*/
uses_allowed?: number | null;
/**
* The latest time the token is valid. Given as the number of milliseconds since 1970-01-01 00:00:00 UTC (the start of the Unix epoch).
* If null the token will not expire.
* Default: token does not expire.
*/
expiry_time?: number | null;
}
export interface SynapseRegistrationTokenOptions extends SynapseRegistrationTokenUpdateOptions {
/**
* The registration token. A string of no more than 64 characters that consists only of characters matched by the regex [A-Za-z0-9._~-].
* Default: randomly generated.
*/
token?: string;
/**
* The length of the token randomly generated if token is not specified. Must be between 1 and 64 inclusive.
* Default: 16.
*/
length?: number;
}
/**
* Information about a room on Synapse.
* @category Admin APIs
*/
export interface SynapseRoomListing {
room_id: string;
name: string;
canonical_alias: string;
joined_members: number;
joined_local_members: number;
version: string;
creator: string;
encryption: string;
federatable: boolean;
public: boolean;
join_rules: string;
guest_access: string;
history_visibility: string;
state_events: number;
}
/**
* A resulting list of rooms on Synapse.
* @category Admin APIs
*/
export interface SynapseRoomList {
rooms: SynapseRoomListing[];
offset: string;
total_rooms: number;
next_batch: string;
prev_batch: string;
}
/**
* Available properties on a Synapse room listing to order by.
* @category Admin APIs
*/
export declare enum SynapseRoomProperty {
Name = "name",
CanonicalAlias = "canonical_alias",
JoinedMembers = "joined_members",
JoinedLocalMembers = "joined_local_members",
Version = "version",
Creator = "creator",
Encryption = "encryption",
CanFederate = "federatable",
IsPublic = "public",
JoinRules = "join_rules",
GuestAccess = "guest_access",
HistoryVisibility = "history_visibility",
NumStateEvents = "state_events"
}
export interface SynapseListUserOptions {
/**
* Filters to only return users with user IDs that contain this value. This parameter is ignored when using the name parameter.
*/
user_id?: string;
/**
* Filters to only return users with user ID localparts or displaynames that contain this value.
*/
name?: string;
/**
* If false will exclude guest users. Defaults to true to include guest users.
*/
guests?: boolean;
/**
* If true will include deactivated users. Defaults to false to exclude deactivated users.
*/
deactivated?: boolean;
/**
* The method by which to sort the returned list of users. If the ordered field has duplicates, the
* second order is always by ascending name, which guarantees a stable ordering.
* **Caution**: The database only has indexes on the columns `name` and `creation_ts`. This means
* that if a different sort order is used, it can cause a large load on the database.
*/
order_by?: "name" | "is_guest" | "admin" | "user_type" | "deactivated" | "shadow_banned" | "displayname" | "avatar_url" | "creation_ts";
/**
* The number of results to return at a time.
*/
limit?: number;
}
/**
* Access to various administrative APIs specifically available in Synapse.
* @category Admin APIs
*/
export declare class SynapseAdminApis {
private client;
constructor(client: MatrixClient);
/**
* Get information about a user. The client making the request must be an admin user.
* @param {string} userId The user ID to check.
* @returns {Promise<SynapseUser>} The resulting Synapse user record
*/
getUser(userId: string): Promise<SynapseUser>;
/**
* Create or update a given user on a Synapse server. The
* client making the request must be an admin user.
* @param {string} userId The user ID to check.
* @param {SynapseUserProperties} opts Options to set when creating or updating the user.
* @returns {Promise<SynapseUser>} The resulting Synapse user record
*/
upsertUser(userId: string, opts?: SynapseUserProperties): Promise<SynapseUser>;
/**
* Get a list of users registered with Synapse, optionally filtered by some criteria. The
* client making the request must be an admin user.
* @param {string} from The token to continue listing users from.
* @param {number} limit The maximum number of users to request.
* @param {string} name Optional localpart or display name filter for results.
* @param {boolean} guests Whether or not to include guest accounts. Default true.
* @param {boolean} deactivated Whether or not to include deactivated accounts. Default false.
* @returns {Promise<SynapseUserList>} A batch of user results.
*/
listUsers(from?: string, limit?: number, name?: string, guests?: boolean, deactivated?: boolean): Promise<SynapseUserList>;
/**
* Get a list of all users registered with Synapse, optionally filtered by some criteria. The
* client making the request must be an admin user.
*
* This method returns an async generator that can be used to filter results.
* @param options Options to pass to the user listing function
* @example
* for await (const user of synapseAdminApis.listAllUsers()) {
* if (user.name === '@alice:example.com') {
* return user;
* }
* }
*/
listAllUsers(options?: SynapseListUserOptions): AsyncGenerator<SynapseUserListing>;
/**
* Determines if the given user is a Synapse server administrator for this homeserver. The
* client making the request must be an admin user themselves (check with `isSelfAdmin`)
* @param {string} userId The user ID to check.
* @returns {Promise<boolean>} Resolves to true if the user is an admin, false otherwise.
* Throws if there's an error.
*/
isAdmin(userId: string): Promise<boolean>;
/**
* Determines if the current user is an admin for the Synapse homeserver.
* @returns {Promise<boolean>} Resolve to true if the user is an admin, false otherwise.
* Throws if there's an error.
*/
isSelfAdmin(): Promise<boolean>;
/**
* Lists the rooms on the server.
* @param {string} searchTerm A term to search for in the room names
* @param {string} from A previous batch token to search from
* @param {number} limit The maximum number of rooms to return
* @param {SynapseRoomProperty} orderBy A property of rooms to order by
* @param {boolean} reverseOrder True to reverse the orderBy direction.
* @returns {Promise<SynapseRoomList>} Resolves to the server's rooms, ordered and filtered.
*/
listRooms(searchTerm?: string, from?: string, limit?: number, orderBy?: SynapseRoomProperty, reverseOrder?: boolean): Promise<SynapseRoomList>;
/**
* Gets a list of state events in a room.
* @param {string} roomId The room ID to get state for.
* @returns {Promise<any[]>} Resolves to the room's state events.
*/
getRoomState(roomId: string): Promise<any[]>;
/**
* Deletes a room from the server, purging all record of it.
* @param {string} roomId The room to delete.
* @returns {Promise} Resolves when complete.
*/
deleteRoom(roomId: string): Promise<void>;
/**
* Gets the status of all active deletion tasks, and all those completed in the last 24h, for the given room_id.
* @param {string} roomId The room ID to get deletion state for.
* @returns {Promise<any[]>} Resolves to the room's deletion status results.
*/
getDeleteRoomState(roomId: string): Promise<any[]>;
/**
* List all registration tokens on the homeserver.
* @param valid If true, only valid tokens are returned.
* If false, only tokens that have expired or have had all uses exhausted are returned.
* If omitted, all tokens are returned regardless of validity.
* @returns An array of registration tokens.
*/
listRegistrationTokens(valid?: boolean): Promise<SynapseRegistrationToken[]>;
/**
* Get details about a single token.
* @param token The token to fetch.
* @returns A registration tokens, or null if not found.
*/
getRegistrationToken(token: string): Promise<SynapseRegistrationToken | null>;
/**
* Create a new registration token.
* @param options Options to pass to the request.
* @returns The newly created token.
*/
createRegistrationToken(options?: SynapseRegistrationTokenOptions): Promise<SynapseRegistrationToken>;
/**
* Update an existing registration token.
* @param token The token to update.
* @param options Options to pass to the request.
* @returns The newly created token.
*/
updateRegistrationToken(token: string, options: SynapseRegistrationTokenUpdateOptions): Promise<SynapseRegistrationToken>;
/**
* Delete a registration token
* @param token The token to update.
* @returns A promise that resolves upon success.
*/
deleteRegistrationToken(token: string): Promise<void>;
/**
* Grants another user the highest power available to a local user who is in the room.
* If the user is not in the room, and it is not publicly joinable, then invite the user.
* @param roomId The room to make the user admin in.
* @param userId The user to make admin in the room. If undefined, it uses the authenticated user.
* @returns Resolves when complete.
*/
makeRoomAdmin(roomId: string, userId?: string): Promise<void>;
/**
* Get the nearest event to a given timestamp, either forwards or backwards. You do not
* need to be joined to the room to retrieve this information.
* @param roomId The room ID to get the context in.
* @param ts The event ID to get the context of.
* @param dir The maximum number of events to return on either side of the event.
* @returns The ID and origin server timestamp of the event.
*/
getEventNearestToTimestamp(roomId: string, ts: number, dir: "f" | "b"): Promise<{
event_id: string;
origin_server_ts: number;
}>;
}

View File

@@ -0,0 +1,256 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SynapseAdminApis = exports.SynapseRoomProperty = void 0;
const MatrixError_1 = require("./models/MatrixError");
/**
* Available properties on a Synapse room listing to order by.
* @category Admin APIs
*/
var SynapseRoomProperty;
(function (SynapseRoomProperty) {
SynapseRoomProperty["Name"] = "name";
SynapseRoomProperty["CanonicalAlias"] = "canonical_alias";
SynapseRoomProperty["JoinedMembers"] = "joined_members";
SynapseRoomProperty["JoinedLocalMembers"] = "joined_local_members";
SynapseRoomProperty["Version"] = "version";
SynapseRoomProperty["Creator"] = "creator";
SynapseRoomProperty["Encryption"] = "encryption";
SynapseRoomProperty["CanFederate"] = "federatable";
SynapseRoomProperty["IsPublic"] = "public";
SynapseRoomProperty["JoinRules"] = "join_rules";
SynapseRoomProperty["GuestAccess"] = "guest_access";
SynapseRoomProperty["HistoryVisibility"] = "history_visibility";
SynapseRoomProperty["NumStateEvents"] = "state_events";
})(SynapseRoomProperty || (exports.SynapseRoomProperty = SynapseRoomProperty = {}));
/**
* Access to various administrative APIs specifically available in Synapse.
* @category Admin APIs
*/
class SynapseAdminApis {
client;
constructor(client) {
this.client = client;
}
/**
* Get information about a user. The client making the request must be an admin user.
* @param {string} userId The user ID to check.
* @returns {Promise<SynapseUser>} The resulting Synapse user record
*/
async getUser(userId) {
return this.client.doRequest("GET", "/_synapse/admin/v2/users/" + encodeURIComponent(userId));
}
/**
* Create or update a given user on a Synapse server. The
* client making the request must be an admin user.
* @param {string} userId The user ID to check.
* @param {SynapseUserProperties} opts Options to set when creating or updating the user.
* @returns {Promise<SynapseUser>} The resulting Synapse user record
*/
async upsertUser(userId, opts = {}) {
return this.client.doRequest("PUT", "/_synapse/admin/v2/users/" + encodeURIComponent(userId), undefined, opts);
}
/**
* Get a list of users registered with Synapse, optionally filtered by some criteria. The
* client making the request must be an admin user.
* @param {string} from The token to continue listing users from.
* @param {number} limit The maximum number of users to request.
* @param {string} name Optional localpart or display name filter for results.
* @param {boolean} guests Whether or not to include guest accounts. Default true.
* @param {boolean} deactivated Whether or not to include deactivated accounts. Default false.
* @returns {Promise<SynapseUserList>} A batch of user results.
*/
async listUsers(from, limit, name, guests = true, deactivated = false) {
const qs = { guests, deactivated };
if (from)
qs['from'] = from;
if (limit)
qs['limit'] = limit;
if (name)
qs['name'] = name;
return this.client.doRequest("GET", "/_synapse/admin/v2/users", qs);
}
/**
* Get a list of all users registered with Synapse, optionally filtered by some criteria. The
* client making the request must be an admin user.
*
* This method returns an async generator that can be used to filter results.
* @param options Options to pass to the user listing function
* @example
* for await (const user of synapseAdminApis.listAllUsers()) {
* if (user.name === '@alice:example.com') {
* return user;
* }
* }
*/
async *listAllUsers(options = {}) {
let from = undefined;
let response;
do {
const qs = {
...options,
...(from && { from }),
};
response = await this.client.doRequest("GET", "/_synapse/admin/v2/users", qs);
for (const user of response.users) {
yield user;
}
from = response.next_token;
} while (from);
}
/**
* Determines if the given user is a Synapse server administrator for this homeserver. The
* client making the request must be an admin user themselves (check with `isSelfAdmin`)
* @param {string} userId The user ID to check.
* @returns {Promise<boolean>} Resolves to true if the user is an admin, false otherwise.
* Throws if there's an error.
*/
async isAdmin(userId) {
const response = await this.client.doRequest("GET", `/_synapse/admin/v1/users/${encodeURIComponent(userId)}/admin`);
return response['admin'] || false;
}
/**
* Determines if the current user is an admin for the Synapse homeserver.
* @returns {Promise<boolean>} Resolve to true if the user is an admin, false otherwise.
* Throws if there's an error.
*/
async isSelfAdmin() {
try {
return await this.isAdmin(await this.client.getUserId());
}
catch (err) {
if (err instanceof MatrixError_1.MatrixError && err.errcode === 'M_FORBIDDEN') {
return false;
}
throw err;
}
}
/**
* Lists the rooms on the server.
* @param {string} searchTerm A term to search for in the room names
* @param {string} from A previous batch token to search from
* @param {number} limit The maximum number of rooms to return
* @param {SynapseRoomProperty} orderBy A property of rooms to order by
* @param {boolean} reverseOrder True to reverse the orderBy direction.
* @returns {Promise<SynapseRoomList>} Resolves to the server's rooms, ordered and filtered.
*/
async listRooms(searchTerm, from, limit, orderBy, reverseOrder = false) {
const params = {};
if (from)
params['from'] = from;
if (limit)
params['limit'] = limit;
if (searchTerm)
params['search_term'] = searchTerm;
if (orderBy)
params['order_by'] = orderBy;
if (reverseOrder) {
params['dir'] = 'b';
}
else {
params['dir'] = 'f';
}
return this.client.doRequest("GET", "/_synapse/admin/v1/rooms", params);
}
/**
* Gets a list of state events in a room.
* @param {string} roomId The room ID to get state for.
* @returns {Promise<any[]>} Resolves to the room's state events.
*/
async getRoomState(roomId) {
const r = await this.client.doRequest("GET", `/_synapse/admin/v1/rooms/${encodeURIComponent(roomId)}/state`);
return r?.['state'] || [];
}
/**
* Deletes a room from the server, purging all record of it.
* @param {string} roomId The room to delete.
* @returns {Promise} Resolves when complete.
*/
async deleteRoom(roomId) {
return this.client.doRequest("DELETE", `/_synapse/admin/v2/rooms/${encodeURIComponent(roomId)}`, {}, { purge: true });
}
/**
* Gets the status of all active deletion tasks, and all those completed in the last 24h, for the given room_id.
* @param {string} roomId The room ID to get deletion state for.
* @returns {Promise<any[]>} Resolves to the room's deletion status results.
*/
async getDeleteRoomState(roomId) {
const r = await this.client.doRequest("GET", `/_synapse/admin/v2/rooms/${encodeURIComponent(roomId)}/delete_status`);
return r?.['results'] || [];
}
/**
* List all registration tokens on the homeserver.
* @param valid If true, only valid tokens are returned.
* If false, only tokens that have expired or have had all uses exhausted are returned.
* If omitted, all tokens are returned regardless of validity.
* @returns An array of registration tokens.
*/
async listRegistrationTokens(valid) {
const res = await this.client.doRequest("GET", `/_synapse/admin/v1/registration_tokens`, { valid });
return res.registration_tokens;
}
/**
* Get details about a single token.
* @param token The token to fetch.
* @returns A registration tokens, or null if not found.
*/
async getRegistrationToken(token) {
try {
return await this.client.doRequest("GET", `/_synapse/admin/v1/registration_tokens/${encodeURIComponent(token)}`);
}
catch (e) {
if (e?.statusCode === 404) {
return null;
}
throw e;
}
}
/**
* Create a new registration token.
* @param options Options to pass to the request.
* @returns The newly created token.
*/
async createRegistrationToken(options = {}) {
return this.client.doRequest("POST", `/_synapse/admin/v1/registration_tokens/new`, undefined, options);
}
/**
* Update an existing registration token.
* @param token The token to update.
* @param options Options to pass to the request.
* @returns The newly created token.
*/
async updateRegistrationToken(token, options) {
return this.client.doRequest("PUT", `/_synapse/admin/v1/registration_tokens/${encodeURIComponent(token)}`, undefined, options);
}
/**
* Delete a registration token
* @param token The token to update.
* @returns A promise that resolves upon success.
*/
async deleteRegistrationToken(token) {
return this.client.doRequest("DELETE", `/_synapse/admin/v1/registration_tokens/${encodeURIComponent(token)}`, undefined, {});
}
/**
* Grants another user the highest power available to a local user who is in the room.
* If the user is not in the room, and it is not publicly joinable, then invite the user.
* @param roomId The room to make the user admin in.
* @param userId The user to make admin in the room. If undefined, it uses the authenticated user.
* @returns Resolves when complete.
*/
async makeRoomAdmin(roomId, userId) {
return this.client.doRequest("POST", `/_synapse/admin/v1/rooms/${encodeURIComponent(roomId)}/make_room_admin`, {}, { user_id: userId });
}
/**
* Get the nearest event to a given timestamp, either forwards or backwards. You do not
* need to be joined to the room to retrieve this information.
* @param roomId The room ID to get the context in.
* @param ts The event ID to get the context of.
* @param dir The maximum number of events to return on either side of the event.
* @returns The ID and origin server timestamp of the event.
*/
async getEventNearestToTimestamp(roomId, ts, dir) {
return await this.client.doRequest("GET", "/_synapse/admin/v1/rooms/" + encodeURIComponent(roomId) + "/timestamp_to_event", { ts, dir });
}
}
exports.SynapseAdminApis = SynapseAdminApis;
//# sourceMappingURL=SynapseAdminApis.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SynapseAdminApis.js","sourceRoot":"","sources":["../src/SynapseAdminApis.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,82 @@
import { MatrixClient } from "./MatrixClient";
/**
* A MatrixClient class that handles events in sync for the /sync loop, instead
* of trying to push its way through the /sync loop as fast as possible. It is
* intended that the consumer extend this class and override the onWhatever()
* functions it needs. All of the onWhatever() functions have a default behaviour
* of doing nothing.
*/
export declare abstract class SynchronousMatrixClient extends MatrixClient {
/**
* Creates a new SynchronousMatrixClient. Note that this accepts a MatrixClient, though
* much of the class's properties are not brought over. Always convert your MatrixClient
* instance to a SynchronousMatrixClient as soon as possible to avoid diversion in which
* properties are proxied over.
* @param {MatrixClient} baseClient The client to wrap.
*/
protected constructor(baseClient: MatrixClient);
private handleEvent;
protected startSyncInternal(): Promise<any>;
/**
* Handles the `account_data` event raised by the client.
* @param {any} event The account data event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onAccountData(event: any): Promise<any>;
/**
* Handles the `room.account_data` event raised by the client.
* @param {string} roomId The Room ID the account data applies to.
* @param {any} event The room account data event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomAccountData(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.leave` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomLeave(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.invite` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomInvite(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.join` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomJoin(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.message` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomMessage(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.archived` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomArchived(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.upgraded` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomUpgraded(roomId: string, event: any): Promise<any>;
/**
* Handles the `room.event` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
protected onRoomEvent(roomId: string, event: any): Promise<any>;
}

View File

@@ -0,0 +1,130 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SynchronousMatrixClient = void 0;
const MatrixClient_1 = require("./MatrixClient");
/**
* A MatrixClient class that handles events in sync for the /sync loop, instead
* of trying to push its way through the /sync loop as fast as possible. It is
* intended that the consumer extend this class and override the onWhatever()
* functions it needs. All of the onWhatever() functions have a default behaviour
* of doing nothing.
*/
class SynchronousMatrixClient extends MatrixClient_1.MatrixClient {
/**
* Creates a new SynchronousMatrixClient. Note that this accepts a MatrixClient, though
* much of the class's properties are not brought over. Always convert your MatrixClient
* instance to a SynchronousMatrixClient as soon as possible to avoid diversion in which
* properties are proxied over.
* @param {MatrixClient} baseClient The client to wrap.
*/
constructor(baseClient) {
super(baseClient.homeserverUrl, baseClient.accessToken, baseClient.storageProvider);
}
async handleEvent(emitType, arg1, arg2) {
if (emitType === 'account_data')
await this.onAccountData(arg1);
if (emitType === 'room.account_data')
await this.onRoomAccountData(arg1, arg2);
if (emitType === 'room.leave')
await this.onRoomLeave(arg1, arg2);
if (emitType === 'room.invite')
await this.onRoomInvite(arg1, arg2);
if (emitType === 'room.join')
await this.onRoomJoin(arg1, arg2);
if (emitType === 'room.archived')
await this.onRoomArchived(arg1, arg2);
if (emitType === 'room.upgraded')
await this.onRoomUpgraded(arg1, arg2);
if (emitType === 'room.message')
await this.onRoomMessage(arg1, arg2);
if (emitType === 'room.event')
await this.onRoomEvent(arg1, arg2);
// Still emit though for easier support of plugins.
this.emit(emitType, arg1, arg2);
}
startSyncInternal() {
return this.startSync(this.handleEvent.bind(this));
}
/**
* Handles the `account_data` event raised by the client.
* @param {any} event The account data event.
* @returns {Promise<any>} Resolves when complete.
*/
onAccountData(event) {
return;
}
/**
* Handles the `room.account_data` event raised by the client.
* @param {string} roomId The Room ID the account data applies to.
* @param {any} event The room account data event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomAccountData(roomId, event) {
return;
}
/**
* Handles the `room.leave` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomLeave(roomId, event) {
return;
}
/**
* Handles the `room.invite` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomInvite(roomId, event) {
return;
}
/**
* Handles the `room.join` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomJoin(roomId, event) {
return;
}
/**
* Handles the `room.message` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomMessage(roomId, event) {
return;
}
/**
* Handles the `room.archived` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomArchived(roomId, event) {
return;
}
/**
* Handles the `room.upgraded` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomUpgraded(roomId, event) {
return;
}
/**
* Handles the `room.event` event raised by the client.
* @param {string} roomId The Room ID the event happened in.
* @param {any} event The event.
* @returns {Promise<any>} Resolves when complete.
*/
onRoomEvent(roomId, event) {
return;
}
}
exports.SynchronousMatrixClient = SynchronousMatrixClient;
//# sourceMappingURL=SynchronousMatrixClient.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SynchronousMatrixClient.js","sourceRoot":"","sources":["../src/SynchronousMatrixClient.ts"],"names":[],"mappings":";;;AAAA,iDAA8C;AAE9C;;;;;;GAMG;AACH,MAAsB,uBAAwB,SAAQ,2BAAY;IAC9D;;;;;;OAMG;IACH,YAAsB,UAAwB;QAC1C,KAAK,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IACxF,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,IAAS,EAAE,IAAS;QAC5D,IAAI,QAAQ,KAAK,cAAc;YAAE,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,QAAQ,KAAK,mBAAmB;YAAE,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/E,IAAI,QAAQ,KAAK,YAAY;YAAE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClE,IAAI,QAAQ,KAAK,aAAa;YAAE,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpE,IAAI,QAAQ,KAAK,WAAW;YAAE,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChE,IAAI,QAAQ,KAAK,eAAe;YAAE,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxE,IAAI,QAAQ,KAAK,eAAe;YAAE,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxE,IAAI,QAAQ,KAAK,cAAc;YAAE,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtE,IAAI,QAAQ,KAAK,YAAY;YAAE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAElE,mDAAmD;QACnD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAES,iBAAiB;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;;OAIG;IACO,aAAa,CAAC,KAAU;QAC9B,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,iBAAiB,CAAC,MAAc,EAAE,KAAU;QAClD,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,WAAW,CAAC,MAAc,EAAE,KAAU;QAC5C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,YAAY,CAAC,MAAc,EAAE,KAAU;QAC7C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,UAAU,CAAC,MAAc,EAAE,KAAU;QAC3C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,aAAa,CAAC,MAAc,EAAE,KAAU;QAC9C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,cAAc,CAAC,MAAc,EAAE,KAAU;QAC/C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,cAAc,CAAC,MAAc,EAAE,KAAU;QAC/C,OAAO;IACX,CAAC;IAED;;;;;OAKG;IACO,WAAW,CAAC,MAAc,EAAE,KAAU;QAC5C,OAAO;IACX,CAAC;CACJ;AAvHD,0DAuHC"}

View File

@@ -0,0 +1,43 @@
import { MatrixClient } from "./MatrixClient";
import { MSC2380MediaInfo } from "./models/unstable/MediaInfo";
/**
* Unstable APIs that shouldn't be used in most circumstances.
* @category Unstable APIs
*/
export declare class UnstableApis {
private client;
constructor(client: MatrixClient);
/**
* Gets the local room aliases that are published for a given room.
* @param {string} roomId The room ID to get local aliases for.
* @returns {Promise<string[]>} Resolves to the aliases on the room, or an empty array.
* @deprecated Relies on MSC2432 endpoint.
*/
getRoomAliases(roomId: string): Promise<string[]>;
/**
* Adds a reaction to an event. The contract for this function may change in the future.
* @param {string} roomId The room ID to react in
* @param {string} eventId The event ID to react against, in the given room
* @param {string} emoji The emoji to react with
* @returns {Promise<string>} Resolves to the event ID of the reaction
*/
addReactionToEvent(roomId: string, eventId: string, emoji: string): Promise<string>;
/**
* Get relations for a given event.
* @param {string} roomId The room ID to for the given event.
* @param {string} eventId The event ID to list relations for.
* @param {string?} relationType The type of relations (e.g. `m.room.member`) to filter for. Optional.
* @param {string?} eventType The type of event to look for (e.g. `m.room.member`). Optional.
* @returns {Promise<{chunk: any[]}>} Resolves to an object containing the chunk of relations
* @deprecated Please use the function of the same name in MatrixClient. This will be removed in a future release.
*/
getRelationsForEvent(roomId: string, eventId: string, relationType?: string, eventType?: string): Promise<{
chunk: any[];
}>;
/**
* Get information about a media item. Implements MSC2380
* @param {string} mxcUrl The MXC to get information about.
* @returns {Promise<MSC2380MediaInfo>} Resolves to an object containing the media information.
*/
getMediaInfo(mxcUrl: string): Promise<MSC2380MediaInfo>;
}

View File

@@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnstableApis = void 0;
/**
* Unstable APIs that shouldn't be used in most circumstances.
* @category Unstable APIs
*/
class UnstableApis {
client;
constructor(client) {
this.client = client;
}
/**
* Gets the local room aliases that are published for a given room.
* @param {string} roomId The room ID to get local aliases for.
* @returns {Promise<string[]>} Resolves to the aliases on the room, or an empty array.
* @deprecated Relies on MSC2432 endpoint.
*/
async getRoomAliases(roomId) {
const r = await this.client.doRequest("GET", "/_matrix/client/unstable/org.matrix.msc2432/rooms/" + encodeURIComponent(roomId) + "/aliases");
return r['aliases'] || [];
}
/**
* Adds a reaction to an event. The contract for this function may change in the future.
* @param {string} roomId The room ID to react in
* @param {string} eventId The event ID to react against, in the given room
* @param {string} emoji The emoji to react with
* @returns {Promise<string>} Resolves to the event ID of the reaction
*/
async addReactionToEvent(roomId, eventId, emoji) {
return this.client.sendRawEvent(roomId, "m.reaction", {
"m.relates_to": {
event_id: eventId,
key: emoji,
rel_type: "m.annotation",
},
});
}
/**
* Get relations for a given event.
* @param {string} roomId The room ID to for the given event.
* @param {string} eventId The event ID to list relations for.
* @param {string?} relationType The type of relations (e.g. `m.room.member`) to filter for. Optional.
* @param {string?} eventType The type of event to look for (e.g. `m.room.member`). Optional.
* @returns {Promise<{chunk: any[]}>} Resolves to an object containing the chunk of relations
* @deprecated Please use the function of the same name in MatrixClient. This will be removed in a future release.
*/
async getRelationsForEvent(roomId, eventId, relationType, eventType) {
let url = `/_matrix/client/unstable/rooms/${encodeURIComponent(roomId)}/relations/${encodeURIComponent(eventId)}`;
if (relationType) {
url += `/${relationType}`;
}
if (eventType) {
url += `/${eventType}`;
}
return this.client.doRequest("GET", url);
}
/**
* Get information about a media item. Implements MSC2380
* @param {string} mxcUrl The MXC to get information about.
* @returns {Promise<MSC2380MediaInfo>} Resolves to an object containing the media information.
*/
async getMediaInfo(mxcUrl) {
if (!mxcUrl.toLowerCase().startsWith("mxc://")) {
throw Error("'mxcUrl' does not begin with mxc://");
}
const [domain, mediaId] = mxcUrl.substring("mxc://".length).split("/");
if (!domain || !mediaId) {
throw Error('Missing domain or media ID');
}
return this.client.doRequest("GET", `/_matrix/media/unstable/info/${encodeURIComponent(domain)}/${encodeURIComponent(mediaId)}`);
}
}
exports.UnstableApis = UnstableApis;
//# sourceMappingURL=UnstableApis.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"UnstableApis.js","sourceRoot":"","sources":["../src/UnstableApis.ts"],"names":[],"mappings":";;;AAGA;;;GAGG;AACH,MAAa,YAAY;IACD;IAApB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IACxC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,cAAc,CAAC,MAAc;QACtC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,oDAAoD,GAAG,kBAAkB,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC;QAC7I,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,OAAe,EAAE,KAAa;QAC1E,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE;YAClD,cAAc,EAAE;gBACZ,QAAQ,EAAE,OAAO;gBACjB,GAAG,EAAE,KAAK;gBACV,QAAQ,EAAE,cAAc;aAC3B;SACJ,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,oBAAoB,CAAC,MAAc,EAAE,OAAe,EAAE,YAAqB,EAAE,SAAkB;QACxG,IAAI,GAAG,GAAG,kCAAkC,kBAAkB,CAAC,MAAM,CAAC,cAAc,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAClH,IAAI,YAAY,EAAE;YACd,GAAG,IAAI,IAAI,YAAY,EAAE,CAAC;SAC7B;QACD,IAAI,SAAS,EAAE;YACX,GAAG,IAAI,IAAI,SAAS,EAAE,CAAC;SAC1B;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,MAAc;QACpC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;YAC5C,MAAM,KAAK,CAAC,qCAAqC,CAAC,CAAC;SACtD;QACD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE;YACrB,MAAM,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC7C;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,gCAAgC,kBAAkB,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrI,CAAC;CACJ;AAnED,oCAmEC"}

View File

@@ -0,0 +1,343 @@
/// <reference types="node" />
import { EventEmitter } from "events";
import { Intent } from "./Intent";
import { IAppserviceCryptoStorageProvider, IAppserviceStorageProvider, IJoinRoomStrategy, IPreprocessor, MatrixClient, Metrics } from "..";
import { MatrixBridge } from "./MatrixBridge";
/**
* Represents an application service's registration file. This is expected to be
* loaded from another source, such as a YAML file.
* @category Application services
*/
export interface IAppserviceRegistration {
/**
* Optional ID for the appplication service. Used by homeservers to track which application
* service registers what.
*/
id?: string;
/**
* Optional URL at which the application service can be contacted.
*/
url?: string;
/**
* The token the application service uses to communicate with the homeserver.
*/
as_token: string;
/**
* The token the homeserver uses to communicate with the application service.
*/
hs_token: string;
/**
* The application service's own localpart (eg: "_irc_bot" in the user ID "@_irc_bot:domain.com")
*/
sender_localpart: string;
/**
* The various namespaces the application service can support.
*/
namespaces: {
/**
* The user namespaces the application service is requesting.
*/
users: {
/**
* Whether or not the application service holds an exclusive lock on the namespace. This
* means that no other user on the homeserver may register users that match this namespace.
*/
exclusive: boolean;
/**
* The regular expression that the homeserver uses to determine if a user is in this namespace.
*/
regex: string;
}[];
/**
* The room namespaces the application service is requesting. This is not for alises.
*/
rooms: {
/**
* Whether or not the application service holds an exclusive lock on the namespace.
*/
exclusive: boolean;
/**
* The regular expression that the homeserver uses to determine if a user is in this namespace.
*/
regex: string;
}[];
/**
* The room alias namespaces the application service is requesting.
*/
aliases: {
/**
* Whether or not the application service holds an exclusive lock on the namespace. This means
* that no other user on the homeserver may register aliases that match this namespace.
*/
exclusive: boolean;
/**
* The regular expression that the homeserver uses to determine if an alias is in this namespace.
*/
regex: string;
}[];
};
/**
* The protocols the application service supports. Optional.
*/
protocols?: string[];
/**
* If the application service is rate limited by the homeserver. Optional.
*/
rate_limited?: boolean;
/**
* **Experimental**
*
* Should the application service receive ephemeral events from the homeserver. Optional.
* @see https://github.com/matrix-org/matrix-doc/pull/2409
*/
"de.sorunome.msc2409.push_ephemeral"?: boolean;
}
/**
* General options for the application service
* @category Application services
*/
export interface IAppserviceOptions {
/**
* The port to listen for requests from the homeserver on.
*/
port: number;
/**
* The bind address to listen for requests on.
*/
bindAddress: string;
/**
* The name of the homeserver, as presented over federation (eg: "matrix.org")
*/
homeserverName: string;
/**
* The URL to the homeserver's client server API (eg: "https://matrix.org")
*/
homeserverUrl: string;
/**
* The storage provider to use for this application service.
*/
storage?: IAppserviceStorageProvider;
/**
* The storage provider to use for setting up encryption. Encryption will be
* disabled for all intents and the appservice if not configured.
*/
cryptoStorage?: IAppserviceCryptoStorageProvider;
/**
* The registration for this application service.
*/
registration: IAppserviceRegistration;
/**
* The join strategy to use for all intents, if any.
*/
joinStrategy?: IJoinRoomStrategy;
/**
* Options for how Intents are handled.
*/
intentOptions?: {
/**
* The maximum number of intents to keep cached. Defaults to 10 thousand.
*/
maxCached?: number;
/**
* The maximum age in milliseconds to keep an Intent around for, provided
* the maximum number of intents has been reached. Defaults to 60 minutes.
*/
maxAgeMs?: number;
/**
* If false (default), crypto will not be automatically set up for all intent
* instances - it will need to be manually enabled with
* `await intent.enableEncryption()`.
*
* If true, crypto will be automatically set up.
*
* Note that the appservice bot account is considered an intent.
*/
encryption?: boolean;
/**
* Enable the content scanner API when creating new intents. This means
* that all media requests will be proxied through the scanner.
*/
enableContentScanner?: boolean;
};
}
/**
* Represents an application service. This provides helper utilities such as tracking
* of user intents (clients that are aware of their membership in rooms).
* @category Application services
*/
export declare class Appservice extends EventEmitter {
private options;
/**
* The metrics instance for this appservice. This will raise all metrics
* from this appservice instance as well as any intents/MatrixClients created
* by the appservice.
*/
readonly metrics: Metrics;
private readonly userPrefix;
private readonly aliasPrefix;
private readonly registration;
private readonly storage;
private readonly cryptoStorage;
private readonly bridgeInstance;
private pingRequest?;
private app;
private appServer;
private intentsCache;
private eventProcessors;
private pendingTransactions;
/**
* A cache of intents for the purposes of decrypting rooms
*/
private cryptoClientForRoomId;
/**
* Creates a new application service.
* @param {IAppserviceOptions} options The options for the application service.
*/
constructor(options: IAppserviceOptions);
/**
* Gets the express app instance which is serving requests. Not recommended for
* general usage, but may be used to append routes to the web server.
*/
get expressAppInstance(): import("express-serve-static-core").Express;
/**
* Gets the bridge-specific APIs for this application service.
*/
get bridge(): MatrixBridge;
/**
* Get the application service's "bot" user ID (the sender_localpart).
*/
get botUserId(): string;
/**
* Get the application service's "bot" Intent (the sender_localpart).
* @returns {Intent} The intent for the application service itself.
*/
get botIntent(): Intent;
/**
* Get the application service's "bot" MatrixClient (the sender_localpart).
* Normally the botIntent should be used to ensure that the bot user is safely
* handled.
* @returns {MatrixClient} The client for the application service itself.
*/
get botClient(): MatrixClient;
/**
* Starts the application service, opening the bind address to begin processing requests.
* @returns {Promise<void>} resolves when started
*/
begin(): Promise<void>;
/**
* Stops the application service, freeing the web server.
*/
stop(): void;
/**
* Gets an intent for a given localpart. The user ID will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get an Intent for.
* @returns {Intent} An Intent for the user.
*/
getIntent(localpart: string): Intent;
/**
* Gets a full user ID for a given localpart. The user ID will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get a user ID for.
* @returns {string} The user's ID.
*/
getUserId(localpart: string): string;
/**
* Gets an Intent for a given user suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The user's suffix
* @returns {Intent} An Intent for the user.
*/
getIntentForSuffix(suffix: string): Intent;
/**
* Gets a full user ID for a given suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The user's suffix
* @returns {string} The user's ID.
*/
getUserIdForSuffix(suffix: string): string;
/**
* Gets an Intent for a given user ID.
* @param {string} userId The user ID to get an Intent for.
* @returns {Intent} An Intent for the user.
*/
getIntentForUserId(userId: string): Intent;
/**
* Gets the suffix for the provided user ID. If the user ID is not a namespaced
* user, this will return a falsey value.
* @param {string} userId The user ID to parse
* @returns {string} The suffix from the user ID.
*/
getSuffixForUserId(userId: string): string;
/**
* Determines if a given user ID is namespaced by this application service.
* @param {string} userId The user ID to check
* @returns {boolean} true if the user is namespaced, false otherwise
*/
isNamespacedUser(userId: string): boolean;
/**
* Gets a full alias for a given localpart. The alias will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get an alias for.
* @returns {string} The alias.
*/
getAlias(localpart: string): string;
/**
* Gets a full alias for a given suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The alias's suffix
* @returns {string} The alias.
*/
getAliasForSuffix(suffix: string): string;
/**
* Gets the localpart of an alias for a given suffix. The prefix is automatically detected from the registration
* options. Useful for the createRoom endpoint.
* @param suffix The alias's suffix
* @returns {string} The alias localpart.
*/
getAliasLocalpartForSuffix(suffix: string): string;
/**
* Gets the suffix for the provided alias. If the alias is not a namespaced
* alias, this will return a falsey value.
* @param {string} alias The alias to parse
* @returns {string} The suffix from the alias.
*/
getSuffixForAlias(alias: string): string;
/**
* Determines if a given alias is namespaced by this application service.
* @param {string} alias The alias to check
* @returns {boolean} true if the alias is namespaced, false otherwise
*/
isNamespacedAlias(alias: string): boolean;
/**
* Adds a preprocessor to the event pipeline. When this appservice encounters an event, it
* will try to run it through the preprocessors it can in the order they were added.
* @param {IPreprocessor} preprocessor the preprocessor to add
*/
addPreprocessor(preprocessor: IPreprocessor): void;
/**
* Sets the visibility of a room in the appservice's room directory.
* @param {string} networkId The network ID to group the room under.
* @param {string} roomId The room ID to manipulate the visibility of.
* @param {"public" | "private"} visibility The visibility to set for the room.
* @return {Promise<any>} resolves when the visibility has been updated.
*/
setRoomDirectoryVisibility(networkId: string, roomId: string, visibility: "public" | "private"): Promise<any>;
pingHomeserver(): Promise<any>;
private processEphemeralEvent;
private processEvent;
private processMembershipEvent;
private isAuthed;
private decryptAppserviceEvent;
private handleTransaction;
private onTransaction;
private onUser;
private onRoomAlias;
private onKeysClaim;
private onKeysQuery;
private onThirdpartyProtocol;
private handleThirdpartyObject;
private onThirdpartyUser;
private onThirdpartyLocation;
private onPing;
}

View File

@@ -0,0 +1,902 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Appservice = void 0;
const express = require("express");
const events_1 = require("events");
const morgan = require("morgan");
const LRU = require("lru-cache");
const querystring_1 = require("querystring");
const crypto_1 = require("crypto");
const Intent_1 = require("./Intent");
const __1 = require("..");
const MatrixBridge_1 = require("./MatrixBridge");
const EDU_ANNOTATION_KEY = "io.t2bot.sdk.bot.type";
var EduAnnotation;
(function (EduAnnotation) {
EduAnnotation["ToDevice"] = "to_device";
EduAnnotation["Ephemeral"] = "ephemeral";
})(EduAnnotation || (EduAnnotation = {}));
/**
* Represents an application service. This provides helper utilities such as tracking
* of user intents (clients that are aware of their membership in rooms).
* @category Application services
*/
class Appservice extends events_1.EventEmitter {
options;
/**
* The metrics instance for this appservice. This will raise all metrics
* from this appservice instance as well as any intents/MatrixClients created
* by the appservice.
*/
metrics = new __1.Metrics();
userPrefix;
aliasPrefix;
registration;
storage;
cryptoStorage;
bridgeInstance = new MatrixBridge_1.MatrixBridge(this);
pingRequest;
app = express();
appServer;
intentsCache;
eventProcessors = {};
pendingTransactions = new Map();
/**
* A cache of intents for the purposes of decrypting rooms
*/
cryptoClientForRoomId;
/**
* Creates a new application service.
* @param {IAppserviceOptions} options The options for the application service.
*/
constructor(options) {
super();
this.options = options;
options.joinStrategy = new __1.AppserviceJoinRoomStrategy(options.joinStrategy, this);
if (!options.intentOptions)
options.intentOptions = {};
if (options.intentOptions.maxAgeMs === undefined)
options.intentOptions.maxAgeMs = 60 * 60 * 1000;
if (options.intentOptions.maxCached === undefined)
options.intentOptions.maxCached = 10000;
this.intentsCache = new LRU.LRUCache({
max: options.intentOptions.maxCached,
ttl: options.intentOptions.maxAgeMs,
});
this.cryptoClientForRoomId = new LRU.LRUCache({
max: options.intentOptions.maxCached,
ttl: options.intentOptions.maxAgeMs,
});
this.registration = options.registration;
// If protocol is not defined, define an empty array.
if (!this.registration.protocols) {
this.registration.protocols = [];
}
this.storage = options.storage || new __1.MemoryStorageProvider();
options.storage = this.storage;
this.cryptoStorage = options.cryptoStorage;
this.app.use(express.json({ limit: Number.MAX_SAFE_INTEGER })); // disable limits, use a reverse proxy
morgan.token('url-safe', (req) => `${req.path}?${(0, querystring_1.stringify)((0, __1.redactObjectForLogging)(req.query ?? {}))}`);
this.app.use(morgan(
// Same as "combined", but with sensitive values removed from requests.
':remote-addr - :remote-user [:date[clf]] ":method :url-safe HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"', { stream: { write: __1.LogService.info.bind(__1.LogService, 'Appservice') } }));
// ETag headers break the tests sometimes, and we don't actually need them anyways for
// appservices - none of this should be cached.
this.app.set('etag', false);
this.app.get("/users/:userId", this.onUser.bind(this));
this.app.get("/rooms/:roomAlias", this.onRoomAlias.bind(this));
this.app.put("/transactions/:txnId", this.onTransaction.bind(this));
this.app.get("/_matrix/app/v1/users/:userId", this.onUser.bind(this));
this.app.get("/_matrix/app/v1/rooms/:roomAlias", this.onRoomAlias.bind(this));
this.app.put("/_matrix/app/v1/transactions/:txnId", this.onTransaction.bind(this));
this.app.get("/_matrix/app/v1/thirdparty/protocol/:protocol", this.onThirdpartyProtocol.bind(this));
this.app.get("/_matrix/app/v1/thirdparty/user/:protocol", this.onThirdpartyUser.bind(this));
this.app.get("/_matrix/app/v1/thirdparty/user", this.onThirdpartyUser.bind(this));
this.app.get("/_matrix/app/v1/thirdparty/location/:protocol", this.onThirdpartyLocation.bind(this));
this.app.get("/_matrix/app/v1/thirdparty/location", this.onThirdpartyLocation.bind(this));
this.app.post("/_matrix/app/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
this.app.post("/_matrix/app/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
// Workaround for https://github.com/matrix-org/synapse/issues/3780
this.app.post("/_matrix/app/v1/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
this.app.post("/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
this.app.post("/_matrix/app/v1/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
this.app.post("/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
this.app.post("/_matrix/app/v1/ping", this.onPing.bind(this));
// We register the 404 handler in the `begin()` function to allow consumers to add their own endpoints.
if (!this.registration.namespaces || !this.registration.namespaces.users || this.registration.namespaces.users.length === 0) {
throw new Error("No user namespaces in registration");
}
if (this.registration.namespaces.users.length !== 1) {
throw new Error("Too many user namespaces registered: expecting exactly one");
}
const userPrefix = (this.registration.namespaces.users[0].regex || "").split(":")[0];
if (!userPrefix.endsWith(".*") && !userPrefix.endsWith(".+")) {
this.userPrefix = null;
}
else {
this.userPrefix = userPrefix.substring(0, userPrefix.length - 2); // trim off the .* part
}
if (!this.registration.namespaces?.aliases || this.registration.namespaces.aliases.length !== 1) {
this.aliasPrefix = null;
}
else {
this.aliasPrefix = (this.registration.namespaces.aliases[0].regex || "").split(":")[0];
if (!this.aliasPrefix.endsWith(".*") && !this.aliasPrefix.endsWith(".+")) {
this.aliasPrefix = null;
}
else {
this.aliasPrefix = this.aliasPrefix.substring(0, this.aliasPrefix.length - 2); // trim off the .* part
}
}
}
/**
* Gets the express app instance which is serving requests. Not recommended for
* general usage, but may be used to append routes to the web server.
*/
get expressAppInstance() {
return this.app;
}
/**
* Gets the bridge-specific APIs for this application service.
*/
get bridge() {
return this.bridgeInstance;
}
/**
* Get the application service's "bot" user ID (the sender_localpart).
*/
get botUserId() {
return this.getUserId(this.registration.sender_localpart);
}
/**
* Get the application service's "bot" Intent (the sender_localpart).
* @returns {Intent} The intent for the application service itself.
*/
get botIntent() {
return this.getIntentForUserId(this.botUserId);
}
/**
* Get the application service's "bot" MatrixClient (the sender_localpart).
* Normally the botIntent should be used to ensure that the bot user is safely
* handled.
* @returns {MatrixClient} The client for the application service itself.
*/
get botClient() {
return this.botIntent.underlyingClient;
}
/**
* Starts the application service, opening the bind address to begin processing requests.
* @returns {Promise<void>} resolves when started
*/
begin() {
return new Promise((resolve, reject) => {
// Per constructor, all other endpoints should 404.
// Technically, according to https://spec.matrix.org/v1.6/application-service-api/#unknown-routes we should
// be returning 405 for *known* endpoints with the wrong method.
this.app.all("*", (req, res) => {
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
});
this.appServer = this.app.listen(this.options.port, this.options.bindAddress, () => resolve());
}).then(async () => {
if (this.options.intentOptions?.encryption) {
await this.botIntent.enableEncryption();
}
else {
await this.botIntent.ensureRegistered();
}
});
}
/**
* Stops the application service, freeing the web server.
*/
stop() {
if (!this.appServer)
return;
this.appServer.close();
}
/**
* Gets an intent for a given localpart. The user ID will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get an Intent for.
* @returns {Intent} An Intent for the user.
*/
getIntent(localpart) {
return this.getIntentForUserId(this.getUserId(localpart));
}
/**
* Gets a full user ID for a given localpart. The user ID will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get a user ID for.
* @returns {string} The user's ID.
*/
getUserId(localpart) {
return `@${localpart}:${this.options.homeserverName}`;
}
/**
* Gets an Intent for a given user suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The user's suffix
* @returns {Intent} An Intent for the user.
*/
getIntentForSuffix(suffix) {
return this.getIntentForUserId(this.getUserIdForSuffix(suffix));
}
/**
* Gets a full user ID for a given suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The user's suffix
* @returns {string} The user's ID.
*/
getUserIdForSuffix(suffix) {
if (!this.userPrefix) {
throw new Error(`Cannot use getUserIdForSuffix, provided namespace did not include a valid suffix`);
}
return `${this.userPrefix}${suffix}:${this.options.homeserverName}`;
}
/**
* Gets an Intent for a given user ID.
* @param {string} userId The user ID to get an Intent for.
* @returns {Intent} An Intent for the user.
*/
getIntentForUserId(userId) {
let intent = this.intentsCache.get(userId);
if (!intent) {
intent = new Intent_1.Intent(this.options, userId, this);
this.intentsCache.set(userId, intent);
this.emit("intent.new", intent);
if (this.options.intentOptions.encryption) {
intent.enableEncryption().catch(e => {
__1.LogService.error("Appservice", `Failed to set up crypto on intent ${userId}`, e);
throw e; // re-throw to cause unhandled exception
});
}
}
return intent;
}
/**
* Gets the suffix for the provided user ID. If the user ID is not a namespaced
* user, this will return a falsey value.
* @param {string} userId The user ID to parse
* @returns {string} The suffix from the user ID.
*/
getSuffixForUserId(userId) {
if (!this.userPrefix) {
throw new Error(`Cannot use getUserIdForSuffix, provided namespace did not include a valid suffix`);
}
if (!userId || !userId.startsWith(this.userPrefix) || !userId.endsWith(`:${this.options.homeserverName}`)) {
// Invalid ID
return null;
}
return userId
.split('')
.slice(this.userPrefix.length)
.reverse()
.slice(this.options.homeserverName.length + 1)
.reverse()
.join('');
}
/**
* Determines if a given user ID is namespaced by this application service.
* @param {string} userId The user ID to check
* @returns {boolean} true if the user is namespaced, false otherwise
*/
isNamespacedUser(userId) {
return userId === this.botUserId ||
!!this.registration.namespaces?.users.find(({ regex }) => new RegExp(regex).test(userId));
}
/**
* Gets a full alias for a given localpart. The alias will be formed with the domain name given
* in the constructor.
* @param localpart The localpart to get an alias for.
* @returns {string} The alias.
*/
getAlias(localpart) {
return `#${localpart}:${this.options.homeserverName}`;
}
/**
* Gets a full alias for a given suffix. The prefix is automatically detected from the registration
* options.
* @param suffix The alias's suffix
* @returns {string} The alias.
*/
getAliasForSuffix(suffix) {
if (!this.aliasPrefix) {
throw new Error("Invalid configured alias prefix");
}
return `${this.aliasPrefix}${suffix}:${this.options.homeserverName}`;
}
/**
* Gets the localpart of an alias for a given suffix. The prefix is automatically detected from the registration
* options. Useful for the createRoom endpoint.
* @param suffix The alias's suffix
* @returns {string} The alias localpart.
*/
getAliasLocalpartForSuffix(suffix) {
if (!this.aliasPrefix) {
throw new Error("Invalid configured alias prefix");
}
return `${this.aliasPrefix.substr(1)}${suffix}`;
}
/**
* Gets the suffix for the provided alias. If the alias is not a namespaced
* alias, this will return a falsey value.
* @param {string} alias The alias to parse
* @returns {string} The suffix from the alias.
*/
getSuffixForAlias(alias) {
if (!this.aliasPrefix) {
throw new Error("Invalid configured alias prefix");
}
if (!alias || !this.isNamespacedAlias(alias)) {
// Invalid ID
return null;
}
return alias
.split('')
.slice(this.aliasPrefix.length)
.reverse()
.slice(this.options.homeserverName.length + 1)
.reverse()
.join('');
}
/**
* Determines if a given alias is namespaced by this application service.
* @param {string} alias The alias to check
* @returns {boolean} true if the alias is namespaced, false otherwise
*/
isNamespacedAlias(alias) {
if (!this.aliasPrefix) {
throw new Error("Invalid configured alias prefix");
}
return alias.startsWith(this.aliasPrefix) && alias.endsWith(":" + this.options.homeserverName);
}
/**
* Adds a preprocessor to the event pipeline. When this appservice encounters an event, it
* will try to run it through the preprocessors it can in the order they were added.
* @param {IPreprocessor} preprocessor the preprocessor to add
*/
addPreprocessor(preprocessor) {
if (!preprocessor)
throw new Error("Preprocessor cannot be null");
const eventTypes = preprocessor.getSupportedEventTypes();
if (!eventTypes)
return; // Nothing to do
for (const eventType of eventTypes) {
if (!this.eventProcessors[eventType])
this.eventProcessors[eventType] = [];
this.eventProcessors[eventType].push(preprocessor);
}
}
/**
* Sets the visibility of a room in the appservice's room directory.
* @param {string} networkId The network ID to group the room under.
* @param {string} roomId The room ID to manipulate the visibility of.
* @param {"public" | "private"} visibility The visibility to set for the room.
* @return {Promise<any>} resolves when the visibility has been updated.
*/
setRoomDirectoryVisibility(networkId, roomId, visibility) {
roomId = encodeURIComponent(roomId);
networkId = encodeURIComponent(networkId);
return this.botClient.doRequest("PUT", `/_matrix/client/v3/directory/list/appservice/${networkId}/${roomId}`, null, {
visibility,
});
}
async pingHomeserver() {
if (!this.registration.id) {
throw Error('No `id` given in registration information. Cannot ping homeserver');
}
this.pingRequest = (0, crypto_1.randomUUID)();
return this.botClient.doRequest("POST", `/_matrix/client/v1/appservice/${this.registration.id}/ping`, undefined, { transaction_id: this.pingRequest });
}
async processEphemeralEvent(event) {
if (!event)
return event;
if (!this.eventProcessors[event["type"]])
return event;
for (const processor of this.eventProcessors[event["type"]]) {
await processor.processEvent(event, this.botIntent.underlyingClient, __1.EventKind.EphemeralEvent);
}
return event;
}
async processEvent(event) {
if (!event)
return event;
if (!this.eventProcessors[event["type"]])
return event;
for (const processor of this.eventProcessors[event["type"]]) {
await processor.processEvent(event, this.botIntent.underlyingClient, __1.EventKind.RoomEvent);
}
return event;
}
async processMembershipEvent(event) {
if (!event["content"])
return;
const domain = new __1.UserID(event['state_key']).domain;
const botDomain = new __1.UserID(this.botUserId).domain;
if (domain !== botDomain)
return; // can't be impersonated, so don't try
const intent = this.getIntentForUserId(event['state_key']);
const targetMembership = event["content"]["membership"];
if (targetMembership === "join") {
this.emit("room.join", event["room_id"], event);
await intent.underlyingClient.crypto?.onRoomJoin(event["room_id"]);
}
else if (targetMembership === "ban" || targetMembership === "leave") {
this.emit("room.leave", event["room_id"], event);
}
else if (targetMembership === "invite") {
this.emit("room.invite", event["room_id"], event);
}
}
isAuthed(req) {
let providedToken = req.query ? req.query["access_token"] : null;
if (req.headers && req.headers["authorization"]) {
const authHeader = req.headers["authorization"];
if (!authHeader.startsWith("Bearer "))
providedToken = null;
else
providedToken = authHeader.substring("Bearer ".length);
}
return providedToken === this.registration.hs_token;
}
async decryptAppserviceEvent(roomId, encrypted) {
const existingClient = this.cryptoClientForRoomId.get(roomId);
const decryptFn = async (client) => {
// Also fetches state in order to decrypt room. We should throw if the client is confused.
if (!await client.crypto.isRoomEncrypted(roomId)) {
throw new Error("Client detected that the room is not encrypted.");
}
let event = (await client.crypto.decryptRoomEvent(encrypted, roomId)).raw;
event = await this.processEvent(event);
this.cryptoClientForRoomId.set(roomId, client);
// For logging purposes: show that the event was decrypted
__1.LogService.info("Appservice", `Processing decrypted event of type ${event["type"]}`);
return event;
};
// 1. Try cached client
if (existingClient) {
try {
return await decryptFn(existingClient);
}
catch (error) {
__1.LogService.debug("Appservice", `Failed to decrypt via cached client ${await existingClient.getUserId()}`, error);
__1.LogService.warn("Appservice", `Cached client was not able to decrypt ${roomId} ${encrypted.eventId} - trying other intents`);
}
}
this.cryptoClientForRoomId.delete(roomId);
// 2. Try the bot client
if (this.botClient.crypto?.isReady) {
try {
return await decryptFn(this.botClient);
}
catch (error) {
__1.LogService.debug("Appservice", `Failed to decrypt via bot client`, error);
__1.LogService.warn("Appservice", `Bot client was not able to decrypt ${roomId} ${encrypted.eventId} - trying other intents`);
}
}
const userIdsInRoom = (await this.botClient.getJoinedRoomMembers(roomId)).filter(u => this.isNamespacedUser(u));
// 3. Try existing clients with crypto enabled.
for (const intentCacheEntry of this.intentsCache.entries()) {
const [userId, intent] = intentCacheEntry;
if (!userIdsInRoom.includes(userId)) {
// Not in this room.
continue;
}
// Is this client crypto enabled?
if (!intent.underlyingClient.crypto?.isReady) {
continue;
}
try {
return await decryptFn(intent.underlyingClient);
}
catch (error) {
__1.LogService.debug("Appservice", `Failed to decrypt via ${userId}`, error);
__1.LogService.warn("Appservice", `Existing encrypted client was not able to decrypt ${roomId} ${encrypted.eventId} - trying other intents`);
}
}
// 4. Try to enable crypto on any client to decrypt it.
// We deliberately do not enable crypto on every client for performance reasons.
const userInRoom = this.intentsCache.find((intent, userId) => !intent.underlyingClient.crypto?.isReady && userIdsInRoom.includes(userId));
if (!userInRoom) {
throw Error('No users in room, cannot decrypt');
}
try {
await userInRoom.enableEncryption();
return await decryptFn(userInRoom.underlyingClient);
}
catch (error) {
__1.LogService.debug("Appservice", `Failed to decrypt via random user ${userInRoom.userId}`, error);
throw new Error("Unable to decrypt event", { cause: error });
}
}
async handleTransaction(txnId, body) {
// Process all the crypto stuff first to ensure that future transactions (if not this one)
// will decrypt successfully. We start with EDUs because we need structures to put counts
// and such into in a later stage, and EDUs are independent of crypto.
if (await this.storage.isTransactionCompleted(txnId)) {
// Duplicate.
return;
}
const byUserId = {};
const orderedEdus = [];
if (Array.isArray(body["de.sorunome.msc2409.to_device"])) {
orderedEdus.push(...body["de.sorunome.msc2409.to_device"].map(e => ({
...e,
unsigned: {
...e['unsigned'],
[EDU_ANNOTATION_KEY]: EduAnnotation.ToDevice,
},
})));
}
if (Array.isArray(body["de.sorunome.msc2409.ephemeral"])) {
orderedEdus.push(...body["de.sorunome.msc2409.ephemeral"].map(e => ({
...e,
unsigned: {
...e['unsigned'],
[EDU_ANNOTATION_KEY]: EduAnnotation.Ephemeral,
},
})));
}
for (let event of orderedEdus) {
if (event['edu_type'])
event['type'] = event['edu_type']; // handle property change during MSC2409's course
__1.LogService.info("Appservice", `Processing ${event['unsigned'][EDU_ANNOTATION_KEY]} event of type ${event["type"]}`);
event = await this.processEphemeralEvent(event);
// These events aren't tied to rooms, so just emit them generically
this.emit("ephemeral.event", event);
if (this.cryptoStorage && (event["type"] === "m.room.encrypted" || event.unsigned?.[EDU_ANNOTATION_KEY] === EduAnnotation.ToDevice)) {
const toUser = event["to_user_id"];
const intent = this.getIntentForUserId(toUser);
await intent.enableEncryption();
if (!byUserId[toUser])
byUserId[toUser] = { counts: null, toDevice: null, unusedFallbacks: null };
if (!byUserId[toUser].toDevice)
byUserId[toUser].toDevice = [];
byUserId[toUser].toDevice.push(event);
}
}
const deviceLists = body["org.matrix.msc3202.device_lists"] ?? {
changed: [],
removed: [],
};
if (!deviceLists.changed)
deviceLists.changed = [];
if (!deviceLists.removed)
deviceLists.removed = [];
if (deviceLists.changed.length || deviceLists.removed.length) {
this.emit("device_lists", deviceLists);
}
let otks = body["org.matrix.msc3202.device_one_time_keys_count"];
const otks2 = body["org.matrix.msc3202.device_one_time_key_counts"];
if (otks2 && !otks) {
__1.LogService.warn("Appservice", "Your homeserver is using an outdated field (device_one_time_key_counts) to talk to this appservice. " +
"If you're using Synapse, please upgrade to 1.73.0 or higher.");
otks = otks2;
}
if (otks) {
this.emit("otk.counts", otks);
}
if (otks && this.cryptoStorage) {
for (const userId of Object.keys(otks)) {
const intent = this.getIntentForUserId(userId);
await intent.enableEncryption();
const otksForUser = otks[userId][intent.underlyingClient.crypto.clientDeviceId];
if (otksForUser) {
if (!byUserId[userId]) {
byUserId[userId] = {
counts: null,
toDevice: null,
unusedFallbacks: null,
};
}
byUserId[userId].counts = otksForUser;
}
}
}
const fallbacks = body["org.matrix.msc3202.device_unused_fallback_key_types"];
if (fallbacks) {
this.emit("otk.unused_fallback_keys", fallbacks);
}
if (fallbacks && this.cryptoStorage) {
for (const userId of Object.keys(fallbacks)) {
const intent = this.getIntentForUserId(userId);
await intent.enableEncryption();
const fallbacksForUser = fallbacks[userId][intent.underlyingClient.crypto.clientDeviceId];
if (Array.isArray(fallbacksForUser) && !fallbacksForUser.includes(__1.OTKAlgorithm.Signed)) {
if (!byUserId[userId]) {
byUserId[userId] = {
counts: null,
toDevice: null,
unusedFallbacks: null,
};
}
byUserId[userId].unusedFallbacks = fallbacksForUser;
}
}
}
if (this.cryptoStorage) {
for (const userId of Object.keys(byUserId)) {
const intent = this.getIntentForUserId(userId);
await intent.enableEncryption();
const info = byUserId[userId];
const userStorage = this.storage.storageForUser(userId);
if (!info.toDevice)
info.toDevice = [];
if (!info.unusedFallbacks)
info.unusedFallbacks = JSON.parse(await userStorage.readValue("last_unused_fallbacks") || "[]");
if (!info.counts)
info.counts = JSON.parse(await userStorage.readValue("last_counts") || "{}");
__1.LogService.info("Appservice", `Updating crypto state for ${userId}`);
await intent.underlyingClient.crypto.updateSyncData(info.toDevice, info.counts, info.unusedFallbacks, deviceLists.changed, deviceLists.removed);
}
}
for (let event of body.events) {
__1.LogService.info("Appservice", `Processing event of type ${event["type"]}`);
event = await this.processEvent(event);
if (event['type'] === 'm.room.encrypted') {
this.emit("room.encrypted_event", event["room_id"], event);
if (this.cryptoStorage) {
try {
const encrypted = new __1.EncryptedRoomEvent(event);
const roomId = event['room_id'];
event = await this.decryptAppserviceEvent(roomId, encrypted);
this.emit("room.decrypted_event", roomId, event);
// For logging purposes: show that the event was decrypted
__1.LogService.info("Appservice", `Processing decrypted event of type ${event["type"]}`);
}
catch (e) {
__1.LogService.error("Appservice", `Decryption error on ${event['room_id']} ${event['event_id']}`, e);
this.emit("room.failed_decryption", event['room_id'], event, e);
}
}
}
this.emit("room.event", event["room_id"], event);
if (event['type'] === 'm.room.message') {
this.emit("room.message", event["room_id"], event);
}
if (event['type'] === 'm.room.member' && this.isNamespacedUser(event['state_key'])) {
await this.processMembershipEvent(event);
}
if (event['type'] === 'm.room.tombstone' && event['state_key'] === '') {
this.emit("room.archived", event['room_id'], event);
}
if (event['type'] === 'm.room.create' && event['state_key'] === '' && event['content'] && event['content']['predecessor']) {
this.emit("room.upgraded", event['room_id'], event);
}
}
}
async onTransaction(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
if (typeof (req.body) !== "object") {
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
return;
}
if (!req.body["events"] || !Array.isArray(req.body["events"])) {
res.status(400).json({ errcode: "BAD_REQUEST", error: "Invalid JSON: expected events" });
return;
}
const { txnId } = req.params;
if (this.pendingTransactions.has(txnId)) {
// The homeserver has retried a transaction while we're still handling it.
try {
await this.pendingTransactions.get(txnId);
res.status(200).json({});
}
catch (e) {
__1.LogService.error("Appservice", e);
res.status(500).json({});
}
return;
}
__1.LogService.info("Appservice", `Processing transaction ${txnId}`);
const txnHandler = this.handleTransaction(txnId, req.body);
this.pendingTransactions.set(txnId, txnHandler);
try {
await txnHandler;
try {
await this.storage.setTransactionCompleted(txnId);
}
catch (ex) {
// Not fatal for the transaction since we *did* process it, but we should
// warn loudly.
__1.LogService.warn("Appservice", "Failed to store completed transaction", ex);
}
res.status(200).json({});
}
catch (e) {
__1.LogService.error("Appservice", e);
res.status(500).json({});
}
finally {
this.pendingTransactions.delete(txnId);
}
}
async onUser(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
const userId = req.params["userId"];
this.emit("query.user", userId, async (result) => {
if (result.then)
result = await result;
if (result === false) {
res.status(404).json({ errcode: "USER_DOES_NOT_EXIST", error: "User not created" });
}
else {
const intent = this.getIntentForUserId(userId);
await intent.ensureRegistered();
if (result.display_name)
await intent.underlyingClient.setDisplayName(result.display_name);
if (result.avatar_mxc)
await intent.underlyingClient.setAvatarUrl(result.avatar_mxc);
res.status(200).json(result); // return result for debugging + testing
}
});
}
async onRoomAlias(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
const roomAlias = req.params["roomAlias"];
this.emit("query.room", roomAlias, async (result) => {
if (result.then)
result = await result;
if (result === false) {
res.status(404).json({ errcode: "ROOM_DOES_NOT_EXIST", error: "Room not created" });
}
else {
const intent = this.botIntent;
await intent.ensureRegistered();
result["room_alias_name"] = roomAlias.substring(1).split(':')[0];
result["__roomId"] = await intent.underlyingClient.createRoom(result);
res.status(200).json(result); // return result for debugging + testing
}
});
}
async onKeysClaim(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
if (typeof (req.body) !== "object") {
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
return;
}
let responded = false;
this.emit("query.key_claim", req.body, (result) => {
responded = true;
const handleResult = (result2) => {
if (!result2) {
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
return;
}
res.status(200).json(result2);
};
Promise.resolve(result).then(r => handleResult(r)).catch(e => {
__1.LogService.error("Appservice", "Error handling key claim API", e);
res.status(500).json({ errcode: "M_UNKNOWN", error: "Error handling key claim API" });
});
});
if (!responded) {
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
}
}
async onKeysQuery(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
if (typeof (req.body) !== "object") {
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
return;
}
let responded = false;
this.emit("query.key", req.body, (result) => {
responded = true;
const handleResult = (result2) => {
if (!result2) {
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
return;
}
// Implementation note: we could probably query the device keys from our storage if we wanted to.
res.status(200).json(result2);
};
Promise.resolve(result).then(r => handleResult(r)).catch(e => {
__1.LogService.error("Appservice", "Error handling key query API", e);
res.status(500).json({ errcode: "M_UNKNOWN", error: "Error handling key query API" });
});
});
if (!responded) {
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
}
}
onThirdpartyProtocol(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
const protocol = req.params["protocol"];
if (!this.registration.protocols.includes(protocol)) {
res.status(404).json({
errcode: "PROTOCOL_NOT_HANDLED",
error: "Protocol is not handled by this appservice",
});
return;
}
this.emit("thirdparty.protocol", protocol, (protocolResponse) => {
res.status(200).json(protocolResponse);
});
}
handleThirdpartyObject(req, res, objType, matrixId) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
const protocol = req.params["protocol"];
const responseFunc = (items) => {
if (items && items.length > 0) {
res.status(200).json(items);
return;
}
res.status(404).json({
errcode: "NO_MAPPING_FOUND",
error: "No mappings found",
});
};
// Lookup remote objects(s)
if (protocol) { // If protocol is given, we are looking up a objects based on fields
if (!this.registration.protocols.includes(protocol)) {
res.status(404).json({
errcode: "PROTOCOL_NOT_HANDLED",
error: "Protocol is not handled by this appservice",
});
return;
}
// Remove the access_token
delete req.query.access_token;
this.emit(`thirdparty.${objType}.remote`, protocol, req.query, responseFunc);
return;
}
else if (matrixId) { // If a user ID is given, we are looking up a remote objects based on a id
this.emit(`thirdparty.${objType}.matrix`, matrixId, responseFunc);
return;
}
res.status(400).json({
errcode: "INVALID_PARAMETERS",
error: "Invalid parameters given",
});
}
onThirdpartyUser(req, res) {
return this.handleThirdpartyObject(req, res, "user", req.query["userid"]);
}
onThirdpartyLocation(req, res) {
return this.handleThirdpartyObject(req, res, "location", req.query["alias"]);
}
onPing(req, res) {
if (!this.isAuthed(req)) {
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
return;
}
if (typeof (req.body) !== "object") {
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
return;
}
if (!this.pingRequest) {
res.status(400).json({ errcode: "BAD_REQUEST", error: "No ping request expected" });
return;
}
if (req.body.transaction_id !== this.pingRequest) {
res.status(400).json({ errcode: "BAD_REQUEST", error: "transaction_id did not match" });
return;
}
this.pingRequest = undefined;
res.status(200).json({});
}
}
exports.Appservice = Appservice;
//# sourceMappingURL=Appservice.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,115 @@
import { MatrixClient, Metrics } from "..";
import { Appservice, IAppserviceOptions } from "./Appservice";
import { UnstableAppserviceApis } from "./UnstableAppserviceApis";
/**
* An Intent is an intelligent client that tracks things like the user's membership
* in rooms to ensure the action being performed is possible. This is very similar
* to how Intents work in the matrix-js-sdk in that the Intent will ensure that the
* user is joined to the room before posting a message, for example.
* @category Application services
*/
export declare class Intent {
private options;
private impersonateUserId;
private appservice;
/**
* The metrics instance for this intent. Note that this will not raise metrics
* for the underlying client - those will be available through this instance's
* parent (the appservice).
*/
readonly metrics: Metrics;
private readonly storage;
private readonly cryptoStorage;
private client;
private unstableApisInstance;
private cryptoSetupPromise;
/**
* Creates a new intent. Intended to be created by application services.
* @param {IAppserviceOptions} options The options for the application service.
* @param {string} impersonateUserId The user ID to impersonate.
* @param {Appservice} appservice The application service itself.
*/
constructor(options: IAppserviceOptions, impersonateUserId: string, appservice: Appservice);
private makeClient;
/**
* Gets the user ID this intent is for.
*/
get userId(): string;
/**
* Gets the underlying MatrixClient that powers this Intent.
*/
get underlyingClient(): MatrixClient;
/**
* Gets the unstable API access class. This is generally not recommended to be
* used by appservices.
* @return {UnstableAppserviceApis} The unstable API access class.
*/
get unstableApis(): UnstableAppserviceApis;
/**
* Sets up crypto on the client if it hasn't already been set up.
* @param providedDeviceId Optional device ID. If given, this will used instead of trying to
* masquerade as the first non-key enabled device.
* @returns {Promise<void>} Resolves when complete.
*/
enableEncryption(providedDeviceId?: string): Promise<void>;
/**
* Gets the joined rooms for the intent.
* @returns {Promise<string[]>} Resolves to an array of room IDs where
* the intent is joined.
*/
getJoinedRooms(): Promise<string[]>;
/**
* Leaves the given room.
* @param {string} roomId The room ID to leave
* @param {string=} reason Optional reason to be included as the reason for leaving the room.
* @returns {Promise<any>} Resolves when the room has been left.
*/
leaveRoom(roomId: string, reason?: string): Promise<any>;
/**
* Joins the given room
* @param {string} roomIdOrAlias the room ID or alias to join
* @returns {Promise<string>} resolves to the joined room ID
*/
joinRoom(roomIdOrAlias: string): Promise<string>;
/**
* Sends a text message to a room.
* @param {string} roomId The room ID to send text to.
* @param {string} body The message body to send.
* @param {"m.text" | "m.emote" | "m.notice"} msgtype The message type to send.
* @returns {Promise<string>} Resolves to the event ID of the sent message.
*/
sendText(roomId: string, body: string, msgtype?: "m.text" | "m.emote" | "m.notice"): Promise<string>;
/**
* Sends an event to a room.
* @param {string} roomId The room ID to send the event to.
* @param {any} content The content of the event.
* @returns {Promise<string>} Resolves to the event ID of the sent event.
*/
sendEvent(roomId: string, content: any): Promise<string>;
/**
* Ensures the user is registered and joined to the given room.
* @param {string} roomId The room ID to join
* @returns {Promise<any>} Resolves when complete
*/
ensureRegisteredAndJoined(roomId: string): Promise<void>;
/**
* Ensures the user is joined to the given room
* @param {string} roomId The room ID to join
* @returns {Promise<any>} Resolves when complete
* @deprecated Use `joinRoom()` instead
*/
ensureJoined(roomId: string): Promise<string>;
/**
* Refreshes which rooms the user is joined to, potentially saving time on
* calls like ensureJoined()
* @deprecated There is no longer a joined rooms cache, use `getJoinedRooms()` instead
* @returns {Promise<string[]>} Resolves to the joined room IDs for the user.
*/
refreshJoinedRooms(): Promise<string[]>;
/**
* Ensures the user is registered
* @param deviceId An optional device ID to register with.
* @returns {Promise<any>} Resolves when complete
*/
ensureRegistered(deviceId?: string): Promise<any>;
}

View File

@@ -0,0 +1,373 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Intent = void 0;
const __1 = require("..");
// noinspection TypeScriptPreferShortImport
const decorators_1 = require("../metrics/decorators");
const UnstableAppserviceApis_1 = require("./UnstableAppserviceApis");
const MatrixError_1 = require("../models/MatrixError");
/**
* An Intent is an intelligent client that tracks things like the user's membership
* in rooms to ensure the action being performed is possible. This is very similar
* to how Intents work in the matrix-js-sdk in that the Intent will ensure that the
* user is joined to the room before posting a message, for example.
* @category Application services
*/
class Intent {
options;
impersonateUserId;
appservice;
/**
* The metrics instance for this intent. Note that this will not raise metrics
* for the underlying client - those will be available through this instance's
* parent (the appservice).
*/
metrics;
storage;
cryptoStorage;
client;
unstableApisInstance;
cryptoSetupPromise;
/**
* Creates a new intent. Intended to be created by application services.
* @param {IAppserviceOptions} options The options for the application service.
* @param {string} impersonateUserId The user ID to impersonate.
* @param {Appservice} appservice The application service itself.
*/
constructor(options, impersonateUserId, appservice) {
this.options = options;
this.impersonateUserId = impersonateUserId;
this.appservice = appservice;
this.metrics = new __1.Metrics(appservice.metrics);
this.storage = options.storage;
this.cryptoStorage = options.cryptoStorage;
this.makeClient(false);
}
makeClient(withCrypto, accessToken) {
let cryptoStore;
const storage = this.storage?.storageForUser?.(this.userId);
if (withCrypto) {
cryptoStore = this.cryptoStorage?.storageForUser(this.userId);
if (!cryptoStore) {
throw new Error("Tried to set up client with crypto when not available");
}
if (!storage) {
throw new Error("Tried to set up client with crypto, but no persistent storage");
}
}
this.client = new __1.MatrixClient(this.options.homeserverUrl, accessToken ?? this.options.registration.as_token, storage, cryptoStore, {
enableContentScanner: this.options.intentOptions?.enableContentScanner,
});
this.client.metrics = new __1.Metrics(this.appservice.metrics); // Metrics only go up by one parent
this.unstableApisInstance = new UnstableAppserviceApis_1.UnstableAppserviceApis(this.client);
if (this.impersonateUserId !== this.appservice.botUserId) {
this.client.impersonateUserId(this.impersonateUserId);
}
if (this.options.joinStrategy) {
this.client.setJoinStrategy(this.options.joinStrategy);
}
}
/**
* Gets the user ID this intent is for.
*/
get userId() {
return this.impersonateUserId;
}
/**
* Gets the underlying MatrixClient that powers this Intent.
*/
get underlyingClient() {
return this.client;
}
/**
* Gets the unstable API access class. This is generally not recommended to be
* used by appservices.
* @return {UnstableAppserviceApis} The unstable API access class.
*/
get unstableApis() {
return this.unstableApisInstance;
}
/**
* Sets up crypto on the client if it hasn't already been set up.
* @param providedDeviceId Optional device ID. If given, this will used instead of trying to
* masquerade as the first non-key enabled device.
* @returns {Promise<void>} Resolves when complete.
*/
async enableEncryption(providedDeviceId) {
if (!this.cryptoSetupPromise) {
// eslint-disable-next-line no-async-promise-executor
this.cryptoSetupPromise = new Promise(async (resolve, reject) => {
try {
// Prepare a client first
await this.ensureRegistered();
const storage = this.storage?.storageForUser?.(this.userId);
this.client.impersonateUserId(this.userId); // make sure the devices call works
const cryptoStore = this.cryptoStorage?.storageForUser(this.userId);
if (!cryptoStore) {
// noinspection ExceptionCaughtLocallyJS
throw new Error("Failed to create crypto store");
}
let deviceId = await cryptoStore.getDeviceId();
if (!providedDeviceId) {
// Try to impersonate a device ID
const ownDevices = await this.client.getOwnDevices();
let deviceId = await cryptoStore.getDeviceId();
if (!deviceId || !ownDevices.some(d => d.device_id === deviceId)) {
const deviceKeys = await this.client.getUserDevices([this.userId]);
const userDeviceKeys = deviceKeys.device_keys[this.userId];
if (userDeviceKeys) {
// We really should be validating signatures here, but we're actively looking
// for devices without keys to impersonate, so it should be fine. In theory,
// those devices won't even be present but we're cautious.
const devicesWithKeys = Array.from(Object.entries(userDeviceKeys))
.filter(d => d[0] === d[1].device_id && !!d[1].keys?.[`${__1.DeviceKeyAlgorithm.Curve25519}:${d[1].device_id}`])
.map(t => t[0]); // grab device ID from tuple
deviceId = ownDevices.find(d => !devicesWithKeys.includes(d.device_id))?.device_id;
}
}
}
else {
if (deviceId && deviceId !== providedDeviceId) {
__1.LogService.warn("Intent", `Storage already configured with an existing device ${deviceId}. Old storage will be cleared.`);
}
deviceId = providedDeviceId;
}
let prepared = false;
if (deviceId) {
const cryptoStore = this.cryptoStorage?.storageForUser(this.userId);
const existingDeviceId = await cryptoStore.getDeviceId();
if (existingDeviceId && existingDeviceId !== deviceId) {
__1.LogService.warn("Intent", `Device ID has changed for user ${this.userId} from ${existingDeviceId} to ${deviceId}`);
}
this.makeClient(true);
this.client.impersonateUserId(this.userId, deviceId);
// verify that the server supports impersonating the device
const respDeviceId = (await this.client.getWhoAmI()).device_id;
prepared = (respDeviceId === deviceId);
}
if (!prepared) {
// XXX: We work around servers that don't support device_id impersonation
const accessToken = await Promise.resolve(storage?.readValue("accessToken"));
if (!accessToken) {
const loginBody = {
type: "m.login.application_service",
identifier: {
type: "m.id.user",
user: this.userId,
},
};
const res = await this.client.doRequest("POST", "/_matrix/client/v3/login", {}, loginBody);
this.makeClient(true, res['access_token']);
storage.storeValue("accessToken", this.client.accessToken);
prepared = true;
}
else {
this.makeClient(true, accessToken);
prepared = true;
}
}
if (!prepared) { // noinspection ExceptionCaughtLocallyJS
throw new Error("Unable to establish a device ID");
}
// Now set up crypto
await this.client.crypto.prepare();
this.appservice.on("room.event", (roomId, event) => {
this.client.crypto.onRoomEvent(roomId, event);
});
resolve();
}
catch (e) {
reject(e);
}
});
}
return this.cryptoSetupPromise;
}
/**
* Gets the joined rooms for the intent.
* @returns {Promise<string[]>} Resolves to an array of room IDs where
* the intent is joined.
*/
async getJoinedRooms() {
await this.ensureRegistered();
return await this.client.getJoinedRooms();
}
/**
* Leaves the given room.
* @param {string} roomId The room ID to leave
* @param {string=} reason Optional reason to be included as the reason for leaving the room.
* @returns {Promise<any>} Resolves when the room has been left.
*/
async leaveRoom(roomId, reason) {
await this.ensureRegistered();
return this.client.leaveRoom(roomId, reason);
}
/**
* Joins the given room
* @param {string} roomIdOrAlias the room ID or alias to join
* @returns {Promise<string>} resolves to the joined room ID
*/
async joinRoom(roomIdOrAlias) {
await this.ensureRegistered();
return this.client.joinRoom(roomIdOrAlias);
}
/**
* Sends a text message to a room.
* @param {string} roomId The room ID to send text to.
* @param {string} body The message body to send.
* @param {"m.text" | "m.emote" | "m.notice"} msgtype The message type to send.
* @returns {Promise<string>} Resolves to the event ID of the sent message.
*/
async sendText(roomId, body, msgtype = "m.text") {
return this.sendEvent(roomId, { body: body, msgtype: msgtype });
}
/**
* Sends an event to a room.
* @param {string} roomId The room ID to send the event to.
* @param {any} content The content of the event.
* @returns {Promise<string>} Resolves to the event ID of the sent event.
*/
async sendEvent(roomId, content) {
await this.ensureRegisteredAndJoined(roomId);
return this.client.sendMessage(roomId, content);
}
/**
* Ensures the user is registered and joined to the given room.
* @param {string} roomId The room ID to join
* @returns {Promise<any>} Resolves when complete
*/
async ensureRegisteredAndJoined(roomId) {
await this.ensureRegistered();
await this.ensureJoined(roomId);
}
/**
* Ensures the user is joined to the given room
* @param {string} roomId The room ID to join
* @returns {Promise<any>} Resolves when complete
* @deprecated Use `joinRoom()` instead
*/
async ensureJoined(roomId) {
const returnedRoomId = await this.client.joinRoom(roomId);
return returnedRoomId;
}
/**
* Refreshes which rooms the user is joined to, potentially saving time on
* calls like ensureJoined()
* @deprecated There is no longer a joined rooms cache, use `getJoinedRooms()` instead
* @returns {Promise<string[]>} Resolves to the joined room IDs for the user.
*/
async refreshJoinedRooms() {
return await this.getJoinedRooms();
}
/**
* Ensures the user is registered
* @param deviceId An optional device ID to register with.
* @returns {Promise<any>} Resolves when complete
*/
async ensureRegistered(deviceId) {
if (!(await Promise.resolve(this.storage.isUserRegistered(this.userId)))) {
try {
const result = await this.client.doRequest("POST", "/_matrix/client/v3/register", null, {
type: "m.login.application_service",
username: this.userId.substring(1).split(":")[0],
device_id: deviceId,
});
// HACK: Workaround for unit tests
if (result['errcode']) {
// noinspection ExceptionCaughtLocallyJS
throw { body: result }; // eslint-disable-line no-throw-literal
}
this.client.impersonateUserId(this.userId, result["device_id"]);
}
catch (err) {
if (err instanceof MatrixError_1.MatrixError && err.errcode === "M_USER_IN_USE") {
await Promise.resolve(this.storage.addRegisteredUser(this.userId));
if (this.userId === this.appservice.botUserId) {
return null;
}
else {
__1.LogService.error("Appservice", "Error registering user: User ID is in use");
return null;
}
}
else {
__1.LogService.error("Appservice", "Encountered error registering user: ");
__1.LogService.error("Appservice", (0, __1.extractRequestError)(err));
}
throw err;
}
await Promise.resolve(this.storage.addRegisteredUser(this.userId));
}
}
}
exports.Intent = Intent;
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "enableEncryption", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], Intent.prototype, "getJoinedRooms", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "leaveRoom", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "joinRoom", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "sendText", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], Intent.prototype, "sendEvent", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "ensureRegisteredAndJoined", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "ensureJoined", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], Intent.prototype, "refreshJoinedRooms", null);
__decorate([
(0, decorators_1.timedIntentFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], Intent.prototype, "ensureRegistered", null);
//# sourceMappingURL=Intent.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,85 @@
import { Appservice } from "./Appservice";
import { Intent } from "./Intent";
export declare const REMOTE_USER_INFO_ACCOUNT_DATA_EVENT_TYPE = "io.t2bot.sdk.bot.remote_user_info";
export declare const REMOTE_ROOM_INFO_ACCOUNT_DATA_EVENT_TYPE = "io.t2bot.sdk.bot.remote_room_info";
export declare const REMOTE_USER_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = "io.t2bot.sdk.bot.remote_user_map";
export declare const REMOTE_ROOM_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = "io.t2bot.sdk.bot.remote_room_map";
/**
* @see MatrixBridge
* @category Application services
*/
export interface IRemoteRoomInfo {
/**
* A unique identifier for the remote user.
*/
id: string;
}
/**
* @see MatrixBridge
* @category Application services
*/
export interface IRemoteUserInfo {
/**
* A unique identifier for the remote room (or room equivalent).
*/
id: string;
}
/**
* Utility class for common operations performed by bridges (represented
* as appservices).
*
* The storage utilities are not intended for bridges which allow 1:many
* relationships with the remote network.
*
* Bridges are generally expected to create their own classes which extend
* the IRemoteRoomInfo and IRemoteUserInfo interfaces and serialize to JSON
* cleanly. The serialized version of these classes is persisted in various
* account data locations for future lookups.
* @category Application services
*/
export declare class MatrixBridge {
private appservice;
constructor(appservice: Appservice);
/**
* Gets information about a remote user.
* @param {Intent} userIntent The Matrix user intent to get information on.
* @returns {Promise<IRemoteUserInfo>} Resolves to the remote user information.
*/
getRemoteUserInfo<T extends IRemoteUserInfo>(userIntent: Intent): Promise<T>;
/**
* Sets information about a remote user. Calling this function will map the
* provided remote user ID to the intent's owner.
* @param {Intent} userIntent The Matrix user intent to store information on.
* @param {IRemoteUserInfo} remoteInfo The remote user information to store
* @returns {Promise<any>} Resolves when the information has been updated.
*/
setRemoteUserInfo<T extends IRemoteUserInfo>(userIntent: Intent, remoteInfo: T): Promise<any>;
/**
* Gets information about a remote room.
* @param {string} matrixRoomId The Matrix room ID to get information on.
* @returns {Promise<IRemoteRoomInfo>} Resolves to the remote room information.
*/
getRemoteRoomInfo<T extends IRemoteRoomInfo>(matrixRoomId: string): Promise<T>;
/**
* Sets information about a remote room. Calling this function will map the
* provided remote room ID to the matrix room ID.
* @param {string} matrixRoomId The Matrix room ID to store information on.
* @param {IRemoteRoomInfo} remoteInfo The remote room information to store
* @returns {Promise<any>} Resolves when the information has been updated.
*/
setRemoteRoomInfo<T extends IRemoteRoomInfo>(matrixRoomId: string, remoteInfo: T): Promise<any>;
/**
* Gets the Matrix room ID for the provided remote room ID.
* @param {string} remoteRoomId The remote room ID to look up.
* @returns {Promise<string>} Resolves to the Matrix room ID.
*/
getMatrixRoomIdForRemote(remoteRoomId: string): Promise<string>;
/**
* Gets a Matrix user intent for the provided remote user ID.
* @param {string} remoteUserId The remote user ID to look up.
* @returns {Promise<Intent>} Resolves to the Matrix user intent.
*/
getIntentForRemote(remoteUserId: string): Promise<Intent>;
private updateRemoteUserMapping;
private updateRemoteRoomMapping;
}

View File

@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixBridge = exports.REMOTE_ROOM_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = exports.REMOTE_USER_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = exports.REMOTE_ROOM_INFO_ACCOUNT_DATA_EVENT_TYPE = exports.REMOTE_USER_INFO_ACCOUNT_DATA_EVENT_TYPE = void 0;
exports.REMOTE_USER_INFO_ACCOUNT_DATA_EVENT_TYPE = "io.t2bot.sdk.bot.remote_user_info";
exports.REMOTE_ROOM_INFO_ACCOUNT_DATA_EVENT_TYPE = "io.t2bot.sdk.bot.remote_room_info";
exports.REMOTE_USER_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = "io.t2bot.sdk.bot.remote_user_map";
exports.REMOTE_ROOM_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX = "io.t2bot.sdk.bot.remote_room_map";
/**
* Utility class for common operations performed by bridges (represented
* as appservices).
*
* The storage utilities are not intended for bridges which allow 1:many
* relationships with the remote network.
*
* Bridges are generally expected to create their own classes which extend
* the IRemoteRoomInfo and IRemoteUserInfo interfaces and serialize to JSON
* cleanly. The serialized version of these classes is persisted in various
* account data locations for future lookups.
* @category Application services
*/
class MatrixBridge {
appservice;
constructor(appservice) {
this.appservice = appservice;
}
/**
* Gets information about a remote user.
* @param {Intent} userIntent The Matrix user intent to get information on.
* @returns {Promise<IRemoteUserInfo>} Resolves to the remote user information.
*/
async getRemoteUserInfo(userIntent) {
await userIntent.ensureRegistered();
return userIntent.underlyingClient.getAccountData(exports.REMOTE_USER_INFO_ACCOUNT_DATA_EVENT_TYPE);
}
/**
* Sets information about a remote user. Calling this function will map the
* provided remote user ID to the intent's owner.
* @param {Intent} userIntent The Matrix user intent to store information on.
* @param {IRemoteUserInfo} remoteInfo The remote user information to store
* @returns {Promise<any>} Resolves when the information has been updated.
*/
async setRemoteUserInfo(userIntent, remoteInfo) {
await userIntent.ensureRegistered();
await userIntent.underlyingClient.setAccountData(exports.REMOTE_USER_INFO_ACCOUNT_DATA_EVENT_TYPE, remoteInfo);
await this.updateRemoteUserMapping(userIntent.userId, remoteInfo.id);
}
/**
* Gets information about a remote room.
* @param {string} matrixRoomId The Matrix room ID to get information on.
* @returns {Promise<IRemoteRoomInfo>} Resolves to the remote room information.
*/
async getRemoteRoomInfo(matrixRoomId) {
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
// We do not need to ensure the user is joined to the room because we can associate
// room account data with any arbitrary room.
return bridgeBot.underlyingClient.getRoomAccountData(exports.REMOTE_ROOM_INFO_ACCOUNT_DATA_EVENT_TYPE, matrixRoomId);
}
/**
* Sets information about a remote room. Calling this function will map the
* provided remote room ID to the matrix room ID.
* @param {string} matrixRoomId The Matrix room ID to store information on.
* @param {IRemoteRoomInfo} remoteInfo The remote room information to store
* @returns {Promise<any>} Resolves when the information has been updated.
*/
async setRemoteRoomInfo(matrixRoomId, remoteInfo) {
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
// We do not need to ensure the user is joined to the room because we can associate
// room account data with any arbitrary room.
await bridgeBot.underlyingClient.setRoomAccountData(exports.REMOTE_ROOM_INFO_ACCOUNT_DATA_EVENT_TYPE, matrixRoomId, remoteInfo);
await this.updateRemoteRoomMapping(matrixRoomId, remoteInfo.id);
}
/**
* Gets the Matrix room ID for the provided remote room ID.
* @param {string} remoteRoomId The remote room ID to look up.
* @returns {Promise<string>} Resolves to the Matrix room ID.
*/
async getMatrixRoomIdForRemote(remoteRoomId) {
const eventType = `${exports.REMOTE_ROOM_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX}.${remoteRoomId}`;
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
const result = await bridgeBot.underlyingClient.getAccountData(eventType);
return result['id'];
}
/**
* Gets a Matrix user intent for the provided remote user ID.
* @param {string} remoteUserId The remote user ID to look up.
* @returns {Promise<Intent>} Resolves to the Matrix user intent.
*/
async getIntentForRemote(remoteUserId) {
const eventType = `${exports.REMOTE_USER_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX}.${remoteUserId}`;
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
const result = await bridgeBot.underlyingClient.getAccountData(eventType);
return this.appservice.getIntentForUserId(result['id']);
}
async updateRemoteUserMapping(matrixUserId, remoteUserId) {
const eventType = `${exports.REMOTE_USER_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX}.${remoteUserId}`;
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
await bridgeBot.underlyingClient.setAccountData(eventType, { id: matrixUserId });
}
async updateRemoteRoomMapping(matrixRoomId, remoteRoomId) {
const eventType = `${exports.REMOTE_ROOM_MAP_ACCOUNT_DATA_EVENT_TYPE_PREFIX}.${remoteRoomId}`;
const bridgeBot = this.appservice.botIntent;
await bridgeBot.ensureRegistered();
await bridgeBot.underlyingClient.setAccountData(eventType, { id: matrixRoomId });
}
}
exports.MatrixBridge = MatrixBridge;
//# sourceMappingURL=MatrixBridge.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MatrixBridge.js","sourceRoot":"","sources":["../../src/appservice/MatrixBridge.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,41 @@
import { MatrixClient } from "../MatrixClient";
import { MSC2716BatchSendResponse } from "../models/MSC2176";
/**
* Unstable APIs that shouldn't be used in most circumstances for appservices.
* @category Unstable APIs
*/
export declare class UnstableAppserviceApis {
private client;
private requestId;
constructor(client: MatrixClient);
/**
* Send several historical events into a room.
* @see https://github.com/matrix-org/matrix-doc/pull/2716
* @param {string} roomId The roomID to send to.
* @param {string} prevEventId The event ID where this batch will be inserted
* @param {string} chunkId The chunk ID returned from a previous call. Set falsy to start at the beginning.
* @param {any[]} events A set of event contents for events to be inserted into the room.
* @param {any[]} stateEventsAtStart A set of state events to be inserted into the room. Defaults to empty.
* @returns A set of eventIds and the next chunk ID
*/
sendHistoricalEventBatch(roomId: string, prevEventId: string, events: any[], stateEventsAtStart?: any[], chunkId?: string): Promise<MSC2716BatchSendResponse>;
/**
* Sends an event to the given room with a given timestamp.
* @param {string} roomId the room ID to send the event to
* @param {string} eventType the type of event to send
* @param {string} content the event body to send
* @param {number} ts The origin_server_ts of the new event
* @returns {Promise<string>} resolves to the event ID that represents the event
*/
sendEventWithTimestamp(roomId: string, eventType: string, content: any, ts: number): Promise<any>;
/**
* Sends a state event to the given room with a given timestamp.
* @param {string} roomId the room ID to send the event to
* @param {string} type the event type to send
* @param {string} stateKey the state key to send, should not be null
* @param {string} content the event body to send
* @param {number} ts The origin_server_ts of the new event
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
sendStateEventWithTimestamp(roomId: string, type: string, stateKey: string, content: any, ts: number): Promise<string>;
}

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnstableAppserviceApis = void 0;
/**
* Unstable APIs that shouldn't be used in most circumstances for appservices.
* @category Unstable APIs
*/
class UnstableAppserviceApis {
client;
requestId = 0;
constructor(client) {
this.client = client;
}
/**
* Send several historical events into a room.
* @see https://github.com/matrix-org/matrix-doc/pull/2716
* @param {string} roomId The roomID to send to.
* @param {string} prevEventId The event ID where this batch will be inserted
* @param {string} chunkId The chunk ID returned from a previous call. Set falsy to start at the beginning.
* @param {any[]} events A set of event contents for events to be inserted into the room.
* @param {any[]} stateEventsAtStart A set of state events to be inserted into the room. Defaults to empty.
* @returns A set of eventIds and the next chunk ID
*/
async sendHistoricalEventBatch(roomId, prevEventId, events, stateEventsAtStart = [], chunkId) {
return this.client.doRequest("POST", `/_matrix/client/unstable/org.matrix.msc2716/rooms/${encodeURIComponent(roomId)}/batch_send`, {
prev_event: prevEventId,
chunk_id: chunkId,
}, {
events,
state_events_at_start: stateEventsAtStart,
});
}
/**
* Sends an event to the given room with a given timestamp.
* @param {string} roomId the room ID to send the event to
* @param {string} eventType the type of event to send
* @param {string} content the event body to send
* @param {number} ts The origin_server_ts of the new event
* @returns {Promise<string>} resolves to the event ID that represents the event
*/
async sendEventWithTimestamp(roomId, eventType, content, ts) {
const txnId = `${(new Date().getTime())}__inc_appts${++this.requestId}`;
const path = `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/${encodeURIComponent(eventType)}/${encodeURIComponent(txnId)}`;
const response = await this.client.doRequest("PUT", path, { ts }, content);
return response.event_id;
}
/**
* Sends a state event to the given room with a given timestamp.
* @param {string} roomId the room ID to send the event to
* @param {string} type the event type to send
* @param {string} stateKey the state key to send, should not be null
* @param {string} content the event body to send
* @param {number} ts The origin_server_ts of the new event
* @returns {Promise<string>} resolves to the event ID that represents the message
*/
async sendStateEventWithTimestamp(roomId, type, stateKey, content, ts) {
const path = `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/${encodeURIComponent(type)}/${encodeURIComponent(stateKey)}`;
const response = await this.client.doRequest("PUT", path, { ts }, content);
return response.event_id;
}
}
exports.UnstableAppserviceApis = UnstableAppserviceApis;
//# sourceMappingURL=UnstableAppserviceApis.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"UnstableAppserviceApis.js","sourceRoot":"","sources":["../../src/appservice/UnstableAppserviceApis.ts"],"names":[],"mappings":";;;AAGA;;;GAGG;AACH,MAAa,sBAAsB;IAGX;IAFZ,SAAS,GAAG,CAAC,CAAC;IAEtB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IACxC,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,wBAAwB,CAAC,MAAc,EAAE,WAAmB,EAAE,MAAa,EAAE,qBAA4B,EAAE,EAAE,OAAgB;QACtI,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,qDAAqD,kBAAkB,CAAC,MAAM,CAAC,aAAa,EAAE;YAC/H,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,OAAO;SACpB,EAAE;YACC,MAAM;YACN,qBAAqB,EAAE,kBAAkB;SAC5C,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,sBAAsB,CAAC,MAAc,EAAE,SAAiB,EAAE,OAAY,EAAE,EAAU;QAC3F,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,4BAA4B,kBAAkB,CAAC,MAAM,CAAC,SAAS,kBAAkB,CAAC,SAAS,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QACzI,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAC3E,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,2BAA2B,CAAC,MAAc,EAAE,IAAY,EAAE,QAAgB,EAAE,OAAY,EAAE,EAAU;QAC7G,MAAM,IAAI,GAAG,4BAA4B,kBAAkB,CAAC,MAAM,CAAC,UAAU,kBAAkB,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxI,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAC3E,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACJ;AAvDD,wDAuDC"}

View File

@@ -0,0 +1,77 @@
/**
* This is the response format documented in
* https://matrix.org/docs/spec/application_service/r0.1.2#get-matrix-app-v1-thirdparty-protocol-protocol
* @category Application services
*/
export interface IApplicationServiceProtocol {
user_fields: string[];
location_fields: string[];
icon: string;
field_types: {
[field: string]: IFieldType;
};
instances: {
[name: string]: IProtocolInstance;
};
}
interface IFieldType {
regexp: string;
placeholder: string;
}
interface IProtocolInstance {
desc: string;
icon: string;
fields: {
[field: string]: string;
};
network_id: string;
}
/**
* This is the response format for an MSC3983 `/keys/claim` request.
* See https://github.com/matrix-org/matrix-spec-proposals/pull/3983
* @deprecated This can be removed at any time without notice as it is unstable functionality.
* @category Application services
*/
export interface MSC3983KeyClaimResponse {
[userId: string]: {
[deviceId: string]: {
[keyId: string]: {
key: string;
signatures: {
[userId: string]: {
[keyId: string]: string;
};
};
};
};
};
}
/**
* This is the response format for an MSC3984 `/keys/query` request.
* See https://github.com/matrix-org/matrix-spec-proposals/pull/3984
* @deprecated This can be removed at any time without notice as it is unstable functionality.
* @category Application services
*/
export interface MSC3984KeyQueryResponse {
device_keys: {
[userId: string]: {
[deviceId: string]: {
algorithms: string[];
device_id: string;
user_id: string;
keys: {
[keyId: string]: string;
};
signatures: {
[userId: string]: {
[keyId: string]: string;
};
};
unsigned?: {
[key: string]: any;
};
};
};
};
}
export {};

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=http_responses.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http_responses.js","sourceRoot":"","sources":["../../src/appservice/http_responses.ts"],"names":[],"mappings":""}

42
node_modules/@vector-im/matrix-bot-sdk/lib/b64.d.ts generated vendored Normal file
View File

@@ -0,0 +1,42 @@
/**
* Encodes Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
export declare function encodeBase64(b: ArrayBuffer | Uint8Array): string;
/**
* Encodes Unpadded Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
export declare function encodeUnpaddedBase64(b: ArrayBuffer | Uint8Array): string;
/**
* Encodes URL-Safe Unpadded Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
export declare function encodeUnpaddedUrlSafeBase64(b: ArrayBuffer | Uint8Array): string;
/**
* Decodes Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
export declare function decodeBase64(s: string): Uint8Array;
/**
* Decodes Unpadded Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
export declare function decodeUnpaddedBase64(s: string): Uint8Array;
/**
* Decodes URL-Safe Unpadded Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
export declare function decodeUnpaddedUrlSafeBase64(s: string): Uint8Array;

64
node_modules/@vector-im/matrix-bot-sdk/lib/b64.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeUnpaddedUrlSafeBase64 = exports.decodeUnpaddedBase64 = exports.decodeBase64 = exports.encodeUnpaddedUrlSafeBase64 = exports.encodeUnpaddedBase64 = exports.encodeBase64 = void 0;
/**
* Encodes Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
function encodeBase64(b) {
return Buffer.from(b).toString('base64');
}
exports.encodeBase64 = encodeBase64;
/**
* Encodes Unpadded Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
function encodeUnpaddedBase64(b) {
return encodeBase64(b).replace(/=+/g, '');
}
exports.encodeUnpaddedBase64 = encodeUnpaddedBase64;
/**
* Encodes URL-Safe Unpadded Base64.
* @category Utilities
* @param {ArrayBuffer | Uint8Array} b The buffer to encode.
* @returns {string} The Base64 string.
*/
function encodeUnpaddedUrlSafeBase64(b) {
return encodeUnpaddedBase64(b).replace(/\+/g, '-').replace(/\//g, '_');
}
exports.encodeUnpaddedUrlSafeBase64 = encodeUnpaddedUrlSafeBase64;
/**
* Decodes Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
function decodeBase64(s) {
return Buffer.from(s, 'base64');
}
exports.decodeBase64 = decodeBase64;
/**
* Decodes Unpadded Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
function decodeUnpaddedBase64(s) {
return decodeBase64(s); // yay, it's the same
}
exports.decodeUnpaddedBase64 = decodeUnpaddedBase64;
/**
* Decodes URL-Safe Unpadded Base64.
* @category Utilities
* @param {string} s The Base64 string.
* @returns {Uint8Array} The encoded data as a buffer.
*/
function decodeUnpaddedUrlSafeBase64(s) {
return decodeUnpaddedBase64(s.replace(/-/g, '+').replace(/_/g, '/'));
}
exports.decodeUnpaddedUrlSafeBase64 = decodeUnpaddedUrlSafeBase64;
//# sourceMappingURL=b64.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"b64.js","sourceRoot":"","sources":["../src/b64.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,CAA2B;IACpD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAFD,oCAEC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,CAA2B;IAC5D,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAFD,oDAEC;AAED;;;;;GAKG;AACH,SAAgB,2BAA2B,CAAC,CAA2B;IACnE,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC3E,CAAC;AAFD,kEAEC;AAED;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,CAAS;IAClC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAFD,oCAEC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,CAAS;IAC1C,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB;AACjD,CAAC;AAFD,oDAEC;AAED;;;;;GAKG;AACH,SAAgB,2BAA2B,CAAC,CAAS;IACjD,OAAO,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AACzE,CAAC;AAFD,kEAEC"}

View File

@@ -0,0 +1,132 @@
/// <reference types="node" />
import { MatrixClient } from "../MatrixClient";
import { IMegolmEncrypted, IOlmEncrypted, IToDeviceMessage, OTKAlgorithm, OTKCounts, Signatures } from "../models/Crypto";
import { EncryptedRoomEvent } from "../models/events/EncryptedRoomEvent";
import { RoomEvent } from "../models/events/RoomEvent";
import { EncryptedFile } from "../models/events/MessageEvent";
import { IKeyBackupInfoRetrieved } from "../models/KeyBackup";
/**
* Manages encryption for a MatrixClient. Get an instance from a MatrixClient directly
* rather than creating one manually.
* @category Encryption
*/
export declare class CryptoClient {
private client;
private ready;
private deviceId;
private deviceEd25519;
private deviceCurve25519;
private roomTracker;
private engine;
constructor(client: MatrixClient);
private get storage();
/**
* The device ID for the MatrixClient.
*/
get clientDeviceId(): string;
/**
* The device's Ed25519 identity
*/
get clientDeviceEd25519(): string;
/**
* Whether or not the crypto client is ready to be used. If not ready, prepare() should be called.
* @see prepare
*/
get isReady(): boolean;
/**
* Prepares the crypto client for usage.
* @param {string[]} roomIds The room IDs the MatrixClient is joined to.
*/
prepare(): Promise<void>;
/**
* Handles a room event.
* @internal
* @param roomId The room ID.
* @param event The event.
*/
onRoomEvent(roomId: string, event: any): Promise<void>;
/**
* Handles a room join.
* @internal
* @param roomId The room ID.
*/
onRoomJoin(roomId: string): Promise<void>;
/**
* Exports a set of keys for a given session.
* @param roomId The room ID for the session.
* @param sessionId The session ID.
* @returns An array of session keys.
*/
exportRoomKeysForSession(roomId: string, sessionId: string): Promise<import("../models/KeyBackup").IMegolmSessionDataExport[]>;
/**
* Checks if a room is encrypted.
* @param {string} roomId The room ID to check.
* @returns {Promise<boolean>} Resolves to true if encrypted, false otherwise.
*/
isRoomEncrypted(roomId: string): Promise<boolean>;
/**
* Updates the client's sync-related data.
* @param {Array.<IToDeviceMessage<IOlmEncrypted>>} toDeviceMessages The to-device messages received.
* @param {OTKCounts} otkCounts The current OTK counts.
* @param {OTKAlgorithm[]} unusedFallbackKeyAlgs The unused fallback key algorithms.
* @param {string[]} changedDeviceLists The user IDs which had device list changes.
* @param {string[]} leftDeviceLists The user IDs which the server believes we no longer need to track.
* @returns {Promise<void>} Resolves when complete.
*/
updateSyncData(toDeviceMessages: IToDeviceMessage<IOlmEncrypted>[], otkCounts: OTKCounts, unusedFallbackKeyAlgs: OTKAlgorithm[], changedDeviceLists: string[], leftDeviceLists: string[]): Promise<void>;
/**
* Signs an object using the device keys.
* @param {object} obj The object to sign.
* @returns {Promise<Signatures>} The signatures for the object.
*/
sign(obj: object): Promise<Signatures>;
/**
* Encrypts the details of a room event, returning an encrypted payload to be sent in an
* `m.room.encrypted` event to the room. If needed, this function will send decryption keys
* to the appropriate devices in the room (this happens when the Megolm session rotates or
* gets created).
* @param {string} roomId The room ID to encrypt within. If the room is not encrypted, an
* error is thrown.
* @param {string} eventType The event type being encrypted.
* @param {any} content The event content being encrypted.
* @returns {Promise<IMegolmEncrypted>} Resolves to the encrypted content for an `m.room.encrypted` event.
*/
encryptRoomEvent(roomId: string, eventType: string, content: any): Promise<IMegolmEncrypted>;
/**
* Decrypts a room event. Currently only supports Megolm-encrypted events (default for this SDK).
* @param {EncryptedRoomEvent} event The encrypted event.
* @param {string} roomId The room ID where the event was sent.
* @returns {Promise<RoomEvent<unknown>>} Resolves to a decrypted room event, or rejects/throws with
* an error if the event is undecryptable.
*/
decryptRoomEvent(event: EncryptedRoomEvent, roomId: string): Promise<RoomEvent<unknown>>;
/**
* Encrypts a file for uploading in a room, returning the encrypted data and information
* to include in a message event (except media URL) for sending.
* @param {Buffer} file The file to encrypt.
* @returns {{buffer: Buffer, file: Omit<EncryptedFile, "url">}} Resolves to the encrypted
* contents and file information.
*/
encryptMedia(file: Buffer): Promise<{
buffer: Buffer;
file: Omit<EncryptedFile, "url">;
}>;
/**
* Decrypts a previously-uploaded encrypted file, validating the fields along the way.
* @param {EncryptedFile} file The file to decrypt.
* @returns {Promise<Buffer>} Resolves to the decrypted file contents.
*/
decryptMedia(file: EncryptedFile): Promise<Buffer>;
/**
* Enable backing up of room keys.
* @param {IKeyBackupInfoRetrieved} info The configuration for key backup behaviour,
* as returned by {@link MatrixClient#getKeyBackupVersion}.
* @returns {Promise<void>} Resolves once backups have been enabled.
*/
enableKeyBackup(info: IKeyBackupInfoRetrieved): Promise<void>;
/**
* Disable backing up of room keys.
*/
disableKeyBackup(): Promise<void>;
private readonly onToDeviceMessage;
}

View File

@@ -0,0 +1,334 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CryptoClient = void 0;
const matrix_sdk_crypto_nodejs_1 = require("@matrix-org/matrix-sdk-crypto-nodejs");
const LogService_1 = require("../logging/LogService");
const decorators_1 = require("./decorators");
const RoomTracker_1 = require("./RoomTracker");
const EncryptedRoomEvent_1 = require("../models/events/EncryptedRoomEvent");
const RoomEvent_1 = require("../models/events/RoomEvent");
const RustEngine_1 = require("./RustEngine");
const MembershipEvent_1 = require("../models/events/MembershipEvent");
/**
* Manages encryption for a MatrixClient. Get an instance from a MatrixClient directly
* rather than creating one manually.
* @category Encryption
*/
class CryptoClient {
client;
ready = false;
deviceId;
deviceEd25519;
deviceCurve25519;
roomTracker;
engine;
constructor(client) {
this.client = client;
this.roomTracker = new RoomTracker_1.RoomTracker(this.client);
}
get storage() {
return this.client.cryptoStore;
}
/**
* The device ID for the MatrixClient.
*/
get clientDeviceId() {
return this.deviceId;
}
/**
* The device's Ed25519 identity
*/
get clientDeviceEd25519() {
return this.deviceEd25519;
}
/**
* Whether or not the crypto client is ready to be used. If not ready, prepare() should be called.
* @see prepare
*/
get isReady() {
return this.ready;
}
/**
* Prepares the crypto client for usage.
* @param {string[]} roomIds The room IDs the MatrixClient is joined to.
*/
async prepare() {
if (this.ready)
return; // stop re-preparing here
const storedDeviceId = await this.client.cryptoStore.getDeviceId();
const { user_id: userId, device_id: deviceId } = (await this.client.getWhoAmI());
if (!deviceId) {
throw new Error("Encryption not possible: server not revealing device ID");
}
const storagePath = await this.storage.getMachineStoragePath(deviceId);
if (storedDeviceId !== deviceId) {
this.client.cryptoStore.setDeviceId(deviceId);
}
this.deviceId = deviceId;
LogService_1.LogService.info("CryptoClient", `Starting ${userId} with device ID:`, this.deviceId); // info so all bots know for debugging
const machine = await matrix_sdk_crypto_nodejs_1.OlmMachine.initialize(new matrix_sdk_crypto_nodejs_1.UserId(userId), new matrix_sdk_crypto_nodejs_1.DeviceId(this.deviceId), storagePath, "", this.storage.storageType);
this.engine = new RustEngine_1.RustEngine(machine, this.client);
await this.engine.run();
const identity = this.engine.machine.identityKeys;
this.deviceCurve25519 = identity.curve25519.toBase64();
this.deviceEd25519 = identity.ed25519.toBase64();
LogService_1.LogService.info("CryptoClient", `Running ${userId} with device Ed25519 identity:`, this.deviceEd25519); // info so all bots know for debugging
this.ready = true;
}
/**
* Handles a room event.
* @internal
* @param roomId The room ID.
* @param event The event.
*/
async onRoomEvent(roomId, event) {
await this.roomTracker.onRoomEvent(roomId, event);
if (typeof event['state_key'] !== 'string')
return;
if (event['type'] === 'm.room.member') {
const membership = new MembershipEvent_1.MembershipEvent(event);
if (membership.effectiveMembership !== 'join' && membership.effectiveMembership !== 'invite')
return;
await this.engine.addTrackedUsers([membership.membershipFor]);
}
else if (event['type'] === 'm.room.encryption') {
return this.client.getRoomMembers(roomId, null, ['join', 'invite']).then(members => this.engine.addTrackedUsers(members.map(e => e.membershipFor)), e => void LogService_1.LogService.warn("CryptoClient", `Unable to get members of room ${roomId}`));
}
}
/**
* Handles a room join.
* @internal
* @param roomId The room ID.
*/
async onRoomJoin(roomId) {
await this.roomTracker.onRoomJoin(roomId);
if (await this.isRoomEncrypted(roomId)) {
const members = await this.client.getRoomMembers(roomId, null, ['join', 'invite']);
await this.engine.addTrackedUsers(members.map(e => e.membershipFor));
}
}
/**
* Exports a set of keys for a given session.
* @param roomId The room ID for the session.
* @param sessionId The session ID.
* @returns An array of session keys.
*/
async exportRoomKeysForSession(roomId, sessionId) {
return this.engine.exportRoomKeysForSession(roomId, sessionId);
}
/**
* Checks if a room is encrypted.
* @param {string} roomId The room ID to check.
* @returns {Promise<boolean>} Resolves to true if encrypted, false otherwise.
*/
async isRoomEncrypted(roomId) {
const config = await this.roomTracker.getRoomCryptoConfig(roomId);
return !!config?.algorithm;
}
/**
* Updates the client's sync-related data.
* @param {Array.<IToDeviceMessage<IOlmEncrypted>>} toDeviceMessages The to-device messages received.
* @param {OTKCounts} otkCounts The current OTK counts.
* @param {OTKAlgorithm[]} unusedFallbackKeyAlgs The unused fallback key algorithms.
* @param {string[]} changedDeviceLists The user IDs which had device list changes.
* @param {string[]} leftDeviceLists The user IDs which the server believes we no longer need to track.
* @returns {Promise<void>} Resolves when complete.
*/
async updateSyncData(toDeviceMessages, otkCounts, unusedFallbackKeyAlgs, changedDeviceLists, leftDeviceLists) {
const deviceMessages = JSON.stringify(toDeviceMessages);
const deviceLists = new matrix_sdk_crypto_nodejs_1.DeviceLists(changedDeviceLists.map(u => new matrix_sdk_crypto_nodejs_1.UserId(u)), leftDeviceLists.map(u => new matrix_sdk_crypto_nodejs_1.UserId(u)));
await this.engine.lock.acquire(RustEngine_1.SYNC_LOCK_NAME, async () => {
const syncResp = JSON.parse(await this.engine.machine.receiveSyncChanges(deviceMessages, deviceLists, otkCounts, unusedFallbackKeyAlgs));
if (Array.isArray(syncResp) && syncResp.length === 2 && Array.isArray(syncResp[0])) {
for (const msg of syncResp[0]) {
this.client.emit("to_device.decrypted", msg);
}
}
else {
LogService_1.LogService.error("CryptoClient", "OlmMachine.receiveSyncChanges did not return an expected value of [to-device events, room key changes]");
}
await this.engine.run();
});
}
/**
* Signs an object using the device keys.
* @param {object} obj The object to sign.
* @returns {Promise<Signatures>} The signatures for the object.
*/
async sign(obj) {
obj = JSON.parse(JSON.stringify(obj));
const existingSignatures = obj['signatures'] || {};
delete obj['signatures'];
delete obj['unsigned'];
const container = await this.engine.machine.sign(JSON.stringify(obj));
const userSignature = container.get(new matrix_sdk_crypto_nodejs_1.UserId(await this.client.getUserId()));
const sig = {
[await this.client.getUserId()]: {},
};
for (const [key, maybeSignature] of Object.entries(userSignature)) {
if (maybeSignature.isValid) {
sig[await this.client.getUserId()][key] = maybeSignature.signature.toBase64();
}
}
return {
...sig,
...existingSignatures,
};
}
/**
* Encrypts the details of a room event, returning an encrypted payload to be sent in an
* `m.room.encrypted` event to the room. If needed, this function will send decryption keys
* to the appropriate devices in the room (this happens when the Megolm session rotates or
* gets created).
* @param {string} roomId The room ID to encrypt within. If the room is not encrypted, an
* error is thrown.
* @param {string} eventType The event type being encrypted.
* @param {any} content The event content being encrypted.
* @returns {Promise<IMegolmEncrypted>} Resolves to the encrypted content for an `m.room.encrypted` event.
*/
async encryptRoomEvent(roomId, eventType, content) {
if (!(await this.isRoomEncrypted(roomId))) {
throw new Error("Room is not encrypted");
}
await this.engine.prepareEncrypt(roomId, await this.roomTracker.getRoomCryptoConfig(roomId));
const encrypted = JSON.parse(await this.engine.machine.encryptRoomEvent(new matrix_sdk_crypto_nodejs_1.RoomId(roomId), eventType, JSON.stringify(content)));
await this.engine.run();
return encrypted;
}
/**
* Decrypts a room event. Currently only supports Megolm-encrypted events (default for this SDK).
* @param {EncryptedRoomEvent} event The encrypted event.
* @param {string} roomId The room ID where the event was sent.
* @returns {Promise<RoomEvent<unknown>>} Resolves to a decrypted room event, or rejects/throws with
* an error if the event is undecryptable.
*/
async decryptRoomEvent(event, roomId) {
const decrypted = await this.engine.machine.decryptRoomEvent(JSON.stringify(event.raw), new matrix_sdk_crypto_nodejs_1.RoomId(roomId));
const clearEvent = JSON.parse(decrypted.event);
return new RoomEvent_1.RoomEvent({
...event.raw,
type: clearEvent.type || "io.t2bot.unknown",
content: (typeof (clearEvent.content) === 'object') ? clearEvent.content : {},
});
}
/**
* Encrypts a file for uploading in a room, returning the encrypted data and information
* to include in a message event (except media URL) for sending.
* @param {Buffer} file The file to encrypt.
* @returns {{buffer: Buffer, file: Omit<EncryptedFile, "url">}} Resolves to the encrypted
* contents and file information.
*/
async encryptMedia(file) {
const encrypted = matrix_sdk_crypto_nodejs_1.Attachment.encrypt(file);
const info = JSON.parse(encrypted.mediaEncryptionInfo);
return {
buffer: Buffer.from(encrypted.encryptedData),
file: info,
};
}
/**
* Decrypts a previously-uploaded encrypted file, validating the fields along the way.
* @param {EncryptedFile} file The file to decrypt.
* @returns {Promise<Buffer>} Resolves to the decrypted file contents.
*/
async decryptMedia(file) {
const contents = this.client.contentScannerInstance ?
await this.client.contentScannerInstance.downloadEncryptedContent(file) :
(await this.client.downloadContent(file.url)).data;
const encrypted = new matrix_sdk_crypto_nodejs_1.EncryptedAttachment(contents, JSON.stringify(file));
const decrypted = matrix_sdk_crypto_nodejs_1.Attachment.decrypt(encrypted);
return Buffer.from(decrypted);
}
/**
* Enable backing up of room keys.
* @param {IKeyBackupInfoRetrieved} info The configuration for key backup behaviour,
* as returned by {@link MatrixClient#getKeyBackupVersion}.
* @returns {Promise<void>} Resolves once backups have been enabled.
*/
async enableKeyBackup(info) {
if (!this.engine.isBackupEnabled()) {
// Only add the listener if we didn't add it already
this.client.on("to_device.decrypted", this.onToDeviceMessage);
}
await this.engine.enableKeyBackup(info);
// Back up any pending keys now, but asynchronously
void this.engine.backupRoomKeys();
}
/**
* Disable backing up of room keys.
*/
async disableKeyBackup() {
await this.engine.disableKeyBackup();
this.client.removeListener("to_device.decrypted", this.onToDeviceMessage);
}
onToDeviceMessage = (msg) => {
if (msg.type === "m.room_key") {
this.engine.backupRoomKeys();
}
};
}
exports.CryptoClient = CryptoClient;
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "isRoomEncrypted", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Object, Array, Array, Array]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "updateSyncData", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "sign", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "encryptRoomEvent", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [EncryptedRoomEvent_1.EncryptedRoomEvent, String]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "decryptRoomEvent", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Buffer]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "encryptMedia", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "decryptMedia", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "enableKeyBackup", null);
__decorate([
(0, decorators_1.requiresReady)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], CryptoClient.prototype, "disableKeyBackup", null);
//# sourceMappingURL=CryptoClient.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { EncryptionEventContent } from "../models/events/EncryptionEvent";
/**
* Information about a room for the purposes of crypto.
* @category Encryption
*/
export interface ICryptoRoomInformation extends Partial<EncryptionEventContent> {
historyVisibility?: string;
}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ICryptoRoomInformation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ICryptoRoomInformation.js","sourceRoot":"","sources":["../../src/e2ee/ICryptoRoomInformation.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,36 @@
import { MatrixClient } from "../MatrixClient";
import { ICryptoRoomInformation } from "./ICryptoRoomInformation";
/**
* Tracks room encryption status for a MatrixClient.
* @category Encryption
*/
export declare class RoomTracker {
private client;
constructor(client: MatrixClient);
/**
* Handles a room join
* @internal
* @param roomId The room ID.
*/
onRoomJoin(roomId: string): Promise<void>;
/**
* Handles a room event.
* @internal
* @param roomId The room ID.
* @param event The event.
*/
onRoomEvent(roomId: string, event: any): Promise<void>;
/**
* Queues a room check for the tracker. If the room needs an update to the store, an
* update will be made.
* @param {string} roomId The room ID to check.
*/
queueRoomCheck(roomId: string): Promise<void>;
/**
* Gets the room's crypto configuration, as known by the underlying store. If the room is
* not encrypted then this will return an empty object.
* @param {string} roomId The room ID to get the config for.
* @returns {Promise<ICryptoRoomInformation>} Resolves to the encryption config.
*/
getRoomCryptoConfig(roomId: string): Promise<ICryptoRoomInformation>;
}

View File

@@ -0,0 +1,94 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoomTracker = void 0;
const MatrixError_1 = require("../models/MatrixError");
// noinspection ES6RedundantAwait
/**
* Tracks room encryption status for a MatrixClient.
* @category Encryption
*/
class RoomTracker {
client;
constructor(client) {
this.client = client;
}
/**
* Handles a room join
* @internal
* @param roomId The room ID.
*/
async onRoomJoin(roomId) {
await this.queueRoomCheck(roomId);
}
/**
* Handles a room event.
* @internal
* @param roomId The room ID.
* @param event The event.
*/
async onRoomEvent(roomId, event) {
if (event['state_key'] !== '')
return; // we don't care about anything else
if (event['type'] === 'm.room.encryption' || event['type'] === 'm.room.history_visibility') {
await this.queueRoomCheck(roomId);
}
}
/**
* Queues a room check for the tracker. If the room needs an update to the store, an
* update will be made.
* @param {string} roomId The room ID to check.
*/
async queueRoomCheck(roomId) {
const config = await this.client.cryptoStore.getRoom(roomId);
if (config) {
if (config.algorithm !== undefined) {
return; // assume no change to encryption config
}
}
let encEvent;
try {
encEvent = await this.client.getRoomStateEvent(roomId, "m.room.encryption", "");
encEvent.algorithm = encEvent.algorithm ?? 'UNKNOWN';
}
catch (e) {
if (e instanceof MatrixError_1.MatrixError && e.errcode === "M_NOT_FOUND") {
encEvent = {};
}
else {
return; // Other failures should not be cached.
}
}
// Pick out the history visibility setting too
let historyVisibility;
try {
const ev = await this.client.getRoomStateEvent(roomId, "m.room.history_visibility", "");
historyVisibility = ev.history_visibility;
}
catch (e) {
// ignore - we'll just treat history visibility as normal
}
await this.client.cryptoStore.storeRoom(roomId, {
...encEvent,
historyVisibility,
});
}
/**
* Gets the room's crypto configuration, as known by the underlying store. If the room is
* not encrypted then this will return an empty object.
* @param {string} roomId The room ID to get the config for.
* @returns {Promise<ICryptoRoomInformation>} Resolves to the encryption config.
*/
async getRoomCryptoConfig(roomId) {
let config = await this.client.cryptoStore.getRoom(roomId);
if (!config) {
await this.queueRoomCheck(roomId);
config = await this.client.cryptoStore.getRoom(roomId);
}
if (!config) {
return {};
}
return config;
}
}
exports.RoomTracker = RoomTracker;
//# sourceMappingURL=RoomTracker.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RoomTracker.js","sourceRoot":"","sources":["../../src/e2ee/RoomTracker.ts"],"names":[],"mappings":";;;AACA,uDAAoD;AAIpD,iCAAiC;AACjC;;;GAGG;AACH,MAAa,WAAW;IACO;IAA3B,YAA2B,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAC/C,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,UAAU,CAAC,MAAc;QAClC,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,KAAU;QAC/C,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE;YAAE,OAAO,CAAC,oCAAoC;QAC3E,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,mBAAmB,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,2BAA2B,EAAE;YACxF,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;SACrC;IACL,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,cAAc,CAAC,MAAc;QACtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,MAAM,EAAE;YACR,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE;gBAChC,OAAO,CAAC,wCAAwC;aACnD;SACJ;QAED,IAAI,QAAyC,CAAC;QAC9C,IAAI;YACA,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAC;YAChF,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,SAAS,CAAC;SACxD;QAAC,OAAO,CAAC,EAAE;YACR,IAAI,CAAC,YAAY,yBAAW,IAAI,CAAC,CAAC,OAAO,KAAK,aAAa,EAAE;gBACzD,QAAQ,GAAG,EAAE,CAAC;aACjB;iBAAM;gBACH,OAAO,CAAC,uCAAuC;aAClD;SACJ;QAED,8CAA8C;QAC9C,IAAI,iBAAyB,CAAC;QAC9B,IAAI;YACA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,2BAA2B,EAAE,EAAE,CAAC,CAAC;YACxF,iBAAiB,GAAG,EAAE,CAAC,kBAAkB,CAAC;SAC7C;QAAC,OAAO,CAAC,EAAE;YACR,yDAAyD;SAC5D;QAED,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE;YAC5C,GAAG,QAAQ;YACX,iBAAiB;SACpB,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,mBAAmB,CAAC,MAAc;QAC3C,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,EAAE;YACT,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;SAC1D;QACD,IAAI,CAAC,MAAM,EAAE;YACT,OAAO,EAAE,CAAC;SACb;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAnFD,kCAmFC"}

View File

@@ -0,0 +1,40 @@
import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-nodejs";
import * as AsyncLock from "async-lock";
import { MatrixClient } from "../MatrixClient";
import { ICryptoRoomInformation } from "./ICryptoRoomInformation";
import { IKeyBackupInfoRetrieved, IMegolmSessionDataExport } from "../models/KeyBackup";
/**
* @internal
*/
export declare const SYNC_LOCK_NAME = "sync";
/**
* @internal
*/
export declare class RustEngine {
readonly machine: OlmMachine;
private client;
readonly lock: AsyncLock;
readonly trackedUsersToAdd: Set<string>;
addTrackedUsersPromise: Promise<void> | undefined;
private keyBackupVersion;
private keyBackupWaiter;
private backupEnabled;
isBackupEnabled(): boolean;
constructor(machine: OlmMachine, client: MatrixClient);
run(): Promise<void>;
private runOnly;
addTrackedUsers(userIds: string[]): Promise<void>;
prepareEncrypt(roomId: string, roomInfo: ICryptoRoomInformation): Promise<void>;
enableKeyBackup(info: IKeyBackupInfoRetrieved): Promise<void>;
disableKeyBackup(): Promise<void>;
private actuallyDisableKeyBackup;
backupRoomKeys(): Promise<void>;
exportRoomKeysForSession(roomId: string, sessionId: string): Promise<IMegolmSessionDataExport[]>;
private backupRoomKeysIfEnabled;
private actuallyBackupRoomKeys;
private processKeysClaimRequest;
private processKeysUploadRequest;
private processKeysQueryRequest;
private processToDeviceRequest;
private processKeysBackupRequest;
}

View File

@@ -0,0 +1,242 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RustEngine = exports.SYNC_LOCK_NAME = void 0;
const matrix_sdk_crypto_nodejs_1 = require("@matrix-org/matrix-sdk-crypto-nodejs");
const AsyncLock = require("async-lock");
const LogService_1 = require("../logging/LogService");
const Crypto_1 = require("../models/Crypto");
const EncryptionEvent_1 = require("../models/events/EncryptionEvent");
const KeyBackup_1 = require("../models/KeyBackup");
/**
* @internal
*/
exports.SYNC_LOCK_NAME = "sync";
/**
* @internal
*/
class RustEngine {
machine;
client;
lock = new AsyncLock();
trackedUsersToAdd = new Set();
addTrackedUsersPromise;
keyBackupVersion;
keyBackupWaiter = Promise.resolve();
backupEnabled = false;
isBackupEnabled() {
return this.backupEnabled;
}
constructor(machine, client) {
this.machine = machine;
this.client = client;
}
async run() {
await this.runOnly(); // run everything, but with syntactic sugar
}
async runOnly(...types) {
// Note: we should not be running this until it runs out, so cache the value into a variable
const requests = await this.machine.outgoingRequests();
for (const request of requests) {
if (types.length && !types.includes(request.type))
continue;
switch (request.type) {
case 0 /* RequestType.KeysUpload */:
await this.processKeysUploadRequest(request);
break;
case 1 /* RequestType.KeysQuery */:
await this.processKeysQueryRequest(request);
break;
case 2 /* RequestType.KeysClaim */:
await this.processKeysClaimRequest(request);
break;
case 3 /* RequestType.ToDevice */:
await this.processToDeviceRequest(request);
break;
case 5 /* RequestType.RoomMessage */:
throw new Error("Bindings error: Sending room messages is not supported");
case 4 /* RequestType.SignatureUpload */:
throw new Error("Bindings error: Backup feature not possible");
case 6 /* RequestType.KeysBackup */:
await this.processKeysBackupRequest(request);
break;
default:
throw new Error("Bindings error: Unrecognized request type: " + request.type);
}
}
}
async addTrackedUsers(userIds) {
// Add the new set of users to the pool
userIds.forEach(uId => this.trackedUsersToAdd.add(uId));
if (this.addTrackedUsersPromise) {
// If we have a pending promise, don't create another lock requirement.
return;
}
return this.addTrackedUsersPromise = this.lock.acquire(exports.SYNC_LOCK_NAME, async () => {
// Immediately clear this promise so that a new promise is queued up.
this.addTrackedUsersPromise = undefined;
const uids = new Array(this.trackedUsersToAdd.size);
let idx = 0;
for (const u of this.trackedUsersToAdd.values()) {
uids[idx++] = new matrix_sdk_crypto_nodejs_1.UserId(u);
}
// Clear the existing pool
this.trackedUsersToAdd.clear();
await this.machine.updateTrackedUsers(uids);
const keysClaim = await this.machine.getMissingSessions(uids);
if (keysClaim) {
await this.processKeysClaimRequest(keysClaim);
}
});
}
async prepareEncrypt(roomId, roomInfo) {
let memberships = ["join", "invite"];
let historyVis = 1 /* HistoryVisibility.Joined */;
switch (roomInfo.historyVisibility) {
case "world_readable":
historyVis = 3 /* HistoryVisibility.WorldReadable */;
break;
case "invited":
historyVis = 0 /* HistoryVisibility.Invited */;
break;
case "shared":
historyVis = 2 /* HistoryVisibility.Shared */;
break;
case "joined":
default:
memberships = ["join"];
}
const members = new Set();
for (const membership of memberships) {
try {
(await this.client.getRoomMembersByMembership(roomId, membership))
.map(u => new matrix_sdk_crypto_nodejs_1.UserId(u.membershipFor))
.forEach(u => void members.add(u));
}
catch (err) {
LogService_1.LogService.warn("RustEngine", `Failed to get room members for membership type "${membership}" in ${roomId}`, (0, LogService_1.extractRequestError)(err));
}
}
if (members.size === 0) {
return;
}
const membersArray = Array.from(members);
const encEv = new EncryptionEvent_1.EncryptionEvent({
type: "m.room.encryption",
content: roomInfo,
});
const settings = new matrix_sdk_crypto_nodejs_1.EncryptionSettings();
settings.algorithm = roomInfo.algorithm === Crypto_1.EncryptionAlgorithm.MegolmV1AesSha2
? 1 /* RustEncryptionAlgorithm.MegolmV1AesSha2 */
: undefined;
settings.historyVisibility = historyVis;
settings.rotationPeriod = BigInt(encEv.rotationPeriodMs);
settings.rotationPeriodMessages = BigInt(encEv.rotationPeriodMessages);
await this.lock.acquire(exports.SYNC_LOCK_NAME, async () => {
await this.machine.updateTrackedUsers(membersArray); // just in case we missed some
await this.runOnly(1 /* RequestType.KeysQuery */);
const keysClaim = await this.machine.getMissingSessions(membersArray);
if (keysClaim) {
await this.processKeysClaimRequest(keysClaim);
}
});
await this.lock.acquire(roomId, async () => {
const requests = await this.machine.shareRoomKey(new matrix_sdk_crypto_nodejs_1.RoomId(roomId), membersArray, settings);
for (const req of requests) {
await this.processToDeviceRequest(req);
}
// Back up keys asynchronously
void this.backupRoomKeysIfEnabled();
});
}
enableKeyBackup(info) {
this.keyBackupWaiter = this.keyBackupWaiter.then(async () => {
if (this.backupEnabled) {
// Finish any pending backups before changing the backup version/pubkey
await this.actuallyDisableKeyBackup();
}
let publicKey;
switch (info.algorithm) {
case KeyBackup_1.KeyBackupEncryptionAlgorithm.MegolmBackupV1Curve25519AesSha2:
publicKey = info.auth_data.public_key;
break;
default:
throw new Error("Key backup error: cannot enable backups with unsupported backup algorithm " + info.algorithm);
}
await this.machine.enableBackupV1(publicKey, info.version);
this.keyBackupVersion = info.version;
this.backupEnabled = true;
});
return this.keyBackupWaiter;
}
disableKeyBackup() {
this.keyBackupWaiter = this.keyBackupWaiter.then(async () => {
await this.actuallyDisableKeyBackup();
});
return this.keyBackupWaiter;
}
async actuallyDisableKeyBackup() {
await this.machine.disableBackup();
this.keyBackupVersion = undefined;
this.backupEnabled = false;
}
backupRoomKeys() {
this.keyBackupWaiter = this.keyBackupWaiter.then(async () => {
if (!this.backupEnabled) {
throw new Error("Key backup error: attempted to create a backup before having enabled backups");
}
await this.actuallyBackupRoomKeys();
});
return this.keyBackupWaiter;
}
async exportRoomKeysForSession(roomId, sessionId) {
return JSON.parse(await this.machine.exportRoomKeysForSession(roomId, sessionId));
}
backupRoomKeysIfEnabled() {
this.keyBackupWaiter = this.keyBackupWaiter.then(async () => {
if (this.backupEnabled) {
await this.actuallyBackupRoomKeys();
}
});
return this.keyBackupWaiter;
}
async actuallyBackupRoomKeys() {
const request = await this.machine.backupRoomKeys();
if (request) {
await this.processKeysBackupRequest(request);
}
}
async processKeysClaimRequest(request) {
const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/claim", null, JSON.parse(request.body));
await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
}
async processKeysUploadRequest(request) {
const body = JSON.parse(request.body);
// delete body["one_time_keys"]; // use this to test MSC3983
const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/upload", null, body);
await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
}
async processKeysQueryRequest(request) {
const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/query", null, JSON.parse(request.body));
await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
}
async processToDeviceRequest(request) {
const resp = await this.client.sendToDevices(request.eventType, JSON.parse(request.body).messages);
await this.machine.markRequestAsSent(request.txnId, 3 /* RequestType.ToDevice */, JSON.stringify(resp));
}
async processKeysBackupRequest(request) {
let resp;
try {
if (!this.keyBackupVersion) {
throw new Error("Key backup version missing");
}
resp = await this.client.doRequest("PUT", "/_matrix/client/v3/room_keys/keys", { version: this.keyBackupVersion }, JSON.parse(request.body));
}
catch (e) {
this.client.emit("crypto.failed_backup", e);
return;
}
await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
}
}
exports.RustEngine = RustEngine;
//# sourceMappingURL=RustEngine.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
/**
* Flags a MatrixClient function as needing end-to-end encryption enabled.
* @category Encryption
*/
export declare function requiresCrypto(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
/**
* Flags a CryptoClient function as needing the CryptoClient to be ready.
* @category Encryption
*/
export declare function requiresReady(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.requiresReady = exports.requiresCrypto = void 0;
/**
* Flags a MatrixClient function as needing end-to-end encryption enabled.
* @category Encryption
*/
function requiresCrypto() {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const client = this; // eslint-disable-line @typescript-eslint/no-this-alias
if (!client.crypto) {
throw new Error("End-to-end encryption is not enabled");
}
return originalMethod.apply(this, args);
};
};
}
exports.requiresCrypto = requiresCrypto;
/**
* Flags a CryptoClient function as needing the CryptoClient to be ready.
* @category Encryption
*/
function requiresReady() {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
const crypto = this; // eslint-disable-line @typescript-eslint/no-this-alias
if (!crypto.isReady) {
throw new Error("End-to-end encryption has not initialized");
}
return originalMethod.apply(this, args);
};
};
}
exports.requiresReady = requiresReady;
//# sourceMappingURL=decorators.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/e2ee/decorators.ts"],"names":[],"mappings":";;;AAGA;;;GAGG;AACH,SAAgB,cAAc;IAC1B,OAAO,UAAS,MAAW,EAAE,WAAmB,EAAE,UAA8B;QAC5E,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QACxC,UAAU,CAAC,KAAK,GAAG,UAAS,GAAG,IAAW;YACtC,MAAM,MAAM,GAAiB,IAAI,CAAC,CAAC,uDAAuD;YAC1F,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;gBAChB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;aAC3D;YAED,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC;IACN,CAAC,CAAC;AACN,CAAC;AAZD,wCAYC;AAED;;;GAGG;AACH,SAAgB,aAAa;IACzB,OAAO,UAAS,MAAW,EAAE,WAAmB,EAAE,UAA8B;QAC5E,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QACxC,UAAU,CAAC,KAAK,GAAG,UAAS,GAAG,IAAW;YACtC,MAAM,MAAM,GAAiB,IAAI,CAAC,CAAC,uDAAuD;YAC1F,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;gBACjB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;aAChE;YAED,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC;IACN,CAAC,CAAC;AACN,CAAC;AAZD,sCAYC"}

View File

@@ -0,0 +1,37 @@
/**
* Represents a Matrix entity
* @category Utilities
*/
export declare class MatrixEntity {
private fullId;
private entityLocalpart;
private entityDomain;
/**
* Creates a new Matrix entity
* @param {string} fullId The full ID of the entity
*/
constructor(fullId: string);
/**
* The localpart for the entity
*/
get localpart(): string;
/**
* The domain for the entity
*/
get domain(): string;
toString(): string;
}
/**
* Represents a Matrix user ID
* @category Utilities
*/
export declare class UserID extends MatrixEntity {
constructor(userId: string);
}
/**
* Represents a Matrix room alias
* @category Utilities
*/
export declare class RoomAlias extends MatrixEntity {
constructor(alias: string);
}

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoomAlias = exports.UserID = exports.MatrixEntity = void 0;
/**
* Represents a Matrix entity
* @category Utilities
*/
class MatrixEntity {
fullId;
entityLocalpart;
entityDomain;
/**
* Creates a new Matrix entity
* @param {string} fullId The full ID of the entity
*/
constructor(fullId) {
this.fullId = fullId;
if (!fullId)
throw new Error("No entity ID provided");
if (fullId.length < 2)
throw new Error("ID too short");
const parts = fullId.split(/:/g);
this.entityLocalpart = parts[0].substring(1);
this.entityDomain = parts.splice(1).join(':');
}
/**
* The localpart for the entity
*/
get localpart() {
return this.entityLocalpart;
}
/**
* The domain for the entity
*/
get domain() {
return this.entityDomain;
}
// override
toString() {
return this.fullId;
}
}
exports.MatrixEntity = MatrixEntity;
/**
* Represents a Matrix user ID
* @category Utilities
*/
class UserID extends MatrixEntity {
constructor(userId) {
super(userId);
if (!userId.startsWith("@")) {
throw new Error("Not a valid user ID");
}
}
}
exports.UserID = UserID;
/**
* Represents a Matrix room alias
* @category Utilities
*/
class RoomAlias extends MatrixEntity {
constructor(alias) {
super(alias);
if (!alias.startsWith("#")) {
throw new Error("Not a valid room alias");
}
}
}
exports.RoomAlias = RoomAlias;
//# sourceMappingURL=MatrixEntity.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MatrixEntity.js","sourceRoot":"","sources":["../../src/helpers/MatrixEntity.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,YAAY;IAQD;IAPZ,eAAe,CAAS;IACxB,YAAY,CAAS;IAE7B;;;OAGG;IACH,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;QAC9B,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAW,SAAS;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAW,MAAM;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,WAAW;IACJ,QAAQ;QACX,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;CACJ;AAlCD,oCAkCC;AAED;;;GAGG;AACH,MAAa,MAAO,SAAQ,YAAY;IACpC,YAAY,MAAc;QACtB,KAAK,CAAC,MAAM,CAAC,CAAC;QACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACzB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;SAC1C;IACL,CAAC;CACJ;AAPD,wBAOC;AAED;;;GAGG;AACH,MAAa,SAAU,SAAQ,YAAY;IACvC,YAAY,KAAa;QACrB,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;SAC7C;IACL,CAAC;CACJ;AAPD,8BAOC"}

View File

@@ -0,0 +1,22 @@
/**
* Represents a common Matrix glob. This is commonly used
* for server ACLs and similar functions.
* @category Utilities
*/
export declare class MatrixGlob {
/**
* The regular expression which represents this glob.
*/
readonly regex: RegExp;
/**
* Creates a new Matrix Glob
* @param {string} glob The glob to convert. Eg: "*.example.org"
*/
constructor(glob: string);
/**
* Tests the glob against a value, returning true if it matches.
* @param {string} val The value to test.
* @returns {boolean} True if the value matches the glob, false otherwise.
*/
test(val: string): boolean;
}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixGlob = void 0;
const globToRegexp = require("glob-to-regexp");
/**
* Represents a common Matrix glob. This is commonly used
* for server ACLs and similar functions.
* @category Utilities
*/
class MatrixGlob {
/**
* The regular expression which represents this glob.
*/
regex;
/**
* Creates a new Matrix Glob
* @param {string} glob The glob to convert. Eg: "*.example.org"
*/
constructor(glob) {
const globRegex = globToRegexp(glob, {
extended: false,
globstar: false,
});
// We need to convert `?` manually because globToRegexp's extended mode
// does more than we want it to.
const replaced = globRegex.toString().replace(/\\\?/g, ".");
this.regex = new RegExp(replaced.substring(1, replaced.length - 1));
}
/**
* Tests the glob against a value, returning true if it matches.
* @param {string} val The value to test.
* @returns {boolean} True if the value matches the glob, false otherwise.
*/
test(val) {
return this.regex.test(val);
}
}
exports.MatrixGlob = MatrixGlob;
//# sourceMappingURL=MatrixGlob.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MatrixGlob.js","sourceRoot":"","sources":["../../src/helpers/MatrixGlob.ts"],"names":[],"mappings":";;;AAAA,+CAA+C;AAE/C;;;;GAIG;AACH,MAAa,UAAU;IACnB;;OAEG;IACa,KAAK,CAAS;IAE9B;;;OAGG;IACH,YAAY,IAAY;QACpB,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE;YACjC,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,KAAK;SAClB,CAAC,CAAC;QAEH,uEAAuE;QACvE,gCAAgC;QAChC,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;CACJ;AA9BD,gCA8BC"}

View File

@@ -0,0 +1,40 @@
import { MatrixClient } from "../MatrixClient";
/**
* Represents a system for generating a mention pill for an entity.
* @category Utilities
*/
export declare class MentionPill {
private entityPermalink;
private displayName;
private constructor();
/**
* The HTML component of the mention.
*/
get html(): string;
/**
* The plain text component of the mention.
*/
get text(): string;
/**
* Creates a new mention for a user in an optional room.
* @param {string} userId The user ID the mention is for.
* @param {String} inRoomId Optional room ID the user is being mentioned in, for the aesthetics of the mention.
* @param {MatrixClient} client Optional client for creating a more pleasing mention.
* @returns {Promise<MentionPill>} Resolves to the user's mention.
*/
static forUser(userId: string, inRoomId?: string, client?: MatrixClient): Promise<MentionPill>;
/**
* Creates a new mention for a room (not @room, but the room itself to be linked).
* @param {string} roomIdOrAlias The room ID or alias to mention.
* @param {MatrixClient} client Optional client for creating a more pleasing mention.
* @returns {Promise<MentionPill>} Resolves to the room's mention.
*/
static forRoom(roomIdOrAlias: string, client?: MatrixClient): Promise<MentionPill>;
/**
* Creates a mention from static information.
* @param {string} userId The user ID the mention is for.
* @param {string} displayName The user's display name.
* @returns {MentionPill} The mention for the user.
*/
static withDisplayName(userId: string, displayName: string): MentionPill;
}

View File

@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MentionPill = void 0;
const Permalinks_1 = require("./Permalinks");
const __1 = require("..");
/**
* Represents a system for generating a mention pill for an entity.
* @category Utilities
*/
class MentionPill {
entityPermalink;
displayName;
constructor(entityPermalink, displayName) {
this.entityPermalink = entityPermalink;
this.displayName = displayName;
}
/**
* The HTML component of the mention.
*/
get html() {
return `<a href="${this.entityPermalink}">${this.displayName}</a>`;
}
/**
* The plain text component of the mention.
*/
get text() {
return this.displayName;
}
/**
* Creates a new mention for a user in an optional room.
* @param {string} userId The user ID the mention is for.
* @param {String} inRoomId Optional room ID the user is being mentioned in, for the aesthetics of the mention.
* @param {MatrixClient} client Optional client for creating a more pleasing mention.
* @returns {Promise<MentionPill>} Resolves to the user's mention.
*/
static async forUser(userId, inRoomId = null, client = null) {
const permalink = Permalinks_1.Permalinks.forUser(userId);
let displayName = userId;
try {
if (client) {
let profile = null;
if (inRoomId) {
profile = await client.getRoomStateEvent(inRoomId, "m.room.member", userId);
}
if (!profile) {
profile = await client.getUserProfile(userId);
}
if (profile['displayname']) {
displayName = profile['displayname'];
}
}
}
catch (e) {
__1.LogService.warn("MentionPill", "Error getting profile", (0, __1.extractRequestError)(e));
}
return new MentionPill(permalink, displayName);
}
/**
* Creates a new mention for a room (not @room, but the room itself to be linked).
* @param {string} roomIdOrAlias The room ID or alias to mention.
* @param {MatrixClient} client Optional client for creating a more pleasing mention.
* @returns {Promise<MentionPill>} Resolves to the room's mention.
*/
static async forRoom(roomIdOrAlias, client = null) {
let permalink = Permalinks_1.Permalinks.forRoom(roomIdOrAlias);
let displayProp = roomIdOrAlias;
try {
if (client) {
const roomId = await client.resolveRoom(roomIdOrAlias);
const canonicalAlias = await client.getRoomStateEvent(roomId, "m.room.canonical_alias", "");
if (canonicalAlias?.alias) {
displayProp = canonicalAlias.alias;
permalink = Permalinks_1.Permalinks.forRoom(displayProp);
}
}
}
catch (e) {
__1.LogService.warn("MentionPill", "Error getting room information", (0, __1.extractRequestError)(e));
}
return new MentionPill(permalink, displayProp);
}
/**
* Creates a mention from static information.
* @param {string} userId The user ID the mention is for.
* @param {string} displayName The user's display name.
* @returns {MentionPill} The mention for the user.
*/
static withDisplayName(userId, displayName) {
return new MentionPill(Permalinks_1.Permalinks.forUser(userId), displayName || userId);
}
}
exports.MentionPill = MentionPill;
//# sourceMappingURL=MentionPill.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"MentionPill.js","sourceRoot":"","sources":["../../src/helpers/MentionPill.ts"],"names":[],"mappings":";;;AACA,6CAA0C;AAC1C,0BAAqD;AAErD;;;GAGG;AACH,MAAa,WAAW;IACQ;IAAiC;IAA7D,YAA4B,eAAuB,EAAU,WAAmB;QAApD,oBAAe,GAAf,eAAe,CAAQ;QAAU,gBAAW,GAAX,WAAW,CAAQ;IAChF,CAAC;IAED;;OAEG;IACH,IAAW,IAAI;QACX,OAAO,YAAY,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,WAAW,MAAM,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,WAAmB,IAAI,EAAE,SAAuB,IAAI;QAC5F,MAAM,SAAS,GAAG,uBAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,WAAW,GAAG,MAAM,CAAC;QACzB,IAAI;YACA,IAAI,MAAM,EAAE;gBACR,IAAI,OAAO,GAAG,IAAI,CAAC;gBAEnB,IAAI,QAAQ,EAAE;oBACV,OAAO,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;iBAC/E;gBACD,IAAI,CAAC,OAAO,EAAE;oBACV,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;iBACjD;gBAED,IAAI,OAAO,CAAC,aAAa,CAAC,EAAE;oBACxB,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;iBACxC;aACJ;SACJ;QAAC,OAAO,CAAC,EAAE;YACR,cAAU,CAAC,IAAI,CAAC,aAAa,EAAE,uBAAuB,EAAE,IAAA,uBAAmB,EAAC,CAAC,CAAC,CAAC,CAAC;SACnF;QAED,OAAO,IAAI,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAqB,EAAE,SAAuB,IAAI;QAC1E,IAAI,SAAS,GAAG,uBAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,WAAW,GAAG,aAAa,CAAC;QAEhC,IAAI;YACA,IAAI,MAAM,EAAE;gBACR,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;gBACvD,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,CAAC,CAAC;gBAC5F,IAAI,cAAc,EAAE,KAAK,EAAE;oBACvB,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC;oBACnC,SAAS,GAAG,uBAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;iBAC/C;aACJ;SACJ;QAAC,OAAO,CAAC,EAAE;YACR,cAAU,CAAC,IAAI,CAAC,aAAa,EAAE,gCAAgC,EAAE,IAAA,uBAAmB,EAAC,CAAC,CAAC,CAAC,CAAC;SAC5F;QAED,OAAO,IAAI,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,eAAe,CAAC,MAAc,EAAE,WAAmB;QAC7D,OAAO,IAAI,WAAW,CAAC,uBAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,MAAM,CAAC,CAAC;IAC9E,CAAC;CACJ;AAtFD,kCAsFC"}

View File

@@ -0,0 +1,58 @@
/**
* The parts of a permalink.
* @see Permalinks
* @category Utilities
*/
export interface PermalinkParts {
/**
* The room ID or alias the permalink references. May be undefined.
*/
roomIdOrAlias: string;
/**
* The user ID the permalink references. May be undefined.
*/
userId: string;
/**
* The event ID the permalink references. May be undefined.
*/
eventId: string;
/**
* The servers the permalink is routed through. May be undefined or empty.
*/
viaServers: string[];
}
/**
* Functions for handling permalinks
* @category Utilities
*/
export declare class Permalinks {
private constructor();
private static encodeViaArgs;
/**
* Creates a room permalink.
* @param {string} roomIdOrAlias The room ID or alias to create a permalink for.
* @param {string[]} viaServers The servers to route the permalink through.
* @returns {string} A room permalink.
*/
static forRoom(roomIdOrAlias: string, viaServers?: string[]): string;
/**
* Creates a user permalink.
* @param {string} userId The user ID to create a permalink for.
* @returns {string} A user permalink.
*/
static forUser(userId: string): string;
/**
* Creates an event permalink.
* @param {string} roomIdOrAlias The room ID or alias to create a permalink in.
* @param {string} eventId The event ID to reference in the permalink.
* @param {string[]} viaServers The servers to route the permalink through.
* @returns {string} An event permalink.
*/
static forEvent(roomIdOrAlias: string, eventId: string, viaServers?: string[]): string;
/**
* Parses a permalink URL into usable parts.
* @param {string} matrixTo The matrix.to URL to parse.
* @returns {PermalinkParts} The parts of the permalink.
*/
static parseUrl(matrixTo: string): PermalinkParts;
}

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Permalinks = void 0;
/**
* Functions for handling permalinks
* @category Utilities
*/
class Permalinks {
constructor() {
}
// TODO: Encode permalinks
static encodeViaArgs(servers) {
if (!servers || !servers.length)
return "";
return `?via=${servers.join("&via=")}`;
}
/**
* Creates a room permalink.
* @param {string} roomIdOrAlias The room ID or alias to create a permalink for.
* @param {string[]} viaServers The servers to route the permalink through.
* @returns {string} A room permalink.
*/
static forRoom(roomIdOrAlias, viaServers = []) {
return `https://matrix.to/#/${roomIdOrAlias}${Permalinks.encodeViaArgs(viaServers)}`;
}
/**
* Creates a user permalink.
* @param {string} userId The user ID to create a permalink for.
* @returns {string} A user permalink.
*/
static forUser(userId) {
return `https://matrix.to/#/${userId}`;
}
/**
* Creates an event permalink.
* @param {string} roomIdOrAlias The room ID or alias to create a permalink in.
* @param {string} eventId The event ID to reference in the permalink.
* @param {string[]} viaServers The servers to route the permalink through.
* @returns {string} An event permalink.
*/
static forEvent(roomIdOrAlias, eventId, viaServers = []) {
return `https://matrix.to/#/${roomIdOrAlias}/${eventId}${Permalinks.encodeViaArgs(viaServers)}`;
}
/**
* Parses a permalink URL into usable parts.
* @param {string} matrixTo The matrix.to URL to parse.
* @returns {PermalinkParts} The parts of the permalink.
*/
static parseUrl(matrixTo) {
const matrixToRegexp = /^https:\/\/matrix\.to\/#\/(?<entity>[^/?]+)\/?(?<eventId>[^?]+)?(?<query>\?[^]*)?$/;
const url = matrixToRegexp.exec(matrixTo)?.groups;
if (!url) {
throw new Error("Not a valid matrix.to URL");
}
const entity = decodeURIComponent(url.entity);
if (entity[0] === '@') {
return { userId: entity, roomIdOrAlias: undefined, eventId: undefined, viaServers: undefined };
}
else if (entity[0] === '#' || entity[0] === '!') {
return {
userId: undefined,
roomIdOrAlias: entity,
eventId: url.eventId && decodeURIComponent(url.eventId),
viaServers: new URLSearchParams(url.query).getAll('via'),
};
}
else {
throw new Error("Unexpected entity");
}
}
}
exports.Permalinks = Permalinks;
//# sourceMappingURL=Permalinks.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Permalinks.js","sourceRoot":"","sources":["../../src/helpers/Permalinks.ts"],"names":[],"mappings":";;;AA2BA;;;GAGG;AACH,MAAa,UAAU;IACnB;IACA,CAAC;IAED,0BAA0B;IAElB,MAAM,CAAC,aAAa,CAAC,OAAiB;QAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QAE3C,OAAO,QAAQ,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,OAAO,CAAC,aAAqB,EAAE,aAAuB,EAAE;QAClE,OAAO,uBAAuB,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;IACzF,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,OAAO,CAAC,MAAc;QAChC,OAAO,uBAAuB,MAAM,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,QAAQ,CAAC,aAAqB,EAAE,OAAe,EAAE,aAAuB,EAAE;QACpF,OAAO,uBAAuB,aAAa,IAAI,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;IACpG,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,QAAQ,CAAC,QAAgB;QACnC,MAAM,cAAc,GAAG,oFAAoF,CAAC;QAE5G,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClD,IAAI,CAAC,GAAG,EAAE;YACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;SAChD;QAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;YACnB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;SAClG;aAAM,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;YAC/C,OAAO;gBACH,MAAM,EAAE,SAAS;gBACjB,aAAa,EAAE,MAAM;gBACrB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;gBACvD,UAAU,EAAE,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC3D,CAAC;SACL;aAAM;YACH,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;SACxC;IACL,CAAC;CACJ;AArED,gCAqEC"}

View File

@@ -0,0 +1,48 @@
import { MatrixClient, MatrixProfile } from "..";
import { Appservice } from "../appservice/Appservice";
/**
* Functions for avoiding calls to profile endpoints. Useful for bots when
* people are mentioned often or bridges which need profile information
* often.
* @category Utilities
*/
export declare class ProfileCache {
private client;
private cache;
/**
* Creates a new profile cache.
* @param {number} maxEntries The maximum number of entries to cache.
* @param {number} maxAgeMs The maximum age of an entry in milliseconds.
* @param {MatrixClient} client The client to use to get profile updates.
*/
constructor(maxEntries: number, maxAgeMs: number, client: MatrixClient);
private getCacheKey;
/**
* Watch for profile changes to cached entries with the provided client. The
* same client will also be used to update the user's profile in the cache.
* @param {MatrixClient} client The client to watch for profile changes with.
*/
watchWithClient(client: MatrixClient): void;
/**
* Watch for profile changes to cached entries with the provided application
* service. The clientFn will be called to get the relevant client for any
* updates. If the clientFn is null, the appservice's bot user will be used.
* The clientFn takes two arguments: the user ID being updated and the room ID
* they are being updated in (shouldn't be null). The return value should be the
* MatrixClient to use, or null to use the appservice's bot client. The same
* client will be used to update the user's general profile, if that profile
* is cached.
* @param {Appservice} appservice The application service to watch for profile changes with.
* @param {Function} clientFn The function to use to acquire profile updates with. If null, the appservice's bot client will be used.
*/
watchWithAppservice(appservice: Appservice, clientFn?: (userId: string, roomId: string) => MatrixClient): void;
/**
* Gets a profile for a user in optional room.
* @param {string} userId The user ID to get a profile for.
* @param {string|null} roomId Optional room ID to get a per-room profile for the user.
* @returns {Promise<MatrixProfile>} Resolves to the user's profile.
*/
getUserProfile(userId: string, roomId?: string): Promise<MatrixProfile>;
private getUserProfileWith;
private tryUpdateProfile;
}

View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProfileCache = void 0;
const LRU = require("lru-cache");
const __1 = require("..");
const MembershipEvent_1 = require("../models/events/MembershipEvent");
/**
* Functions for avoiding calls to profile endpoints. Useful for bots when
* people are mentioned often or bridges which need profile information
* often.
* @category Utilities
*/
class ProfileCache {
client;
cache;
/**
* Creates a new profile cache.
* @param {number} maxEntries The maximum number of entries to cache.
* @param {number} maxAgeMs The maximum age of an entry in milliseconds.
* @param {MatrixClient} client The client to use to get profile updates.
*/
constructor(maxEntries, maxAgeMs, client) {
this.client = client;
this.cache = new LRU.LRUCache({
max: maxEntries,
ttl: maxAgeMs,
});
}
getCacheKey(userId, roomId) {
return `${userId}@${roomId || '<none>'}`;
}
/**
* Watch for profile changes to cached entries with the provided client. The
* same client will also be used to update the user's profile in the cache.
* @param {MatrixClient} client The client to watch for profile changes with.
*/
watchWithClient(client) {
client.on("room.event", async (roomId, event) => {
if (!event['state_key'] || !event['content'] || event['type'] !== 'm.room.member')
return;
await this.tryUpdateProfile(roomId, new MembershipEvent_1.MembershipEvent(event), client);
});
}
/**
* Watch for profile changes to cached entries with the provided application
* service. The clientFn will be called to get the relevant client for any
* updates. If the clientFn is null, the appservice's bot user will be used.
* The clientFn takes two arguments: the user ID being updated and the room ID
* they are being updated in (shouldn't be null). The return value should be the
* MatrixClient to use, or null to use the appservice's bot client. The same
* client will be used to update the user's general profile, if that profile
* is cached.
* @param {Appservice} appservice The application service to watch for profile changes with.
* @param {Function} clientFn The function to use to acquire profile updates with. If null, the appservice's bot client will be used.
*/
watchWithAppservice(appservice, clientFn = null) {
if (!clientFn)
clientFn = () => appservice.botClient;
appservice.on("room.event", async (roomId, event) => {
if (!event['state_key'] || !event['content'] || event['type'] !== 'm.room.member')
return;
const memberEvent = new MembershipEvent_1.MembershipEvent(event);
let client = clientFn(memberEvent.membershipFor, roomId);
if (!client)
client = appservice.botClient;
await this.tryUpdateProfile(roomId, memberEvent, client);
});
}
/**
* Gets a profile for a user in optional room.
* @param {string} userId The user ID to get a profile for.
* @param {string|null} roomId Optional room ID to get a per-room profile for the user.
* @returns {Promise<MatrixProfile>} Resolves to the user's profile.
*/
async getUserProfile(userId, roomId = null) {
const cacheKey = this.getCacheKey(userId, roomId);
const cached = this.cache.get(cacheKey);
if (cached)
return Promise.resolve(cached);
const profile = await this.getUserProfileWith(userId, roomId, this.client);
this.cache.set(cacheKey, profile);
return profile;
}
async getUserProfileWith(userId, roomId, client) {
try {
if (roomId) {
const membership = await client.getRoomStateEvent(roomId, "m.room.member", userId);
return new __1.MatrixProfile(userId, membership);
}
else {
const profile = await client.getUserProfile(userId);
return new __1.MatrixProfile(userId, profile);
}
}
catch (e) {
__1.LogService.warn("ProfileCache", "Non-fatal error getting user profile. They might not exist.");
__1.LogService.warn("ProfileCache", (0, __1.extractRequestError)(e));
return new __1.MatrixProfile(userId, {});
}
}
async tryUpdateProfile(roomId, memberEvent, client) {
const roomCacheKey = this.getCacheKey(memberEvent.membershipFor, roomId);
const generalCacheKey = this.getCacheKey(memberEvent.membershipFor, null);
if (this.cache.has(roomCacheKey)) {
this.cache.set(roomCacheKey, new __1.MatrixProfile(memberEvent.membershipFor, memberEvent.content));
}
// TODO: Try and figure out semantics for this updating.
// Large accounts could cause hammering on the profile endpoint, but hopefully it is cached by the server.
if (this.cache.has(generalCacheKey)) {
const profile = await this.getUserProfileWith(memberEvent.membershipFor, null, client);
this.cache.set(generalCacheKey, profile);
}
}
}
exports.ProfileCache = ProfileCache;
//# sourceMappingURL=ProfileCache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ProfileCache.js","sourceRoot":"","sources":["../../src/helpers/ProfileCache.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,17 @@
/**
* Helper for creating rich replies.
* @category Utilities
*/
export declare class RichReply {
private constructor();
/**
* Generates the event content required to reply to the provided event with the
* provided text.
* @param {string} roomId the room ID the event being replied to resides in
* @param {any} event the event to reply to
* @param {string} withText the plain text to reply with
* @param {string} withHtml the HTML to reply with
* @returns {any} the content of the event representing the reply
*/
static createFor(roomId: string, event: any, withText: string, withHtml: string): any;
}

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RichReply = void 0;
const sanitizeHtml = require("sanitize-html");
/**
* Helper for creating rich replies.
* @category Utilities
*/
class RichReply {
constructor() {
}
/**
* Generates the event content required to reply to the provided event with the
* provided text.
* @param {string} roomId the room ID the event being replied to resides in
* @param {any} event the event to reply to
* @param {string} withText the plain text to reply with
* @param {string} withHtml the HTML to reply with
* @returns {any} the content of the event representing the reply
*/
static createFor(roomId, event, withText, withHtml) {
const originalBody = (event["content"] ? event["content"]["body"] : "") || "";
let originalHtml = (event["content"] ? event["content"]["formatted_body"] : "") || null;
if (originalHtml === null) {
originalHtml = sanitizeHtml(originalBody);
}
const fallbackText = "> <" + event["sender"] + "> " + originalBody.split("\n").join("\n> ");
const fallbackHtml = "<mx-reply><blockquote>"
+ `<a href="https://matrix.to/#/${roomId}/${event["event_id"]}">In reply to</a> `
+ `<a href="https://matrix.to/#/${event["sender"]}">${event["sender"]}</a>`
+ "<br />" + originalHtml
+ "</blockquote></mx-reply>";
return {
"m.relates_to": {
"m.in_reply_to": {
"event_id": event["event_id"],
},
},
"msgtype": "m.text",
"body": fallbackText + "\n\n" + withText,
"format": "org.matrix.custom.html",
"formatted_body": fallbackHtml + withHtml,
};
}
}
exports.RichReply = RichReply;
//# sourceMappingURL=RichReply.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RichReply.js","sourceRoot":"","sources":["../../src/helpers/RichReply.ts"],"names":[],"mappings":";;;AAAA,8CAA8C;AAE9C;;;GAGG;AACH,MAAa,SAAS;IAClB;IACA,CAAC;IAED;;;;;;;;OAQG;IACI,MAAM,CAAC,SAAS,CAAC,MAAc,EAAE,KAAU,EAAE,QAAgB,EAAE,QAAgB;QAClF,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC9E,IAAI,YAAY,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;QACxF,IAAI,YAAY,KAAK,IAAI,EAAE;YACvB,YAAY,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;SAC7C;QAED,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5F,MAAM,YAAY,GAAG,wBAAwB;cACvC,gCAAgC,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,oBAAoB;cAC/E,gCAAgC,KAAK,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,QAAQ,CAAC,MAAM;cACzE,QAAQ,GAAG,YAAY;cACvB,0BAA0B,CAAC;QAEjC,OAAO;YACH,cAAc,EAAE;gBACZ,eAAe,EAAE;oBACb,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC;iBAChC;aACJ;YACD,SAAS,EAAE,QAAQ;YACnB,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,QAAQ;YACxC,QAAQ,EAAE,wBAAwB;YAClC,gBAAgB,EAAE,YAAY,GAAG,QAAQ;SAC5C,CAAC;IACN,CAAC;CACJ;AAvCD,8BAuCC"}

View File

@@ -0,0 +1,6 @@
export type Json = string | number | boolean | null | undefined | Json[] | {
[key: string]: Json;
};
export interface IJsonType {
[key: string]: Json;
}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=Types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Types.js","sourceRoot":"","sources":["../../src/helpers/Types.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,32 @@
/// <reference types="node" />
/**
* Unpadded Base64 utilities for Matrix.
* @category Utilities
*/
export declare class UnpaddedBase64 {
private constructor();
/**
* Encodes a buffer to Unpadded Base64
* @param {Buffer} buf The buffer to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeBuffer(buf: Buffer): string;
/**
* Encodes a string to Unpadded Base64
* @param {string} str The string to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeString(str: string): string;
/**
* Encodes a buffer to Unpadded Base64 (URL Safe Edition)
* @param {Buffer} buf The buffer to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeBufferUrlSafe(buf: Buffer): string;
/**
* Encodes a string to Unpadded Base64 (URL Safe Edition)
* @param {string} str The string to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeStringUrlSafe(str: string): string;
}

View File

@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnpaddedBase64 = void 0;
/**
* Unpadded Base64 utilities for Matrix.
* @category Utilities
*/
class UnpaddedBase64 {
constructor() {
}
/**
* Encodes a buffer to Unpadded Base64
* @param {Buffer} buf The buffer to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeBuffer(buf) {
return buf.toString('base64').replace(/[=]*$/g, '');
}
/**
* Encodes a string to Unpadded Base64
* @param {string} str The string to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeString(str) {
return UnpaddedBase64.encodeBuffer(Buffer.from(str));
}
/**
* Encodes a buffer to Unpadded Base64 (URL Safe Edition)
* @param {Buffer} buf The buffer to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeBufferUrlSafe(buf) {
return UnpaddedBase64.encodeBuffer(buf).replace(/\+/g, '-').replace(/\//g, '_');
}
/**
* Encodes a string to Unpadded Base64 (URL Safe Edition)
* @param {string} str The string to encode.
* @returns {string} The Unpadded Base64 string
*/
static encodeStringUrlSafe(str) {
return UnpaddedBase64.encodeBufferUrlSafe(Buffer.from(str));
}
}
exports.UnpaddedBase64 = UnpaddedBase64;
//# sourceMappingURL=UnpaddedBase64.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"UnpaddedBase64.js","sourceRoot":"","sources":["../../src/helpers/UnpaddedBase64.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,cAAc;IACvB;IACA,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,YAAY,CAAC,GAAW;QAClC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,YAAY,CAAC,GAAW;QAClC,OAAO,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,mBAAmB,CAAC,GAAW;QACzC,OAAO,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpF,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,mBAAmB,CAAC,GAAW;QACzC,OAAO,cAAc,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;CACJ;AAvCD,wCAuCC"}

20
node_modules/@vector-im/matrix-bot-sdk/lib/http.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
export interface DoHttpRequestOpts {
errorHandler?: (response: any, body: any) => Error | undefined;
}
/**
* Performs a web request to a server.
* @category Unit testing
* @param {string} baseUrl The base URL to apply to the call.
* @param {"GET"|"POST"|"PUT"|"DELETE"} method The HTTP method to use in the request
* @param {string} endpoint The endpoint to call. For example: "/_matrix/client/v3/account/whoami"
* @param {any} qs The query string to send. Optional.
* @param {any} body The request body to send. Optional. Will be converted to JSON unless the type is a Buffer.
* @param {any} headers Additional headers to send in the request.
* @param {number} timeout The number of milliseconds to wait before timing out.
* @param {boolean} raw If true, the raw response will be returned instead of the response body.
* @param {string} contentType The content type to send. Only used if the `body` is a Buffer.
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
export declare function doHttpRequest(baseUrl: string, method: "GET" | "POST" | "PUT" | "DELETE", endpoint: string, qs?: any, body?: any, headers?: {}, timeout?: number, raw?: boolean, contentType?: string, noEncoding?: boolean, opts?: DoHttpRequestOpts): Promise<any>;
export declare function redactObjectForLogging(input: any): any;

155
node_modules/@vector-im/matrix-bot-sdk/lib/http.js generated vendored Normal file
View File

@@ -0,0 +1,155 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.redactObjectForLogging = exports.doHttpRequest = void 0;
const LogService_1 = require("./logging/LogService");
const request_1 = require("./request");
const MatrixError_1 = require("./models/MatrixError");
let lastRequestId = 0;
const defaultErrorHandler = (response, errBody) => {
return typeof (errBody) === "object" && 'errcode' in errBody ?
new MatrixError_1.MatrixError(errBody, response.statusCode, response.headers) : undefined;
};
/**
* Performs a web request to a server.
* @category Unit testing
* @param {string} baseUrl The base URL to apply to the call.
* @param {"GET"|"POST"|"PUT"|"DELETE"} method The HTTP method to use in the request
* @param {string} endpoint The endpoint to call. For example: "/_matrix/client/v3/account/whoami"
* @param {any} qs The query string to send. Optional.
* @param {any} body The request body to send. Optional. Will be converted to JSON unless the type is a Buffer.
* @param {any} headers Additional headers to send in the request.
* @param {number} timeout The number of milliseconds to wait before timing out.
* @param {boolean} raw If true, the raw response will be returned instead of the response body.
* @param {string} contentType The content type to send. Only used if the `body` is a Buffer.
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
async function doHttpRequest(baseUrl, method, endpoint, qs = null, body = null, headers = {}, timeout = 60000, raw = false, contentType = "application/json", noEncoding = false, opts = {
errorHandler: defaultErrorHandler,
}) {
if (!endpoint.startsWith('/')) {
endpoint = '/' + endpoint;
}
const requestId = ++lastRequestId;
const url = baseUrl + endpoint;
// This is logged at info so that when a request fails people can figure out which one.
LogService_1.LogService.debug("MatrixHttpClient", "(REQ-" + requestId + ")", method + " " + url);
// Don't log the request unless we're in debug mode. It can be large.
if (LogService_1.LogService.level.includes(LogService_1.LogLevel.TRACE)) {
if (qs)
LogService_1.LogService.trace("MatrixHttpClient", "(REQ-" + requestId + ")", "qs = " + JSON.stringify(qs));
if (body && !Buffer.isBuffer(body))
LogService_1.LogService.trace("MatrixHttpClient", "(REQ-" + requestId + ")", "body = " + JSON.stringify(redactObjectForLogging(body)));
if (body && Buffer.isBuffer(body))
LogService_1.LogService.trace("MatrixHttpClient", "(REQ-" + requestId + ")", "body = <Buffer>");
}
const params = {
uri: url,
method: method,
qs: qs,
// If this is undefined, then a string will be returned. If it's null, a Buffer will be returned.
encoding: noEncoding === false ? undefined : null,
useQuerystring: true,
qsStringifyOptions: {
options: { arrayFormat: 'repeat' },
},
timeout: timeout,
headers: headers,
// Enable KeepAlive for HTTP
forever: true,
};
if (body) {
if (Buffer.isBuffer(body)) {
params.headers["Content-Type"] = contentType;
params.body = body;
}
else {
params.headers["Content-Type"] = "application/json";
params.body = JSON.stringify(body);
}
}
const { response, resBody } = await new Promise((resolve, reject) => {
(0, request_1.getRequestFn)()(params, (err, res, rBody) => {
if (err) {
LogService_1.LogService.error("MatrixHttpClient", "(REQ-" + requestId + ")", err);
reject(err);
return;
}
if (typeof (rBody) === 'string') {
try {
rBody = JSON.parse(rBody);
}
catch (e) {
}
}
if (typeof (res.body) === 'string') {
try {
res.body = JSON.parse(res.body);
}
catch (e) {
}
}
resolve({ response: res, resBody: rBody });
});
});
const respIsBuffer = (response.body instanceof Buffer);
// Check for errors.
const errBody = response.body || resBody;
const handledError = opts.errorHandler(response, errBody);
if (handledError) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(errBody);
LogService_1.LogService.error("MatrixHttpClient", "(REQ-" + requestId + ")", redactedBody);
throw handledError;
}
// Don't log the body unless we're in debug mode. They can be large.
if (LogService_1.LogService.level.includes(LogService_1.LogLevel.TRACE)) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService_1.LogService.trace("MatrixHttpClient", "(REQ-" + requestId + " RESP-H" + response.statusCode + ")", redactedBody);
}
if (response.statusCode < 200 || response.statusCode >= 300) {
const redactedBody = respIsBuffer ? '<Buffer>' : redactObjectForLogging(response.body);
LogService_1.LogService.error("MatrixHttpClient", "(REQ-" + requestId + ")", redactedBody);
throw response;
}
return raw ? response : resBody;
}
exports.doHttpRequest = doHttpRequest;
function redactObjectForLogging(input) {
if (!input)
return input;
const fieldsToRedact = [
'access_token',
'password',
'new_password',
];
const redactFn = (i) => {
if (!i)
return i;
// Don't treat strings like arrays/objects
if (typeof i === 'string')
return i;
if (Array.isArray(i)) {
const rebuilt = [];
for (const v of i) {
rebuilt.push(redactFn(v));
}
return rebuilt;
}
if (i instanceof Object) {
const rebuilt = {};
for (const key of Object.keys(i)) {
if (fieldsToRedact.includes(key)) {
rebuilt[key] = '<redacted>';
}
else {
rebuilt[key] = redactFn(i[key]);
}
}
return rebuilt;
}
return i; // It's a primitive value
};
return redactFn(input);
}
exports.redactObjectForLogging = redactObjectForLogging;
//# sourceMappingURL=http.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,93 @@
import { OpenIDConnectToken } from "../models/OpenIDConnect";
import { Policies } from "../models/Policies";
import { Metrics } from "../metrics/Metrics";
import { Threepid } from "../models/Threepid";
import { MatrixClient } from "../MatrixClient";
import { IdentityServerAccount, IdentityServerInvite } from "../models/IdentityServerModels";
/**
* A way to access an Identity Server using the Identity Service API from a MatrixClient.
* @category Identity Servers
*/
export declare class IdentityClient {
readonly accessToken: string;
readonly serverUrl: string;
readonly matrixClient: MatrixClient;
/**
* The metrics instance for this client. Note that metrics for the underlying MatrixClient will
* not be available here.
*/
readonly metrics: Metrics;
/**
* If truthy, this is a string that will be supplied as `?brand=$brand` where endpoints can
* result in communications to a user.
*/
brand: string;
private constructor();
/**
* Gets account information for the logged in user.
* @returns {Promise<IdentityServerAccount>} Resolves to the account information
*/
getAccount(): Promise<IdentityServerAccount>;
/**
* Gets the terms of service for which the identity server has.
* @returns {Promise<Policies>} Resolves to the policies of the server.
*/
getTermsOfService(): Promise<Policies>;
/**
* Accepts a given set of URLs from Policy objects returned by the server. This implies acceptance of
* the terms. Note that this will not update the user's account data to consider these terms accepted
* in the future - that is an exercise left to the caller.
* @param {string[]} termsUrls The URLs to count as accepted.
* @returns {Promise<void>} Resolves when complete.
*/
acceptTerms(termsUrls: string[]): Promise<void>;
/**
* Accepts all the terms of service offered by the identity server. Note that this is only meant to be
* used by automated bots where terms acceptance is implicit - the terms of service need to be presented
* to the user in most cases.
* @returns {Promise<void>} Resolves when complete.
*/
acceptAllTerms(): Promise<void>;
/**
* Looks up a series of third party identifiers (email addresses or phone numbers) to see if they have
* associated mappings. The returned array will be ordered the same as the input, and have falsey values
* in place of any failed/missing lookups (eg: no mapping).
* @param {Threepid[]} identifiers The identifiers to look up.
* @param {boolean} allowPlaintext If true, the function will accept the server's offer to use plaintext
* lookups when no other methods are available. The function will always prefer hashed methods.
* @returns {Promise<string[]>} Resolves to the user IDs (or falsey values) in the same order as the input.
*/
lookup(identifiers: Threepid[], allowPlaintext?: boolean): Promise<string[]>;
/**
* Creates a third party email invite. This will store the invite in the identity server, but
* not publish the invite to the room - the caller is expected to handle the remaining part. Note
* that this function is not required to be called when using the Client-Server API for inviting
* third party addresses to a room. This will make several calls into the room state to populate
* the invite details, therefore the inviter (the client backing this identity client) must be
* present in the room.
* @param {string} emailAddress The email address to invite.
* @param {string} roomId The room ID to invite to.
* @returns {Promise<IdentityServerInvite>} Resolves to the identity server's stored invite.
*/
makeEmailInvite(emailAddress: string, roomId: string): Promise<IdentityServerInvite>;
/**
* Performs a web request to the server, applying appropriate authorization headers for
* this client.
* @param {"GET"|"POST"|"PUT"|"DELETE"} method The HTTP method to use in the request
* @param {string} endpoint The endpoint to call. For example: "/_matrix/identity/v2/account"
* @param {any} qs The query string to send. Optional.
* @param {any} body The request body to send. Optional. Will be converted to JSON unless the type is a Buffer.
* @param {number} timeout The number of milliseconds to wait before timing out.
* @param {boolean} raw If true, the raw response will be returned instead of the response body.
* @param {string} contentType The content type to send. Only used if the `body` is a Buffer.
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
doRequest(method: any, endpoint: any, qs?: any, body?: any, timeout?: number, raw?: boolean, contentType?: string, noEncoding?: boolean): Promise<any>;
/**
* Gets an instance of an identity client.
* @param {OpenIDConnectToken} oidc The OpenID Connect token to register to the identity server with.
* @param {string} serverUrl The full URL where the identity server can be reached at.
*/
static acquire(oidc: OpenIDConnectToken, serverUrl: string, mxClient: MatrixClient): Promise<IdentityClient>;
}

View File

@@ -0,0 +1,261 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IdentityClient = void 0;
const crypto = require("crypto");
const http_1 = require("../http");
const decorators_1 = require("../metrics/decorators");
const Metrics_1 = require("../metrics/Metrics");
const UnpaddedBase64_1 = require("../helpers/UnpaddedBase64");
const MatrixProfile_1 = require("../models/MatrixProfile");
/**
* A way to access an Identity Server using the Identity Service API from a MatrixClient.
* @category Identity Servers
*/
class IdentityClient {
accessToken;
serverUrl;
matrixClient;
/**
* The metrics instance for this client. Note that metrics for the underlying MatrixClient will
* not be available here.
*/
metrics;
/**
* If truthy, this is a string that will be supplied as `?brand=$brand` where endpoints can
* result in communications to a user.
*/
brand;
constructor(accessToken, serverUrl, matrixClient) {
this.accessToken = accessToken;
this.serverUrl = serverUrl;
this.matrixClient = matrixClient;
this.metrics = new Metrics_1.Metrics();
}
/**
* Gets account information for the logged in user.
* @returns {Promise<IdentityServerAccount>} Resolves to the account information
*/
getAccount() {
return this.doRequest("GET", "/_matrix/identity/v2/account");
}
/**
* Gets the terms of service for which the identity server has.
* @returns {Promise<Policies>} Resolves to the policies of the server.
*/
getTermsOfService() {
return this.doRequest("GET", "/_matrix/identity/v2/terms");
}
/**
* Accepts a given set of URLs from Policy objects returned by the server. This implies acceptance of
* the terms. Note that this will not update the user's account data to consider these terms accepted
* in the future - that is an exercise left to the caller.
* @param {string[]} termsUrls The URLs to count as accepted.
* @returns {Promise<void>} Resolves when complete.
*/
acceptTerms(termsUrls) {
return this.doRequest("POST", "/_matrix/identity/v2/terms", null, {
user_accepts: termsUrls,
});
}
/**
* Accepts all the terms of service offered by the identity server. Note that this is only meant to be
* used by automated bots where terms acceptance is implicit - the terms of service need to be presented
* to the user in most cases.
* @returns {Promise<void>} Resolves when complete.
*/
async acceptAllTerms() {
const terms = await this.getTermsOfService();
const urls = new Set();
for (const policy of Object.values(terms.policies)) {
let chosenLang = policy["en"];
if (!chosenLang) {
chosenLang = policy[Object.keys(policy).find(k => k !== "version")];
}
if (!chosenLang)
continue; // skip - invalid
urls.add(chosenLang.url);
}
return await this.acceptTerms(Array.from(urls));
}
/**
* Looks up a series of third party identifiers (email addresses or phone numbers) to see if they have
* associated mappings. The returned array will be ordered the same as the input, and have falsey values
* in place of any failed/missing lookups (eg: no mapping).
* @param {Threepid[]} identifiers The identifiers to look up.
* @param {boolean} allowPlaintext If true, the function will accept the server's offer to use plaintext
* lookups when no other methods are available. The function will always prefer hashed methods.
* @returns {Promise<string[]>} Resolves to the user IDs (or falsey values) in the same order as the input.
*/
async lookup(identifiers, allowPlaintext = false) {
const hashInfo = await this.doRequest("GET", "/_matrix/identity/v2/hash_details");
if (!hashInfo?.["algorithms"])
throw new Error("Server not supported: invalid response");
const algorithms = hashInfo?.["algorithms"];
let algorithm = algorithms.find(a => a === "sha256");
if (!algorithm && allowPlaintext)
algorithm = algorithms.find(a => a === "none");
if (!algorithm)
throw new Error("No supported hashing algorithm found");
const body = {
algorithm,
pepper: hashInfo["lookup_pepper"],
addresses: [],
};
for (const pid of identifiers) {
let transformed = null;
switch (algorithm) {
case "none":
transformed = `${pid.address.toLowerCase()} ${pid.kind}`;
break;
case "sha256":
transformed = UnpaddedBase64_1.UnpaddedBase64.encodeBufferUrlSafe(crypto.createHash("sha256")
.update(`${pid.address.toLowerCase()} ${pid.kind} ${body.pepper}`).digest());
break;
default:
throw new Error("Unsupported hashing algorithm (programming error)");
}
body.addresses.push(transformed);
}
const resp = await this.doRequest("POST", "/_matrix/identity/v2/lookup", null, body);
const mappings = resp?.["mappings"] || {};
const mxids = [];
for (const addr of body.addresses) {
mxids.push(mappings[addr]);
}
return mxids;
}
/**
* Creates a third party email invite. This will store the invite in the identity server, but
* not publish the invite to the room - the caller is expected to handle the remaining part. Note
* that this function is not required to be called when using the Client-Server API for inviting
* third party addresses to a room. This will make several calls into the room state to populate
* the invite details, therefore the inviter (the client backing this identity client) must be
* present in the room.
* @param {string} emailAddress The email address to invite.
* @param {string} roomId The room ID to invite to.
* @returns {Promise<IdentityServerInvite>} Resolves to the identity server's stored invite.
*/
async makeEmailInvite(emailAddress, roomId) {
const req = {
address: emailAddress,
medium: "email",
room_id: roomId,
sender: await this.matrixClient.getUserId(),
};
const tryFetch = async (eventType, stateKey) => {
try {
return await this.matrixClient.getRoomStateEvent(roomId, eventType, stateKey);
}
catch (e) {
return null;
}
};
const canonicalAlias = (await tryFetch("m.room.canonical_alias", ""))?.["alias"];
const roomName = (await tryFetch("m.room.name", ""))?.["name"];
req["room_alias"] = canonicalAlias;
req["room_avatar_url"] = (await tryFetch("m.room.avatar", ""))?.["url"];
req["room_name"] = roomName || canonicalAlias;
req["room_join_rules"] = (await tryFetch("m.room.join_rules", ""))?.["join_rule"];
let profileInfo;
try {
profileInfo = await this.matrixClient.getUserProfile(await this.matrixClient.getUserId());
}
catch (e) {
// ignore
}
const senderProfile = new MatrixProfile_1.MatrixProfile(await this.matrixClient.getUserId(), profileInfo);
req["sender_avatar_url"] = senderProfile.avatarUrl;
req["sender_display_name"] = senderProfile.displayName;
const inviteReq = {};
for (const entry of Object.entries(req)) {
if (entry[1])
inviteReq[entry[0]] = entry[1];
}
const qs = {};
if (this.brand)
qs['brand'] = this.brand;
return await this.doRequest("POST", "/_matrix/identity/v2/store-invite", qs, inviteReq);
}
/**
* Performs a web request to the server, applying appropriate authorization headers for
* this client.
* @param {"GET"|"POST"|"PUT"|"DELETE"} method The HTTP method to use in the request
* @param {string} endpoint The endpoint to call. For example: "/_matrix/identity/v2/account"
* @param {any} qs The query string to send. Optional.
* @param {any} body The request body to send. Optional. Will be converted to JSON unless the type is a Buffer.
* @param {number} timeout The number of milliseconds to wait before timing out.
* @param {boolean} raw If true, the raw response will be returned instead of the response body.
* @param {string} contentType The content type to send. Only used if the `body` is a Buffer.
* @param {string} noEncoding Set to true to disable encoding, and return a Buffer. Defaults to false
* @returns {Promise<any>} Resolves to the response (body), rejected if a non-2xx status code was returned.
*/
doRequest(method, endpoint, qs = null, body = null, timeout = 60000, raw = false, contentType = "application/json", noEncoding = false) {
const headers = {};
if (this.accessToken) {
headers["Authorization"] = `Bearer ${this.accessToken}`;
}
return (0, http_1.doHttpRequest)(this.serverUrl, method, endpoint, qs, body, headers, timeout, raw, contentType, noEncoding);
}
/**
* Gets an instance of an identity client.
* @param {OpenIDConnectToken} oidc The OpenID Connect token to register to the identity server with.
* @param {string} serverUrl The full URL where the identity server can be reached at.
*/
static async acquire(oidc, serverUrl, mxClient) {
const account = await (0, http_1.doHttpRequest)(serverUrl, "POST", "/_matrix/identity/v2/account/register", null, oidc);
return new IdentityClient(account['token'], serverUrl, mxClient);
}
}
exports.IdentityClient = IdentityClient;
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "getAccount", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "getTermsOfService", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array]),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "acceptTerms", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "acceptAllTerms", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Object]),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "lookup", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "makeEmailInvite", null);
__decorate([
(0, decorators_1.timedIdentityClientFunctionCall)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object, Object]),
__metadata("design:returntype", Promise)
], IdentityClient.prototype, "doRequest", null);
//# sourceMappingURL=IdentityClient.js.map

File diff suppressed because one or more lines are too long

92
node_modules/@vector-im/matrix-bot-sdk/lib/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,92 @@
export * from "./appservice/Appservice";
export * from "./appservice/Intent";
export * from "./appservice/MatrixBridge";
export * from "./appservice/http_responses";
export * from "./appservice/UnstableAppserviceApis";
export * from "./e2ee/RoomTracker";
export * from "./e2ee/CryptoClient";
export * from "./e2ee/decorators";
export * from "./e2ee/ICryptoRoomInformation";
export * from "./helpers/RichReply";
export * from "./helpers/MentionPill";
export * from "./helpers/Permalinks";
export * from "./helpers/MatrixGlob";
export * from "./helpers/ProfileCache";
export * from "./helpers/MatrixEntity";
export * from "./helpers/UnpaddedBase64";
export * from "./logging/ConsoleLogger";
export * from "./logging/RichConsoleLogger";
export * from "./logging/ILogger";
export * from "./logging/LogService";
export * from "./metrics/contexts";
export * from "./metrics/names";
export * from "./metrics/decorators";
export * from "./metrics/IMetricListener";
export * from "./metrics/Metrics";
export * from "./mixins/AutojoinRoomsMixin";
export * from "./mixins/AutojoinUpgradedRoomsMixin";
export * from "./models/Presence";
export * from "./models/MatrixProfile";
export * from "./models/EventContext";
export * from "./models/PowerLevelBounds";
export * from "./models/OpenIDConnect";
export * from "./models/Policies";
export * from "./models/Threepid";
export * from "./models/Spaces";
export * from "./models/IdentityServerModels";
export * from "./models/Crypto";
export * from "./models/MSC2176";
export * from "./models/Account";
export * from "./models/PowerLevelAction";
export * from "./models/ServerVersions";
export * from "./models/MatrixError";
export * from "./models/CreateRoom";
export * from "./models/MXCUrl";
export * from "./models/unstable/MediaInfo";
export * from "./models/events/EventKind";
export * from "./models/events/converter";
export * from "./models/events/InvalidEventError";
export * from "./models/events/Event";
export * from "./models/events/RoomEvent";
export * from "./models/events/PresenceEvent";
export * from "./models/events/MembershipEvent";
export * from "./models/events/MessageEvent";
export * from "./models/events/AliasesEvent";
export * from "./models/events/CanonicalAliasEvent";
export * from "./models/events/CreateEvent";
export * from "./models/events/JoinRulesEvent";
export * from "./models/events/PowerLevelsEvent";
export * from "./models/events/RedactionEvent";
export * from "./models/events/PinnedEventsEvent";
export * from "./models/events/RoomAvatarEvent";
export * from "./models/events/RoomNameEvent";
export * from "./models/events/RoomTopicEvent";
export * from "./models/events/SpaceChildEvent";
export * from "./models/events/EncryptionEvent";
export * from "./models/events/EncryptedRoomEvent";
export * from "./preprocessors/IPreprocessor";
export * from "./preprocessors/RichRepliesPreprocessor";
export * from "./storage/IAppserviceStorageProvider";
export * from "./storage/IStorageProvider";
export * from "./storage/MemoryStorageProvider";
export * from "./storage/SimpleFsStorageProvider";
export * from "./storage/ICryptoStorageProvider";
export * from "./storage/RustSdkCryptoStorageProvider";
export * from "./storage/SimplePostgresStorageProvider";
export * from "./strategies/AppserviceJoinRoomStrategy";
export * from "./strategies/JoinRoomStrategy";
export * from "./identity/IdentityClient";
export * from "./IFilter";
export * from "./MatrixClient";
export * from "./MatrixAuth";
export * from "./UnstableApis";
export * from "./AdminApis";
export * from "./request";
export * from "./PantalaimonClient";
export * from "./SynchronousMatrixClient";
export * from "./SynapseAdminApis";
export * from "./MatrixContentScannerClient";
export * from "./simple-validation";
export * from "./b64";
export * from "./http";
export * from "./DMs";

124
node_modules/@vector-im/matrix-bot-sdk/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,124 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
// Appservices
__exportStar(require("./appservice/Appservice"), exports);
__exportStar(require("./appservice/Intent"), exports);
__exportStar(require("./appservice/MatrixBridge"), exports);
__exportStar(require("./appservice/http_responses"), exports);
__exportStar(require("./appservice/UnstableAppserviceApis"), exports);
// Encryption
__exportStar(require("./e2ee/RoomTracker"), exports);
__exportStar(require("./e2ee/CryptoClient"), exports);
__exportStar(require("./e2ee/decorators"), exports);
// export * from "./e2ee/RustEngine";
__exportStar(require("./e2ee/ICryptoRoomInformation"), exports);
// Helpers
__exportStar(require("./helpers/RichReply"), exports);
__exportStar(require("./helpers/MentionPill"), exports);
__exportStar(require("./helpers/Permalinks"), exports);
__exportStar(require("./helpers/MatrixGlob"), exports);
__exportStar(require("./helpers/ProfileCache"), exports);
__exportStar(require("./helpers/MatrixEntity"), exports);
__exportStar(require("./helpers/UnpaddedBase64"), exports);
// Logging
__exportStar(require("./logging/ConsoleLogger"), exports);
__exportStar(require("./logging/RichConsoleLogger"), exports);
__exportStar(require("./logging/ILogger"), exports);
__exportStar(require("./logging/LogService"), exports);
// Metrics
__exportStar(require("./metrics/contexts"), exports);
__exportStar(require("./metrics/names"), exports);
__exportStar(require("./metrics/decorators"), exports);
__exportStar(require("./metrics/IMetricListener"), exports);
__exportStar(require("./metrics/Metrics"), exports);
// Mixins
__exportStar(require("./mixins/AutojoinRoomsMixin"), exports);
__exportStar(require("./mixins/AutojoinUpgradedRoomsMixin"), exports);
// Models
__exportStar(require("./models/Presence"), exports);
__exportStar(require("./models/MatrixProfile"), exports);
__exportStar(require("./models/EventContext"), exports);
__exportStar(require("./models/PowerLevelBounds"), exports);
__exportStar(require("./models/OpenIDConnect"), exports);
__exportStar(require("./models/Policies"), exports);
__exportStar(require("./models/Threepid"), exports);
__exportStar(require("./models/Spaces"), exports);
__exportStar(require("./models/IdentityServerModels"), exports);
__exportStar(require("./models/Crypto"), exports);
__exportStar(require("./models/MSC2176"), exports);
__exportStar(require("./models/Account"), exports);
__exportStar(require("./models/PowerLevelAction"), exports);
__exportStar(require("./models/ServerVersions"), exports);
__exportStar(require("./models/MatrixError"), exports);
__exportStar(require("./models/CreateRoom"), exports);
__exportStar(require("./models/MXCUrl"), exports);
// Unstable models
__exportStar(require("./models/unstable/MediaInfo"), exports);
// Event models
__exportStar(require("./models/events/EventKind"), exports);
__exportStar(require("./models/events/converter"), exports);
__exportStar(require("./models/events/InvalidEventError"), exports);
__exportStar(require("./models/events/Event"), exports);
__exportStar(require("./models/events/RoomEvent"), exports);
__exportStar(require("./models/events/PresenceEvent"), exports);
__exportStar(require("./models/events/MembershipEvent"), exports);
__exportStar(require("./models/events/MessageEvent"), exports);
__exportStar(require("./models/events/AliasesEvent"), exports);
__exportStar(require("./models/events/CanonicalAliasEvent"), exports);
__exportStar(require("./models/events/CreateEvent"), exports);
__exportStar(require("./models/events/JoinRulesEvent"), exports);
__exportStar(require("./models/events/PowerLevelsEvent"), exports);
__exportStar(require("./models/events/RedactionEvent"), exports);
__exportStar(require("./models/events/PinnedEventsEvent"), exports);
__exportStar(require("./models/events/RoomAvatarEvent"), exports);
__exportStar(require("./models/events/RoomNameEvent"), exports);
__exportStar(require("./models/events/RoomTopicEvent"), exports);
__exportStar(require("./models/events/SpaceChildEvent"), exports);
__exportStar(require("./models/events/EncryptionEvent"), exports);
__exportStar(require("./models/events/EncryptedRoomEvent"), exports);
// Preprocessors
__exportStar(require("./preprocessors/IPreprocessor"), exports);
__exportStar(require("./preprocessors/RichRepliesPreprocessor"), exports);
// Storage stuff
__exportStar(require("./storage/IAppserviceStorageProvider"), exports);
__exportStar(require("./storage/IStorageProvider"), exports);
__exportStar(require("./storage/MemoryStorageProvider"), exports);
__exportStar(require("./storage/SimpleFsStorageProvider"), exports);
__exportStar(require("./storage/ICryptoStorageProvider"), exports);
__exportStar(require("./storage/RustSdkCryptoStorageProvider"), exports);
__exportStar(require("./storage/SimplePostgresStorageProvider"), exports);
// Strategies
__exportStar(require("./strategies/AppserviceJoinRoomStrategy"), exports);
__exportStar(require("./strategies/JoinRoomStrategy"), exports);
// Other clients
__exportStar(require("./identity/IdentityClient"), exports);
// Root-level stuff
__exportStar(require("./IFilter"), exports);
__exportStar(require("./MatrixClient"), exports);
__exportStar(require("./MatrixAuth"), exports);
__exportStar(require("./UnstableApis"), exports);
__exportStar(require("./AdminApis"), exports);
__exportStar(require("./request"), exports);
__exportStar(require("./PantalaimonClient"), exports);
__exportStar(require("./SynchronousMatrixClient"), exports);
__exportStar(require("./SynapseAdminApis"), exports);
__exportStar(require("./MatrixContentScannerClient"), exports);
__exportStar(require("./simple-validation"), exports);
__exportStar(require("./b64"), exports);
__exportStar(require("./http"), exports);
__exportStar(require("./DMs"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,cAAc;AACd,0DAAwC;AACxC,sDAAoC;AACpC,4DAA0C;AAC1C,8DAA4C;AAC5C,sEAAoD;AAEpD,aAAa;AACb,qDAAmC;AACnC,sDAAoC;AACpC,oDAAkC;AAClC,qCAAqC;AACrC,gEAA8C;AAE9C,UAAU;AACV,sDAAoC;AACpC,wDAAsC;AACtC,uDAAqC;AACrC,uDAAqC;AACrC,yDAAuC;AACvC,yDAAuC;AACvC,2DAAyC;AAEzC,UAAU;AACV,0DAAwC;AACxC,8DAA4C;AAC5C,oDAAkC;AAClC,uDAAqC;AAErC,UAAU;AACV,qDAAmC;AACnC,kDAAgC;AAChC,uDAAqC;AACrC,4DAA0C;AAC1C,oDAAkC;AAElC,SAAS;AACT,8DAA4C;AAC5C,sEAAoD;AAEpD,SAAS;AACT,oDAAkC;AAClC,yDAAuC;AACvC,wDAAsC;AACtC,4DAA0C;AAC1C,yDAAuC;AACvC,oDAAkC;AAClC,oDAAkC;AAClC,kDAAgC;AAChC,gEAA8C;AAC9C,kDAAgC;AAChC,mDAAiC;AACjC,mDAAiC;AACjC,4DAA0C;AAC1C,0DAAwC;AACxC,uDAAqC;AACrC,sDAAoC;AACpC,kDAAgC;AAEhC,kBAAkB;AAClB,8DAA4C;AAE5C,eAAe;AACf,4DAA0C;AAC1C,4DAA0C;AAC1C,oEAAkD;AAClD,wDAAsC;AACtC,4DAA0C;AAC1C,gEAA8C;AAC9C,kEAAgD;AAChD,+DAA6C;AAC7C,+DAA6C;AAC7C,sEAAoD;AACpD,8DAA4C;AAC5C,iEAA+C;AAC/C,mEAAiD;AACjD,iEAA+C;AAC/C,oEAAkD;AAClD,kEAAgD;AAChD,gEAA8C;AAC9C,iEAA+C;AAC/C,kEAAgD;AAChD,kEAAgD;AAChD,qEAAmD;AAEnD,gBAAgB;AAChB,gEAA8C;AAC9C,0EAAwD;AAExD,gBAAgB;AAChB,uEAAqD;AACrD,6DAA2C;AAC3C,kEAAgD;AAChD,oEAAkD;AAClD,mEAAiD;AACjD,yEAAuD;AACvD,0EAAwD;AAExD,aAAa;AACb,0EAAwD;AACxD,gEAA8C;AAE9C,gBAAgB;AAChB,4DAA0C;AAE1C,mBAAmB;AACnB,4CAA0B;AAC1B,iDAA+B;AAC/B,+CAA6B;AAC7B,iDAA+B;AAC/B,8CAA4B;AAC5B,4CAA0B;AAC1B,sDAAoC;AACpC,4DAA0C;AAC1C,qDAAmC;AACnC,+DAA6C;AAC7C,sDAAoC;AACpC,wCAAsB;AACtB,yCAAuB;AACvB,wCAAsB"}

View File

@@ -0,0 +1,12 @@
import { ILogger } from "./ILogger";
/**
* Logs to the console in a plain format. This is the default logger.
* @category Logging
*/
export declare class ConsoleLogger implements ILogger {
trace(module: string, ...messageOrObject: any[]): void;
debug(module: string, ...messageOrObject: any[]): void;
error(module: string, ...messageOrObject: any[]): void;
info(module: string, ...messageOrObject: any[]): void;
warn(module: string, ...messageOrObject: any[]): void;
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsoleLogger = void 0;
/* eslint-disable no-console */
/**
* Logs to the console in a plain format. This is the default logger.
* @category Logging
*/
class ConsoleLogger {
trace(module, ...messageOrObject) {
console.trace(module, ...messageOrObject);
}
debug(module, ...messageOrObject) {
console.debug(module, ...messageOrObject);
}
error(module, ...messageOrObject) {
console.error(module, ...messageOrObject);
}
info(module, ...messageOrObject) {
console.log(module, ...messageOrObject);
}
warn(module, ...messageOrObject) {
console.warn(module, ...messageOrObject);
}
}
exports.ConsoleLogger = ConsoleLogger;
/* eslint-enable no-console */
//# sourceMappingURL=ConsoleLogger.js.map

Some files were not shown because too many files have changed in this diff Show More