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

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

View File

@@ -0,0 +1 @@
{"version":3,"file":"ConsoleLogger.js","sourceRoot":"","sources":["../../src/logging/ConsoleLogger.ts"],"names":[],"mappings":";;;AAEA,+BAA+B;AAE/B;;;GAGG;AACH,MAAa,aAAa;IACf,KAAK,CAAC,MAAc,EAAE,GAAG,eAAsB;QAClD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,MAAc,EAAE,GAAG,eAAsB;QAClD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,MAAc,EAAE,GAAG,eAAsB;QAClD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;IAC9C,CAAC;IAEM,IAAI,CAAC,MAAc,EAAE,GAAG,eAAsB;QACjD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;IAC5C,CAAC;IAEM,IAAI,CAAC,MAAc,EAAE,GAAG,eAAsB;QACjD,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC;IAC7C,CAAC;CACJ;AApBD,sCAoBC;AAED,8BAA8B"}

View File

@@ -0,0 +1,36 @@
/**
* Represents a logger
* @category Logging
*/
export interface ILogger {
/**
* Logs to the INFO channel
* @param {string} module The module being logged
* @param {any[]} messageOrObject The data to log
*/
info(module: string, ...messageOrObject: any[]): any;
/**
* Logs to the WARN channel
* @param {string} module The module being logged
* @param {any[]} messageOrObject The data to log
*/
warn(module: string, ...messageOrObject: any[]): any;
/**
* Logs to the ERROR channel
* @param {string} module The module being logged
* @param {any[]} messageOrObject The data to log
*/
error(module: string, ...messageOrObject: any[]): any;
/**
* Logs to the DEBUG channel
* @param {string} module The module being logged
* @param {any[]} messageOrObject The data to log
*/
debug(module: string, ...messageOrObject: any[]): any;
/**
* Logs to the TRACE channel
* @param {string} module The module being logged
* @param {any[]} messageOrObject The data to log
*/
trace(module: string, ...messageOrObject: any[]): any;
}

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