@jeongsu816/matrix-bot-sdk 0.0.0-stage → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -2
- package/lib/AdminApis.d.ts +52 -0
- package/lib/AdminApis.js +29 -0
- package/lib/AdminApis.js.map +1 -0
- package/lib/DMs.d.ts +48 -0
- package/lib/DMs.js +160 -0
- package/lib/DMs.js.map +1 -0
- package/lib/IFilter.d.ts +7 -0
- package/lib/IFilter.js +3 -0
- package/lib/IFilter.js.map +1 -0
- package/lib/MatrixAuth.d.ts +45 -0
- package/lib/MatrixAuth.js +125 -0
- package/lib/MatrixAuth.js.map +1 -0
- package/lib/MatrixClient.d.ts +834 -0
- package/lib/MatrixClient.js +2268 -0
- package/lib/MatrixClient.js.map +1 -0
- package/lib/PantalaimonClient.d.ts +42 -0
- package/lib/PantalaimonClient.js +44 -0
- package/lib/PantalaimonClient.js.map +1 -0
- package/lib/SynapseAdminApis.d.ts +350 -0
- package/lib/SynapseAdminApis.js +244 -0
- package/lib/SynapseAdminApis.js.map +1 -0
- package/lib/SynchronousMatrixClient.d.ts +82 -0
- package/lib/SynchronousMatrixClient.js +130 -0
- package/lib/SynchronousMatrixClient.js.map +1 -0
- package/lib/UnstableApis.d.ts +43 -0
- package/lib/UnstableApis.js +74 -0
- package/lib/UnstableApis.js.map +1 -0
- package/lib/appservice/Appservice.d.ts +328 -0
- package/lib/appservice/Appservice.js +803 -0
- package/lib/appservice/Appservice.js.map +1 -0
- package/lib/appservice/Intent.d.ts +113 -0
- package/lib/appservice/Intent.js +363 -0
- package/lib/appservice/Intent.js.map +1 -0
- package/lib/appservice/MatrixBridge.d.ts +85 -0
- package/lib/appservice/MatrixBridge.js +111 -0
- package/lib/appservice/MatrixBridge.js.map +1 -0
- package/lib/appservice/UnstableAppserviceApis.d.ts +41 -0
- package/lib/appservice/UnstableAppserviceApis.js +62 -0
- package/lib/appservice/UnstableAppserviceApis.js.map +1 -0
- package/lib/appservice/http_responses.d.ts +77 -0
- package/lib/appservice/http_responses.js +3 -0
- package/lib/appservice/http_responses.js.map +1 -0
- package/lib/b64.d.ts +42 -0
- package/lib/b64.js +68 -0
- package/lib/b64.js.map +1 -0
- package/lib/e2ee/CrossSigningBootstrap.d.ts +73 -0
- package/lib/e2ee/CrossSigningBootstrap.js +86 -0
- package/lib/e2ee/CrossSigningBootstrap.js.map +1 -0
- package/lib/e2ee/CryptoClient.d.ts +163 -0
- package/lib/e2ee/CryptoClient.js +382 -0
- package/lib/e2ee/CryptoClient.js.map +1 -0
- package/lib/e2ee/ICryptoRoomInformation.d.ts +8 -0
- package/lib/e2ee/ICryptoRoomInformation.js +3 -0
- package/lib/e2ee/ICryptoRoomInformation.js.map +1 -0
- package/lib/e2ee/RoomTracker.d.ts +41 -0
- package/lib/e2ee/RoomTracker.js +96 -0
- package/lib/e2ee/RoomTracker.js.map +1 -0
- package/lib/e2ee/RustEngine.d.ts +149 -0
- package/lib/e2ee/RustEngine.js +467 -0
- package/lib/e2ee/RustEngine.js.map +1 -0
- package/lib/e2ee/decorators.d.ts +10 -0
- package/lib/e2ee/decorators.js +37 -0
- package/lib/e2ee/decorators.js.map +1 -0
- package/lib/helpers/MatrixEntity.d.ts +37 -0
- package/lib/helpers/MatrixEntity.js +67 -0
- package/lib/helpers/MatrixEntity.js.map +1 -0
- package/lib/helpers/MatrixGlob.d.ts +22 -0
- package/lib/helpers/MatrixGlob.js +35 -0
- package/lib/helpers/MatrixGlob.js.map +1 -0
- package/lib/helpers/MentionPill.d.ts +40 -0
- package/lib/helpers/MentionPill.js +91 -0
- package/lib/helpers/MentionPill.js.map +1 -0
- package/lib/helpers/Permalinks.d.ts +58 -0
- package/lib/helpers/Permalinks.js +73 -0
- package/lib/helpers/Permalinks.js.map +1 -0
- package/lib/helpers/ProfileCache.d.ts +48 -0
- package/lib/helpers/ProfileCache.js +114 -0
- package/lib/helpers/ProfileCache.js.map +1 -0
- package/lib/helpers/RichReply.d.ts +17 -0
- package/lib/helpers/RichReply.js +47 -0
- package/lib/helpers/RichReply.js.map +1 -0
- package/lib/helpers/UnpaddedBase64.d.ts +31 -0
- package/lib/helpers/UnpaddedBase64.js +45 -0
- package/lib/helpers/UnpaddedBase64.js.map +1 -0
- package/lib/http.d.ts +17 -0
- package/lib/http.js +147 -0
- package/lib/http.js.map +1 -0
- package/lib/identity/IdentityClient.d.ts +93 -0
- package/lib/identity/IdentityClient.js +248 -0
- package/lib/identity/IdentityClient.js.map +1 -0
- package/lib/index.d.ts +91 -0
- package/lib/index.js +125 -0
- package/lib/index.js.map +1 -0
- package/lib/logging/ConsoleLogger.d.ts +12 -0
- package/lib/logging/ConsoleLogger.js +28 -0
- package/lib/logging/ConsoleLogger.js.map +1 -0
- package/lib/logging/ILogger.d.ts +36 -0
- package/lib/logging/ILogger.js +3 -0
- package/lib/logging/ILogger.js.map +1 -0
- package/lib/logging/LogService.d.ts +100 -0
- package/lib/logging/LogService.js +169 -0
- package/lib/logging/LogService.js.map +1 -0
- package/lib/logging/RichConsoleLogger.d.ts +20 -0
- package/lib/logging/RichConsoleLogger.js +41 -0
- package/lib/logging/RichConsoleLogger.js.map +1 -0
- package/lib/metrics/IMetricListener.d.ts +52 -0
- package/lib/metrics/IMetricListener.js +3 -0
- package/lib/metrics/IMetricListener.js.map +1 -0
- package/lib/metrics/Metrics.d.ts +66 -0
- package/lib/metrics/Metrics.js +128 -0
- package/lib/metrics/Metrics.js.map +1 -0
- package/lib/metrics/contexts.d.ts +51 -0
- package/lib/metrics/contexts.js +3 -0
- package/lib/metrics/contexts.js.map +1 -0
- package/lib/metrics/decorators.d.ts +15 -0
- package/lib/metrics/decorators.js +92 -0
- package/lib/metrics/decorators.js.map +1 -0
- package/lib/metrics/names.d.ts +45 -0
- package/lib/metrics/names.js +49 -0
- package/lib/metrics/names.js.map +1 -0
- package/lib/mixins/AutojoinRoomsMixin.d.ts +10 -0
- package/lib/mixins/AutojoinRoomsMixin.js +25 -0
- package/lib/mixins/AutojoinRoomsMixin.js.map +1 -0
- package/lib/mixins/AutojoinUpgradedRoomsMixin.d.ts +10 -0
- package/lib/mixins/AutojoinUpgradedRoomsMixin.js +40 -0
- package/lib/mixins/AutojoinUpgradedRoomsMixin.js.map +1 -0
- package/lib/models/Account.d.ts +8 -0
- package/lib/models/Account.js +3 -0
- package/lib/models/Account.js.map +1 -0
- package/lib/models/CreateRoom.d.ts +125 -0
- package/lib/models/CreateRoom.js +3 -0
- package/lib/models/CreateRoom.js.map +1 -0
- package/lib/models/Crypto.d.ts +167 -0
- package/lib/models/Crypto.js +31 -0
- package/lib/models/Crypto.js.map +1 -0
- package/lib/models/EventContext.d.ts +19 -0
- package/lib/models/EventContext.js +3 -0
- package/lib/models/EventContext.js.map +1 -0
- package/lib/models/IdentityServerModels.d.ts +20 -0
- package/lib/models/IdentityServerModels.js +3 -0
- package/lib/models/IdentityServerModels.js.map +1 -0
- package/lib/models/MSC2176.d.ts +42 -0
- package/lib/models/MSC2176.js +3 -0
- package/lib/models/MSC2176.js.map +1 -0
- package/lib/models/MatrixError.d.ts +38 -0
- package/lib/models/MatrixError.js +30 -0
- package/lib/models/MatrixError.js.map +1 -0
- package/lib/models/MatrixProfile.d.ts +43 -0
- package/lib/models/MatrixProfile.js +43 -0
- package/lib/models/MatrixProfile.js.map +1 -0
- package/lib/models/OpenIDConnect.d.ts +10 -0
- package/lib/models/OpenIDConnect.js +3 -0
- package/lib/models/OpenIDConnect.js.map +1 -0
- package/lib/models/Policies.d.ts +25 -0
- package/lib/models/Policies.js +3 -0
- package/lib/models/Policies.js.map +1 -0
- package/lib/models/PowerLevelAction.d.ts +27 -0
- package/lib/models/PowerLevelAction.js +32 -0
- package/lib/models/PowerLevelAction.js.map +1 -0
- package/lib/models/PowerLevelBounds.d.ts +15 -0
- package/lib/models/PowerLevelBounds.js +3 -0
- package/lib/models/PowerLevelBounds.js.map +1 -0
- package/lib/models/Presence.d.ts +25 -0
- package/lib/models/Presence.js +38 -0
- package/lib/models/Presence.js.map +1 -0
- package/lib/models/ServerVersions.d.ts +8 -0
- package/lib/models/ServerVersions.js +3 -0
- package/lib/models/ServerVersions.js.map +1 -0
- package/lib/models/Spaces.d.ts +116 -0
- package/lib/models/Spaces.js +91 -0
- package/lib/models/Spaces.js.map +1 -0
- package/lib/models/Threepid.d.ts +8 -0
- package/lib/models/Threepid.js +3 -0
- package/lib/models/Threepid.js.map +1 -0
- package/lib/models/events/AliasesEvent.d.ts +27 -0
- package/lib/models/events/AliasesEvent.js +27 -0
- package/lib/models/events/AliasesEvent.js.map +1 -0
- package/lib/models/events/CanonicalAliasEvent.d.ts +23 -0
- package/lib/models/events/CanonicalAliasEvent.js +21 -0
- package/lib/models/events/CanonicalAliasEvent.js.map +1 -0
- package/lib/models/events/CreateEvent.d.ts +63 -0
- package/lib/models/events/CreateEvent.js +33 -0
- package/lib/models/events/CreateEvent.js.map +1 -0
- package/lib/models/events/EncryptedRoomEvent.d.ts +42 -0
- package/lib/models/events/EncryptedRoomEvent.js +28 -0
- package/lib/models/events/EncryptedRoomEvent.js.map +1 -0
- package/lib/models/events/EncryptionEvent.d.ts +47 -0
- package/lib/models/events/EncryptionEvent.js +42 -0
- package/lib/models/events/EncryptionEvent.js.map +1 -0
- package/lib/models/events/Event.d.ts +26 -0
- package/lib/models/events/Event.js +40 -0
- package/lib/models/events/Event.js.map +1 -0
- package/lib/models/events/EventKind.d.ts +15 -0
- package/lib/models/events/EventKind.js +20 -0
- package/lib/models/events/EventKind.js.map +1 -0
- package/lib/models/events/InvalidEventError.d.ts +14 -0
- package/lib/models/events/InvalidEventError.js +24 -0
- package/lib/models/events/InvalidEventError.js.map +1 -0
- package/lib/models/events/JoinRulesEvent.d.ts +29 -0
- package/lib/models/events/JoinRulesEvent.js +21 -0
- package/lib/models/events/JoinRulesEvent.js.map +1 -0
- package/lib/models/events/MembershipEvent.d.ts +54 -0
- package/lib/models/events/MembershipEvent.js +49 -0
- package/lib/models/events/MembershipEvent.js.map +1 -0
- package/lib/models/events/MessageEvent.d.ts +233 -0
- package/lib/models/events/MessageEvent.js +49 -0
- package/lib/models/events/MessageEvent.js.map +1 -0
- package/lib/models/events/PinnedEventsEvent.d.ts +23 -0
- package/lib/models/events/PinnedEventsEvent.js +21 -0
- package/lib/models/events/PinnedEventsEvent.js.map +1 -0
- package/lib/models/events/PowerLevelsEvent.d.ts +96 -0
- package/lib/models/events/PowerLevelsEvent.js +70 -0
- package/lib/models/events/PowerLevelsEvent.js.map +1 -0
- package/lib/models/events/PresenceEvent.d.ts +56 -0
- package/lib/models/events/PresenceEvent.js +21 -0
- package/lib/models/events/PresenceEvent.js.map +1 -0
- package/lib/models/events/RedactionEvent.d.ts +28 -0
- package/lib/models/events/RedactionEvent.js +36 -0
- package/lib/models/events/RedactionEvent.js.map +1 -0
- package/lib/models/events/RoomAvatarEvent.d.ts +28 -0
- package/lib/models/events/RoomAvatarEvent.js +21 -0
- package/lib/models/events/RoomAvatarEvent.js.map +1 -0
- package/lib/models/events/RoomEvent.d.ts +58 -0
- package/lib/models/events/RoomEvent.js +57 -0
- package/lib/models/events/RoomEvent.js.map +1 -0
- package/lib/models/events/RoomNameEvent.d.ts +20 -0
- package/lib/models/events/RoomNameEvent.js +21 -0
- package/lib/models/events/RoomNameEvent.js.map +1 -0
- package/lib/models/events/RoomTopicEvent.d.ts +20 -0
- package/lib/models/events/RoomTopicEvent.js +21 -0
- package/lib/models/events/RoomTopicEvent.js.map +1 -0
- package/lib/models/events/SpaceChildEvent.d.ts +53 -0
- package/lib/models/events/SpaceChildEvent.js +49 -0
- package/lib/models/events/SpaceChildEvent.js.map +1 -0
- package/lib/models/events/converter.d.ts +8 -0
- package/lib/models/events/converter.js +50 -0
- package/lib/models/events/converter.js.map +1 -0
- package/lib/models/unstable/MediaInfo.d.ts +16 -0
- package/lib/models/unstable/MediaInfo.js +3 -0
- package/lib/models/unstable/MediaInfo.js.map +1 -0
- package/lib/preprocessors/IPreprocessor.d.ts +22 -0
- package/lib/preprocessors/IPreprocessor.js +3 -0
- package/lib/preprocessors/IPreprocessor.js.map +1 -0
- package/lib/preprocessors/RichRepliesPreprocessor.d.ts +67 -0
- package/lib/preprocessors/RichRepliesPreprocessor.js +112 -0
- package/lib/preprocessors/RichRepliesPreprocessor.js.map +1 -0
- package/lib/request.d.ts +13 -0
- package/lib/request.js +23 -0
- package/lib/request.js.map +1 -0
- package/lib/simple-validation.d.ts +10 -0
- package/lib/simple-validation.js +30 -0
- package/lib/simple-validation.js.map +1 -0
- package/lib/storage/IAppserviceStorageProvider.d.ts +48 -0
- package/lib/storage/IAppserviceStorageProvider.js +3 -0
- package/lib/storage/IAppserviceStorageProvider.js.map +1 -0
- package/lib/storage/ICryptoStorageProvider.d.ts +32 -0
- package/lib/storage/ICryptoStorageProvider.js +3 -0
- package/lib/storage/ICryptoStorageProvider.js.map +1 -0
- package/lib/storage/IStorageProvider.d.ts +47 -0
- package/lib/storage/IStorageProvider.js +3 -0
- package/lib/storage/IStorageProvider.js.map +1 -0
- package/lib/storage/MemoryStorageProvider.d.ts +26 -0
- package/lib/storage/MemoryStorageProvider.js +55 -0
- package/lib/storage/MemoryStorageProvider.js.map +1 -0
- package/lib/storage/RustSdkCryptoStorageProvider.d.ts +38 -0
- package/lib/storage/RustSdkCryptoStorageProvider.js +69 -0
- package/lib/storage/RustSdkCryptoStorageProvider.js.map +1 -0
- package/lib/storage/SimpleFsStorageProvider.d.ts +31 -0
- package/lib/storage/SimpleFsStorageProvider.js +122 -0
- package/lib/storage/SimpleFsStorageProvider.js.map +1 -0
- package/lib/storage/SimplePostgresStorageProvider.d.ts +32 -0
- package/lib/storage/SimplePostgresStorageProvider.js +148 -0
- package/lib/storage/SimplePostgresStorageProvider.js.map +1 -0
- package/lib/strategies/AppserviceJoinRoomStrategy.d.ts +14 -0
- package/lib/strategies/AppserviceJoinRoomStrategy.js +54 -0
- package/lib/strategies/AppserviceJoinRoomStrategy.js.map +1 -0
- package/lib/strategies/JoinRoomStrategy.d.ts +11 -0
- package/lib/strategies/JoinRoomStrategy.js +44 -0
- package/lib/strategies/JoinRoomStrategy.js.map +1 -0
- package/package.json +136 -4
- package/src/AdminApis.ts +60 -0
- package/src/DMs.ts +164 -0
- package/src/IFilter.ts +7 -0
- package/src/MatrixAuth.ts +129 -0
- package/src/MatrixClient.ts +2046 -0
- package/src/PantalaimonClient.ts +62 -0
- package/src/SynapseAdminApis.ts +482 -0
- package/src/SynchronousMatrixClient.ts +129 -0
- package/src/UnstableApis.ts +75 -0
- package/src/appservice/Appservice.ts +1089 -0
- package/src/appservice/Intent.ts +342 -0
- package/src/appservice/MatrixBridge.ts +139 -0
- package/src/appservice/UnstableAppserviceApis.ts +63 -0
- package/src/appservice/http_responses.ts +77 -0
- package/src/b64.ts +63 -0
- package/src/e2ee/CrossSigningBootstrap.ts +141 -0
- package/src/e2ee/CryptoClient.ts +378 -0
- package/src/e2ee/ICryptoRoomInformation.ts +9 -0
- package/src/e2ee/RoomTracker.ts +99 -0
- package/src/e2ee/RustEngine.ts +580 -0
- package/src/e2ee/decorators.ts +38 -0
- package/src/helpers/MatrixEntity.ts +65 -0
- package/src/helpers/MatrixGlob.ts +38 -0
- package/src/helpers/MentionPill.ts +95 -0
- package/src/helpers/Permalinks.ts +101 -0
- package/src/helpers/ProfileCache.ts +119 -0
- package/src/helpers/RichReply.ts +46 -0
- package/src/helpers/UnpaddedBase64.ts +44 -0
- package/src/http.ts +166 -0
- package/src/identity/IdentityClient.ts +225 -0
- package/src/index.ts +125 -0
- package/src/logging/ConsoleLogger.ts +31 -0
- package/src/logging/ILogger.ts +40 -0
- package/src/logging/LogService.ts +167 -0
- package/src/logging/RichConsoleLogger.ts +70 -0
- package/src/metrics/IMetricListener.ts +57 -0
- package/src/metrics/Metrics.ts +134 -0
- package/src/metrics/contexts.ts +56 -0
- package/src/metrics/decorators.ts +94 -0
- package/src/metrics/names.ts +53 -0
- package/src/mixins/AutojoinRoomsMixin.ts +24 -0
- package/src/mixins/AutojoinUpgradedRoomsMixin.ts +37 -0
- package/src/models/Account.ts +8 -0
- package/src/models/CreateRoom.ts +144 -0
- package/src/models/Crypto.ts +188 -0
- package/src/models/EventContext.ts +23 -0
- package/src/models/IdentityServerModels.ts +18 -0
- package/src/models/MSC2176.ts +47 -0
- package/src/models/MatrixError.ts +39 -0
- package/src/models/MatrixProfile.ts +55 -0
- package/src/models/OpenIDConnect.ts +10 -0
- package/src/models/Policies.ts +28 -0
- package/src/models/PowerLevelAction.ts +31 -0
- package/src/models/PowerLevelBounds.ts +16 -0
- package/src/models/Presence.ts +38 -0
- package/src/models/ServerVersions.ts +8 -0
- package/src/models/Spaces.ts +167 -0
- package/src/models/Threepid.ts +8 -0
- package/src/models/events/AliasesEvent.ts +37 -0
- package/src/models/events/CanonicalAliasEvent.ts +30 -0
- package/src/models/events/CreateEvent.ts +82 -0
- package/src/models/events/EncryptedRoomEvent.ts +58 -0
- package/src/models/events/EncryptionEvent.ts +63 -0
- package/src/models/events/Event.ts +38 -0
- package/src/models/events/EventKind.ts +16 -0
- package/src/models/events/InvalidEventError.ts +19 -0
- package/src/models/events/JoinRulesEvent.ts +37 -0
- package/src/models/events/MembershipEvent.ts +77 -0
- package/src/models/events/MessageEvent.ts +292 -0
- package/src/models/events/PinnedEventsEvent.ts +30 -0
- package/src/models/events/PowerLevelsEvent.ts +135 -0
- package/src/models/events/PresenceEvent.ts +69 -0
- package/src/models/events/RedactionEvent.ts +44 -0
- package/src/models/events/RoomAvatarEvent.ts +36 -0
- package/src/models/events/RoomEvent.ts +83 -0
- package/src/models/events/RoomNameEvent.ts +27 -0
- package/src/models/events/RoomTopicEvent.ts +27 -0
- package/src/models/events/SpaceChildEvent.ts +74 -0
- package/src/models/events/_MissingEvents.md +35 -0
- package/src/models/events/converter.ts +47 -0
- package/src/models/unstable/MediaInfo.ts +16 -0
- package/src/preprocessors/IPreprocessor.ts +24 -0
- package/src/preprocessors/RichRepliesPreprocessor.ts +159 -0
- package/src/request.ts +21 -0
- package/src/simple-validation.ts +30 -0
- package/src/storage/IAppserviceStorageProvider.ts +54 -0
- package/src/storage/ICryptoStorageProvider.ts +36 -0
- package/src/storage/IStorageProvider.ts +53 -0
- package/src/storage/MemoryStorageProvider.ts +65 -0
- package/src/storage/RustSdkCryptoStorageProvider.ts +81 -0
- package/src/storage/SimpleFsStorageProvider.ts +142 -0
- package/src/storage/SimplePostgresStorageProvider.ts +168 -0
- package/src/strategies/AppserviceJoinRoomStrategy.ts +47 -0
- package/src/strategies/JoinRoomStrategy.ts +45 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Appservice = void 0;
|
|
4
|
+
const express = require("express");
|
|
5
|
+
const events_1 = require("events");
|
|
6
|
+
const morgan = require("morgan");
|
|
7
|
+
const LRU = require("lru-cache");
|
|
8
|
+
const querystring_1 = require("querystring");
|
|
9
|
+
const Intent_1 = require("./Intent");
|
|
10
|
+
const __1 = require("..");
|
|
11
|
+
const MatrixBridge_1 = require("./MatrixBridge");
|
|
12
|
+
const EDU_ANNOTATION_KEY = "io.t2bot.sdk.bot.type";
|
|
13
|
+
var EduAnnotation;
|
|
14
|
+
(function (EduAnnotation) {
|
|
15
|
+
EduAnnotation["ToDevice"] = "to_device";
|
|
16
|
+
EduAnnotation["Ephemeral"] = "ephemeral";
|
|
17
|
+
})(EduAnnotation || (EduAnnotation = {}));
|
|
18
|
+
/**
|
|
19
|
+
* Represents an application service. This provides helper utilities such as tracking
|
|
20
|
+
* of user intents (clients that are aware of their membership in rooms).
|
|
21
|
+
* @category Application services
|
|
22
|
+
*/
|
|
23
|
+
class Appservice extends events_1.EventEmitter {
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new application service.
|
|
26
|
+
* @param {IAppserviceOptions} options The options for the application service.
|
|
27
|
+
*/
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super();
|
|
30
|
+
this.options = options;
|
|
31
|
+
/**
|
|
32
|
+
* The metrics instance for this appservice. This will raise all metrics
|
|
33
|
+
* from this appservice instance as well as any intents/MatrixClients created
|
|
34
|
+
* by the appservice.
|
|
35
|
+
*/
|
|
36
|
+
this.metrics = new __1.Metrics();
|
|
37
|
+
this.bridgeInstance = new MatrixBridge_1.MatrixBridge(this);
|
|
38
|
+
this.app = express();
|
|
39
|
+
this.eventProcessors = {};
|
|
40
|
+
this.pendingTransactions = {};
|
|
41
|
+
options.joinStrategy = new __1.AppserviceJoinRoomStrategy(options.joinStrategy, this);
|
|
42
|
+
if (!options.intentOptions)
|
|
43
|
+
options.intentOptions = {};
|
|
44
|
+
if (!options.intentOptions.maxAgeMs)
|
|
45
|
+
options.intentOptions.maxAgeMs = 60 * 60 * 1000;
|
|
46
|
+
if (!options.intentOptions.maxCached)
|
|
47
|
+
options.intentOptions.maxCached = 10000;
|
|
48
|
+
this.intentsCache = new LRU.LRUCache({
|
|
49
|
+
max: options.intentOptions.maxCached,
|
|
50
|
+
ttl: options.intentOptions.maxAgeMs,
|
|
51
|
+
});
|
|
52
|
+
this.registration = options.registration;
|
|
53
|
+
// If protocol is not defined, define an empty array.
|
|
54
|
+
if (!this.registration.protocols) {
|
|
55
|
+
this.registration.protocols = [];
|
|
56
|
+
}
|
|
57
|
+
this.storage = options.storage || new __1.MemoryStorageProvider();
|
|
58
|
+
options.storage = this.storage;
|
|
59
|
+
this.cryptoStorage = options.cryptoStorage;
|
|
60
|
+
this.app.use(express.json({ limit: Number.MAX_SAFE_INTEGER })); // disable limits, use a reverse proxy
|
|
61
|
+
morgan.token('url-safe', (req) => `${req.path}?${(0, querystring_1.stringify)((0, __1.redactObjectForLogging)(req.query ?? {}))}`);
|
|
62
|
+
this.app.use(morgan(
|
|
63
|
+
// Same as "combined", but with sensitive values removed from requests.
|
|
64
|
+
':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') } }));
|
|
65
|
+
// ETag headers break the tests sometimes, and we don't actually need them anyways for
|
|
66
|
+
// appservices - none of this should be cached.
|
|
67
|
+
this.app.set('etag', false);
|
|
68
|
+
this.app.get("/users/:userId", this.onUser.bind(this));
|
|
69
|
+
this.app.get("/rooms/:roomAlias", this.onRoomAlias.bind(this));
|
|
70
|
+
this.app.put("/transactions/:txnId", this.onTransaction.bind(this));
|
|
71
|
+
this.app.get("/_matrix/app/v1/users/:userId", this.onUser.bind(this));
|
|
72
|
+
this.app.get("/_matrix/app/v1/rooms/:roomAlias", this.onRoomAlias.bind(this));
|
|
73
|
+
this.app.put("/_matrix/app/v1/transactions/:txnId", this.onTransaction.bind(this));
|
|
74
|
+
this.app.get("/_matrix/app/v1/thirdparty/protocol/:protocol", this.onThirdpartyProtocol.bind(this));
|
|
75
|
+
this.app.get("/_matrix/app/v1/thirdparty/user/:protocol", this.onThirdpartyUser.bind(this));
|
|
76
|
+
this.app.get("/_matrix/app/v1/thirdparty/user", this.onThirdpartyUser.bind(this));
|
|
77
|
+
this.app.get("/_matrix/app/v1/thirdparty/location/:protocol", this.onThirdpartyLocation.bind(this));
|
|
78
|
+
this.app.get("/_matrix/app/v1/thirdparty/location", this.onThirdpartyLocation.bind(this));
|
|
79
|
+
this.app.post("/_matrix/app/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
|
|
80
|
+
this.app.post("/_matrix/app/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
|
|
81
|
+
// Workaround for https://github.com/matrix-org/synapse/issues/3780
|
|
82
|
+
this.app.post("/_matrix/app/v1/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
|
|
83
|
+
this.app.post("/unstable/org.matrix.msc3983/keys/claim", this.onKeysClaim.bind(this));
|
|
84
|
+
this.app.post("/_matrix/app/v1/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
|
|
85
|
+
this.app.post("/unstable/org.matrix.msc3984/keys/query", this.onKeysQuery.bind(this));
|
|
86
|
+
// We register the 404 handler in the `begin()` function to allow consumers to add their own endpoints.
|
|
87
|
+
if (!this.registration.namespaces || !this.registration.namespaces.users || this.registration.namespaces.users.length === 0) {
|
|
88
|
+
throw new Error("No user namespaces in registration");
|
|
89
|
+
}
|
|
90
|
+
if (this.registration.namespaces.users.length !== 1) {
|
|
91
|
+
throw new Error("Too many user namespaces registered: expecting exactly one");
|
|
92
|
+
}
|
|
93
|
+
const userPrefix = (this.registration.namespaces.users[0].regex || "").split(":")[0];
|
|
94
|
+
if (!userPrefix.endsWith(".*") && !userPrefix.endsWith(".+")) {
|
|
95
|
+
this.userPrefix = null;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.userPrefix = userPrefix.substring(0, userPrefix.length - 2); // trim off the .* part
|
|
99
|
+
}
|
|
100
|
+
if (!this.registration.namespaces?.aliases || this.registration.namespaces.aliases.length !== 1) {
|
|
101
|
+
this.aliasPrefix = null;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.aliasPrefix = (this.registration.namespaces.aliases[0].regex || "").split(":")[0];
|
|
105
|
+
if (!this.aliasPrefix.endsWith(".*") && !this.aliasPrefix.endsWith(".+")) {
|
|
106
|
+
this.aliasPrefix = null;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.aliasPrefix = this.aliasPrefix.substring(0, this.aliasPrefix.length - 2); // trim off the .* part
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Gets the express app instance which is serving requests. Not recommended for
|
|
115
|
+
* general usage, but may be used to append routes to the web server.
|
|
116
|
+
*/
|
|
117
|
+
get expressAppInstance() {
|
|
118
|
+
return this.app;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets the bridge-specific APIs for this application service.
|
|
122
|
+
*/
|
|
123
|
+
get bridge() {
|
|
124
|
+
return this.bridgeInstance;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the application service's "bot" user ID (the sender_localpart).
|
|
128
|
+
*/
|
|
129
|
+
get botUserId() {
|
|
130
|
+
return this.getUserId(this.registration.sender_localpart);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the application service's "bot" Intent (the sender_localpart).
|
|
134
|
+
* @returns {Intent} The intent for the application service itself.
|
|
135
|
+
*/
|
|
136
|
+
get botIntent() {
|
|
137
|
+
return this.getIntentForUserId(this.botUserId);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the application service's "bot" MatrixClient (the sender_localpart).
|
|
141
|
+
* Normally the botIntent should be used to ensure that the bot user is safely
|
|
142
|
+
* handled.
|
|
143
|
+
* @returns {MatrixClient} The client for the application service itself.
|
|
144
|
+
*/
|
|
145
|
+
get botClient() {
|
|
146
|
+
return this.botIntent.underlyingClient;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Starts the application service, opening the bind address to begin processing requests.
|
|
150
|
+
* @returns {Promise<void>} resolves when started
|
|
151
|
+
*/
|
|
152
|
+
begin() {
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
// Per constructor, all other endpoints should 404.
|
|
155
|
+
// Technically, according to https://spec.matrix.org/v1.6/application-service-api/#unknown-routes we should
|
|
156
|
+
// be returning 405 for *known* endpoints with the wrong method.
|
|
157
|
+
this.app.all("*", (req, res) => {
|
|
158
|
+
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
|
|
159
|
+
});
|
|
160
|
+
this.appServer = this.app.listen(this.options.port, this.options.bindAddress, () => resolve());
|
|
161
|
+
}).then(async () => {
|
|
162
|
+
if (this.options.intentOptions?.encryption) {
|
|
163
|
+
await this.botIntent.enableEncryption();
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
await this.botIntent.ensureRegistered();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Stops the application service, freeing the web server.
|
|
172
|
+
*/
|
|
173
|
+
stop() {
|
|
174
|
+
if (!this.appServer)
|
|
175
|
+
return;
|
|
176
|
+
this.appServer.close();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Gets an intent for a given localpart. The user ID will be formed with the domain name given
|
|
180
|
+
* in the constructor.
|
|
181
|
+
* @param localpart The localpart to get an Intent for.
|
|
182
|
+
* @returns {Intent} An Intent for the user.
|
|
183
|
+
*/
|
|
184
|
+
getIntent(localpart) {
|
|
185
|
+
return this.getIntentForUserId(this.getUserId(localpart));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Gets a full user ID for a given localpart. The user ID will be formed with the domain name given
|
|
189
|
+
* in the constructor.
|
|
190
|
+
* @param localpart The localpart to get a user ID for.
|
|
191
|
+
* @returns {string} The user's ID.
|
|
192
|
+
*/
|
|
193
|
+
getUserId(localpart) {
|
|
194
|
+
return `@${localpart}:${this.options.homeserverName}`;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Gets an Intent for a given user suffix. The prefix is automatically detected from the registration
|
|
198
|
+
* options.
|
|
199
|
+
* @param suffix The user's suffix
|
|
200
|
+
* @returns {Intent} An Intent for the user.
|
|
201
|
+
*/
|
|
202
|
+
getIntentForSuffix(suffix) {
|
|
203
|
+
return this.getIntentForUserId(this.getUserIdForSuffix(suffix));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Gets a full user ID for a given suffix. The prefix is automatically detected from the registration
|
|
207
|
+
* options.
|
|
208
|
+
* @param suffix The user's suffix
|
|
209
|
+
* @returns {string} The user's ID.
|
|
210
|
+
*/
|
|
211
|
+
getUserIdForSuffix(suffix) {
|
|
212
|
+
if (!this.userPrefix) {
|
|
213
|
+
throw new Error(`Cannot use getUserIdForSuffix, provided namespace did not include a valid suffix`);
|
|
214
|
+
}
|
|
215
|
+
return `${this.userPrefix}${suffix}:${this.options.homeserverName}`;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Gets an Intent for a given user ID.
|
|
219
|
+
* @param {string} userId The user ID to get an Intent for.
|
|
220
|
+
* @returns {Intent} An Intent for the user.
|
|
221
|
+
*/
|
|
222
|
+
getIntentForUserId(userId) {
|
|
223
|
+
let intent = this.intentsCache.get(userId);
|
|
224
|
+
if (!intent) {
|
|
225
|
+
intent = new Intent_1.Intent(this.options, userId, this);
|
|
226
|
+
this.intentsCache.set(userId, intent);
|
|
227
|
+
if (this.options.intentOptions.encryption) {
|
|
228
|
+
intent.enableEncryption().catch(e => {
|
|
229
|
+
__1.LogService.error("Appservice", `Failed to set up crypto on intent ${userId}`, e);
|
|
230
|
+
throw e; // re-throw to cause unhandled exception
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return intent;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Gets the suffix for the provided user ID. If the user ID is not a namespaced
|
|
238
|
+
* user, this will return a falsey value.
|
|
239
|
+
* @param {string} userId The user ID to parse
|
|
240
|
+
* @returns {string} The suffix from the user ID.
|
|
241
|
+
*/
|
|
242
|
+
getSuffixForUserId(userId) {
|
|
243
|
+
if (!this.userPrefix) {
|
|
244
|
+
throw new Error(`Cannot use getUserIdForSuffix, provided namespace did not include a valid suffix`);
|
|
245
|
+
}
|
|
246
|
+
if (!userId || !userId.startsWith(this.userPrefix) || !userId.endsWith(`:${this.options.homeserverName}`)) {
|
|
247
|
+
// Invalid ID
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return userId
|
|
251
|
+
.split('')
|
|
252
|
+
.slice(this.userPrefix.length)
|
|
253
|
+
.reverse()
|
|
254
|
+
.slice(this.options.homeserverName.length + 1)
|
|
255
|
+
.reverse()
|
|
256
|
+
.join('');
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Determines if a given user ID is namespaced by this application service.
|
|
260
|
+
* @param {string} userId The user ID to check
|
|
261
|
+
* @returns {boolean} true if the user is namespaced, false otherwise
|
|
262
|
+
*/
|
|
263
|
+
isNamespacedUser(userId) {
|
|
264
|
+
return userId === this.botUserId ||
|
|
265
|
+
!!this.registration.namespaces?.users.find(({ regex }) => new RegExp(regex).test(userId));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Gets a full alias for a given localpart. The alias will be formed with the domain name given
|
|
269
|
+
* in the constructor.
|
|
270
|
+
* @param localpart The localpart to get an alias for.
|
|
271
|
+
* @returns {string} The alias.
|
|
272
|
+
*/
|
|
273
|
+
getAlias(localpart) {
|
|
274
|
+
return `#${localpart}:${this.options.homeserverName}`;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Gets a full alias for a given suffix. The prefix is automatically detected from the registration
|
|
278
|
+
* options.
|
|
279
|
+
* @param suffix The alias's suffix
|
|
280
|
+
* @returns {string} The alias.
|
|
281
|
+
*/
|
|
282
|
+
getAliasForSuffix(suffix) {
|
|
283
|
+
if (!this.aliasPrefix) {
|
|
284
|
+
throw new Error("Invalid configured alias prefix");
|
|
285
|
+
}
|
|
286
|
+
return `${this.aliasPrefix}${suffix}:${this.options.homeserverName}`;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Gets the localpart of an alias for a given suffix. The prefix is automatically detected from the registration
|
|
290
|
+
* options. Useful for the createRoom endpoint.
|
|
291
|
+
* @param suffix The alias's suffix
|
|
292
|
+
* @returns {string} The alias localpart.
|
|
293
|
+
*/
|
|
294
|
+
getAliasLocalpartForSuffix(suffix) {
|
|
295
|
+
if (!this.aliasPrefix) {
|
|
296
|
+
throw new Error("Invalid configured alias prefix");
|
|
297
|
+
}
|
|
298
|
+
return `${this.aliasPrefix.substr(1)}${suffix}`;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Gets the suffix for the provided alias. If the alias is not a namespaced
|
|
302
|
+
* alias, this will return a falsey value.
|
|
303
|
+
* @param {string} alias The alias to parse
|
|
304
|
+
* @returns {string} The suffix from the alias.
|
|
305
|
+
*/
|
|
306
|
+
getSuffixForAlias(alias) {
|
|
307
|
+
if (!this.aliasPrefix) {
|
|
308
|
+
throw new Error("Invalid configured alias prefix");
|
|
309
|
+
}
|
|
310
|
+
if (!alias || !this.isNamespacedAlias(alias)) {
|
|
311
|
+
// Invalid ID
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
return alias
|
|
315
|
+
.split('')
|
|
316
|
+
.slice(this.aliasPrefix.length)
|
|
317
|
+
.reverse()
|
|
318
|
+
.slice(this.options.homeserverName.length + 1)
|
|
319
|
+
.reverse()
|
|
320
|
+
.join('');
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Determines if a given alias is namespaced by this application service.
|
|
324
|
+
* @param {string} alias The alias to check
|
|
325
|
+
* @returns {boolean} true if the alias is namespaced, false otherwise
|
|
326
|
+
*/
|
|
327
|
+
isNamespacedAlias(alias) {
|
|
328
|
+
if (!this.aliasPrefix) {
|
|
329
|
+
throw new Error("Invalid configured alias prefix");
|
|
330
|
+
}
|
|
331
|
+
return alias.startsWith(this.aliasPrefix) && alias.endsWith(":" + this.options.homeserverName);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Adds a preprocessor to the event pipeline. When this appservice encounters an event, it
|
|
335
|
+
* will try to run it through the preprocessors it can in the order they were added.
|
|
336
|
+
* @param {IPreprocessor} preprocessor the preprocessor to add
|
|
337
|
+
*/
|
|
338
|
+
addPreprocessor(preprocessor) {
|
|
339
|
+
if (!preprocessor)
|
|
340
|
+
throw new Error("Preprocessor cannot be null");
|
|
341
|
+
const eventTypes = preprocessor.getSupportedEventTypes();
|
|
342
|
+
if (!eventTypes)
|
|
343
|
+
return; // Nothing to do
|
|
344
|
+
for (const eventType of eventTypes) {
|
|
345
|
+
if (!this.eventProcessors[eventType])
|
|
346
|
+
this.eventProcessors[eventType] = [];
|
|
347
|
+
this.eventProcessors[eventType].push(preprocessor);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Sets the visibility of a room in the appservice's room directory.
|
|
352
|
+
* @param {string} networkId The network ID to group the room under.
|
|
353
|
+
* @param {string} roomId The room ID to manipulate the visibility of.
|
|
354
|
+
* @param {"public" | "private"} visibility The visibility to set for the room.
|
|
355
|
+
* @return {Promise<any>} resolves when the visibility has been updated.
|
|
356
|
+
*/
|
|
357
|
+
setRoomDirectoryVisibility(networkId, roomId, visibility) {
|
|
358
|
+
roomId = encodeURIComponent(roomId);
|
|
359
|
+
networkId = encodeURIComponent(networkId);
|
|
360
|
+
return this.botClient.doRequest("PUT", `/_matrix/client/v3/directory/list/appservice/${networkId}/${roomId}`, null, {
|
|
361
|
+
visibility,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
async processEphemeralEvent(event) {
|
|
365
|
+
if (!event)
|
|
366
|
+
return event;
|
|
367
|
+
if (!this.eventProcessors[event["type"]])
|
|
368
|
+
return event;
|
|
369
|
+
for (const processor of this.eventProcessors[event["type"]]) {
|
|
370
|
+
await processor.processEvent(event, this.botIntent.underlyingClient, __1.EventKind.EphemeralEvent);
|
|
371
|
+
}
|
|
372
|
+
return event;
|
|
373
|
+
}
|
|
374
|
+
async processEvent(event) {
|
|
375
|
+
if (!event)
|
|
376
|
+
return event;
|
|
377
|
+
if (!this.eventProcessors[event["type"]])
|
|
378
|
+
return event;
|
|
379
|
+
for (const processor of this.eventProcessors[event["type"]]) {
|
|
380
|
+
await processor.processEvent(event, this.botIntent.underlyingClient, __1.EventKind.RoomEvent);
|
|
381
|
+
}
|
|
382
|
+
return event;
|
|
383
|
+
}
|
|
384
|
+
async processMembershipEvent(event) {
|
|
385
|
+
if (!event["content"])
|
|
386
|
+
return;
|
|
387
|
+
const domain = new __1.UserID(event['state_key']).domain;
|
|
388
|
+
const botDomain = new __1.UserID(this.botUserId).domain;
|
|
389
|
+
if (domain !== botDomain)
|
|
390
|
+
return; // can't be impersonated, so don't try
|
|
391
|
+
// Update the target intent's joined rooms (fixes transition errors with the cache, like join->kick->join)
|
|
392
|
+
const intent = this.getIntentForUserId(event['state_key']);
|
|
393
|
+
await intent.refreshJoinedRooms();
|
|
394
|
+
const targetMembership = event["content"]["membership"];
|
|
395
|
+
if (targetMembership === "join") {
|
|
396
|
+
this.emit("room.join", event["room_id"], event);
|
|
397
|
+
await intent.underlyingClient.crypto?.onRoomJoin(event["room_id"]);
|
|
398
|
+
}
|
|
399
|
+
else if (targetMembership === "ban" || targetMembership === "leave") {
|
|
400
|
+
this.emit("room.leave", event["room_id"], event);
|
|
401
|
+
}
|
|
402
|
+
else if (targetMembership === "invite") {
|
|
403
|
+
this.emit("room.invite", event["room_id"], event);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
isAuthed(req) {
|
|
407
|
+
let providedToken = req.query ? req.query["access_token"] : null;
|
|
408
|
+
if (req.headers && req.headers["authorization"]) {
|
|
409
|
+
const authHeader = req.headers["authorization"];
|
|
410
|
+
if (!authHeader.startsWith("Bearer "))
|
|
411
|
+
providedToken = null;
|
|
412
|
+
else
|
|
413
|
+
providedToken = authHeader.substring("Bearer ".length);
|
|
414
|
+
}
|
|
415
|
+
return providedToken === this.registration.hs_token;
|
|
416
|
+
}
|
|
417
|
+
async onTransaction(req, res) {
|
|
418
|
+
if (!this.isAuthed(req)) {
|
|
419
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (typeof (req.body) !== "object") {
|
|
423
|
+
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (!req.body["events"] || !Array.isArray(req.body["events"])) {
|
|
427
|
+
res.status(400).json({ errcode: "BAD_REQUEST", error: "Invalid JSON: expected events" });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const txnId = req.params["txnId"];
|
|
431
|
+
if (await Promise.resolve(this.storage.isTransactionCompleted(txnId))) {
|
|
432
|
+
res.status(200).json({});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (this.pendingTransactions[txnId]) {
|
|
436
|
+
try {
|
|
437
|
+
await this.pendingTransactions[txnId];
|
|
438
|
+
res.status(200).json({});
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
__1.LogService.error("Appservice", e);
|
|
442
|
+
res.status(500).json({});
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
__1.LogService.info("Appservice", "Processing transaction " + txnId);
|
|
447
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
448
|
+
this.pendingTransactions[txnId] = new Promise(async (resolve) => {
|
|
449
|
+
// Process all the crypto stuff first to ensure that future transactions (if not this one)
|
|
450
|
+
// will decrypt successfully. We start with EDUs because we need structures to put counts
|
|
451
|
+
// and such into in a later stage, and EDUs are independent of crypto.
|
|
452
|
+
const byUserId = {};
|
|
453
|
+
const orderedEdus = [];
|
|
454
|
+
if (Array.isArray(req.body["de.sorunome.msc2409.to_device"])) {
|
|
455
|
+
orderedEdus.push(...req.body["de.sorunome.msc2409.to_device"].map(e => ({
|
|
456
|
+
...e,
|
|
457
|
+
unsigned: {
|
|
458
|
+
...e['unsigned'],
|
|
459
|
+
[EDU_ANNOTATION_KEY]: EduAnnotation.ToDevice,
|
|
460
|
+
},
|
|
461
|
+
})));
|
|
462
|
+
}
|
|
463
|
+
if (Array.isArray(req.body["de.sorunome.msc2409.ephemeral"])) {
|
|
464
|
+
orderedEdus.push(...req.body["de.sorunome.msc2409.ephemeral"].map(e => ({
|
|
465
|
+
...e,
|
|
466
|
+
unsigned: {
|
|
467
|
+
...e['unsigned'],
|
|
468
|
+
[EDU_ANNOTATION_KEY]: EduAnnotation.Ephemeral,
|
|
469
|
+
},
|
|
470
|
+
})));
|
|
471
|
+
}
|
|
472
|
+
for (let event of orderedEdus) {
|
|
473
|
+
if (event['edu_type'])
|
|
474
|
+
event['type'] = event['edu_type']; // handle property change during MSC2409's course
|
|
475
|
+
__1.LogService.info("Appservice", `Processing ${event['unsigned'][EDU_ANNOTATION_KEY]} event of type ${event["type"]}`);
|
|
476
|
+
event = await this.processEphemeralEvent(event);
|
|
477
|
+
// These events aren't tied to rooms, so just emit them generically
|
|
478
|
+
this.emit("ephemeral.event", event);
|
|
479
|
+
if (this.cryptoStorage && (event["type"] === "m.room.encrypted" || event.unsigned?.[EDU_ANNOTATION_KEY] === EduAnnotation.ToDevice)) {
|
|
480
|
+
const toUser = event["to_user_id"];
|
|
481
|
+
const intent = this.getIntentForUserId(toUser);
|
|
482
|
+
await intent.enableEncryption();
|
|
483
|
+
if (!byUserId[toUser])
|
|
484
|
+
byUserId[toUser] = { counts: null, toDevice: null, unusedFallbacks: null };
|
|
485
|
+
if (!byUserId[toUser].toDevice)
|
|
486
|
+
byUserId[toUser].toDevice = [];
|
|
487
|
+
byUserId[toUser].toDevice.push(event);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const deviceLists = req.body["org.matrix.msc3202.device_lists"] ?? {
|
|
491
|
+
changed: [],
|
|
492
|
+
removed: [],
|
|
493
|
+
};
|
|
494
|
+
if (!deviceLists.changed)
|
|
495
|
+
deviceLists.changed = [];
|
|
496
|
+
if (!deviceLists.removed)
|
|
497
|
+
deviceLists.removed = [];
|
|
498
|
+
if (deviceLists.changed.length || deviceLists.removed.length) {
|
|
499
|
+
this.emit("device_lists", deviceLists);
|
|
500
|
+
}
|
|
501
|
+
let otks = req.body["org.matrix.msc3202.device_one_time_keys_count"];
|
|
502
|
+
const otks2 = req.body["org.matrix.msc3202.device_one_time_key_counts"];
|
|
503
|
+
if (otks2 && !otks) {
|
|
504
|
+
__1.LogService.warn("Appservice", "Your homeserver is using an outdated field (device_one_time_key_counts) to talk to this appservice. " +
|
|
505
|
+
"If you're using Synapse, please upgrade to 1.73.0 or higher.");
|
|
506
|
+
otks = otks2;
|
|
507
|
+
}
|
|
508
|
+
if (otks) {
|
|
509
|
+
this.emit("otk.counts", otks);
|
|
510
|
+
}
|
|
511
|
+
if (otks && this.cryptoStorage) {
|
|
512
|
+
for (const userId of Object.keys(otks)) {
|
|
513
|
+
const intent = this.getIntentForUserId(userId);
|
|
514
|
+
await intent.enableEncryption();
|
|
515
|
+
const otksForUser = otks[userId][intent.underlyingClient.crypto.clientDeviceId];
|
|
516
|
+
if (otksForUser) {
|
|
517
|
+
if (!byUserId[userId]) {
|
|
518
|
+
byUserId[userId] = {
|
|
519
|
+
counts: null,
|
|
520
|
+
toDevice: null,
|
|
521
|
+
unusedFallbacks: null,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
byUserId[userId].counts = otksForUser;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const fallbacks = req.body["org.matrix.msc3202.device_unused_fallback_key_types"];
|
|
529
|
+
if (fallbacks) {
|
|
530
|
+
this.emit("otk.unused_fallback_keys", fallbacks);
|
|
531
|
+
}
|
|
532
|
+
if (fallbacks && this.cryptoStorage) {
|
|
533
|
+
for (const userId of Object.keys(fallbacks)) {
|
|
534
|
+
const intent = this.getIntentForUserId(userId);
|
|
535
|
+
await intent.enableEncryption();
|
|
536
|
+
const fallbacksForUser = fallbacks[userId][intent.underlyingClient.crypto.clientDeviceId];
|
|
537
|
+
if (Array.isArray(fallbacksForUser) && !fallbacksForUser.includes(__1.OTKAlgorithm.Signed)) {
|
|
538
|
+
if (!byUserId[userId]) {
|
|
539
|
+
byUserId[userId] = {
|
|
540
|
+
counts: null,
|
|
541
|
+
toDevice: null,
|
|
542
|
+
unusedFallbacks: null,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
byUserId[userId].unusedFallbacks = fallbacksForUser;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (this.cryptoStorage) {
|
|
550
|
+
for (const userId of Object.keys(byUserId)) {
|
|
551
|
+
const intent = this.getIntentForUserId(userId);
|
|
552
|
+
await intent.enableEncryption();
|
|
553
|
+
const info = byUserId[userId];
|
|
554
|
+
const userStorage = this.storage.storageForUser(userId);
|
|
555
|
+
if (!info.toDevice)
|
|
556
|
+
info.toDevice = [];
|
|
557
|
+
if (!info.unusedFallbacks)
|
|
558
|
+
info.unusedFallbacks = JSON.parse(await userStorage.readValue("last_unused_fallbacks") || "[]");
|
|
559
|
+
if (!info.counts)
|
|
560
|
+
info.counts = JSON.parse(await userStorage.readValue("last_counts") || "{}");
|
|
561
|
+
__1.LogService.info("Appservice", `Updating crypto state for ${userId}`);
|
|
562
|
+
await intent.underlyingClient.crypto.updateSyncData(info.toDevice, info.counts, info.unusedFallbacks, deviceLists.changed, deviceLists.removed);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
for (let event of req.body["events"]) {
|
|
566
|
+
__1.LogService.info("Appservice", `Processing event of type ${event["type"]}`);
|
|
567
|
+
event = await this.processEvent(event);
|
|
568
|
+
if (event['type'] === 'm.room.encrypted') {
|
|
569
|
+
this.emit("room.encrypted_event", event["room_id"], event);
|
|
570
|
+
if (this.cryptoStorage) {
|
|
571
|
+
try {
|
|
572
|
+
const encrypted = new __1.EncryptedRoomEvent(event);
|
|
573
|
+
const roomId = event['room_id'];
|
|
574
|
+
try {
|
|
575
|
+
event = (await this.botClient.crypto.decryptRoomEvent(encrypted, roomId)).raw;
|
|
576
|
+
event = await this.processEvent(event);
|
|
577
|
+
this.emit("room.decrypted_event", roomId, event);
|
|
578
|
+
// For logging purposes: show that the event was decrypted
|
|
579
|
+
__1.LogService.info("Appservice", `Processing decrypted event of type ${event["type"]}`);
|
|
580
|
+
}
|
|
581
|
+
catch (e1) {
|
|
582
|
+
__1.LogService.warn("Appservice", `Bot client was not able to decrypt ${roomId} ${event['event_id']} - trying other intents`);
|
|
583
|
+
let tryUserId;
|
|
584
|
+
try {
|
|
585
|
+
// TODO: This could be more efficient
|
|
586
|
+
const userIdsInRoom = await this.botClient.getJoinedRoomMembers(roomId);
|
|
587
|
+
tryUserId = userIdsInRoom.find(u => this.isNamespacedUser(u));
|
|
588
|
+
}
|
|
589
|
+
catch (e) {
|
|
590
|
+
__1.LogService.error("Appservice", "Failed to get members of room - cannot decrypt message");
|
|
591
|
+
}
|
|
592
|
+
if (tryUserId) {
|
|
593
|
+
const intent = this.getIntentForUserId(tryUserId);
|
|
594
|
+
event = (await intent.underlyingClient.crypto.decryptRoomEvent(encrypted, roomId)).raw;
|
|
595
|
+
event = await this.processEvent(event);
|
|
596
|
+
this.emit("room.decrypted_event", roomId, event);
|
|
597
|
+
// For logging purposes: show that the event was decrypted
|
|
598
|
+
__1.LogService.info("Appservice", `Processing decrypted event of type ${event["type"]}`);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
// noinspection ExceptionCaughtLocallyJS
|
|
602
|
+
throw e1;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
catch (e) {
|
|
607
|
+
__1.LogService.error("Appservice", `Decryption error on ${event['room_id']} ${event['event_id']}`, e);
|
|
608
|
+
this.emit("room.failed_decryption", event['room_id'], event, e);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
this.emit("room.event", event["room_id"], event);
|
|
613
|
+
if (event['type'] === 'm.room.message') {
|
|
614
|
+
this.emit("room.message", event["room_id"], event);
|
|
615
|
+
}
|
|
616
|
+
if (event['type'] === 'm.room.member' && this.isNamespacedUser(event['state_key'])) {
|
|
617
|
+
await this.processMembershipEvent(event);
|
|
618
|
+
}
|
|
619
|
+
if (event['type'] === 'm.room.tombstone' && event['state_key'] === '') {
|
|
620
|
+
this.emit("room.archived", event['room_id'], event);
|
|
621
|
+
}
|
|
622
|
+
if (event['type'] === 'm.room.create' && event['state_key'] === '' && event['content'] && event['content']['predecessor']) {
|
|
623
|
+
this.emit("room.upgraded", event['room_id'], event);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
resolve();
|
|
627
|
+
});
|
|
628
|
+
try {
|
|
629
|
+
await this.pendingTransactions[txnId];
|
|
630
|
+
await Promise.resolve(this.storage.setTransactionCompleted(txnId));
|
|
631
|
+
res.status(200).json({});
|
|
632
|
+
}
|
|
633
|
+
catch (e) {
|
|
634
|
+
__1.LogService.error("Appservice", e);
|
|
635
|
+
res.status(500).json({});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async onUser(req, res) {
|
|
639
|
+
if (!this.isAuthed(req)) {
|
|
640
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const userId = req.params["userId"];
|
|
644
|
+
this.emit("query.user", userId, async (result) => {
|
|
645
|
+
if (result.then)
|
|
646
|
+
result = await result;
|
|
647
|
+
if (result === false) {
|
|
648
|
+
res.status(404).json({ errcode: "USER_DOES_NOT_EXIST", error: "User not created" });
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
const intent = this.getIntentForUserId(userId);
|
|
652
|
+
await intent.ensureRegistered();
|
|
653
|
+
if (result.display_name)
|
|
654
|
+
await intent.underlyingClient.setDisplayName(result.display_name);
|
|
655
|
+
if (result.avatar_mxc)
|
|
656
|
+
await intent.underlyingClient.setAvatarUrl(result.avatar_mxc);
|
|
657
|
+
res.status(200).json(result); // return result for debugging + testing
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
async onRoomAlias(req, res) {
|
|
662
|
+
if (!this.isAuthed(req)) {
|
|
663
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const roomAlias = req.params["roomAlias"];
|
|
667
|
+
this.emit("query.room", roomAlias, async (result) => {
|
|
668
|
+
if (result.then)
|
|
669
|
+
result = await result;
|
|
670
|
+
if (result === false) {
|
|
671
|
+
res.status(404).json({ errcode: "ROOM_DOES_NOT_EXIST", error: "Room not created" });
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
const intent = this.botIntent;
|
|
675
|
+
await intent.ensureRegistered();
|
|
676
|
+
result["room_alias_name"] = roomAlias.substring(1).split(':')[0];
|
|
677
|
+
result["__roomId"] = await intent.underlyingClient.createRoom(result);
|
|
678
|
+
res.status(200).json(result); // return result for debugging + testing
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
async onKeysClaim(req, res) {
|
|
683
|
+
if (!this.isAuthed(req)) {
|
|
684
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (typeof (req.body) !== "object") {
|
|
688
|
+
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
let responded = false;
|
|
692
|
+
this.emit("query.key_claim", req.body, (result) => {
|
|
693
|
+
responded = true;
|
|
694
|
+
const handleResult = (result2) => {
|
|
695
|
+
if (!result2) {
|
|
696
|
+
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
res.status(200).json(result2);
|
|
700
|
+
};
|
|
701
|
+
Promise.resolve(result).then(r => handleResult(r)).catch(e => {
|
|
702
|
+
__1.LogService.error("Appservice", "Error handling key claim API", e);
|
|
703
|
+
res.status(500).json({ errcode: "M_UNKNOWN", error: "Error handling key claim API" });
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
if (!responded) {
|
|
707
|
+
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
async onKeysQuery(req, res) {
|
|
711
|
+
if (!this.isAuthed(req)) {
|
|
712
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (typeof (req.body) !== "object") {
|
|
716
|
+
res.status(400).json({ errcode: "BAD_REQUEST", error: "Expected JSON" });
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
let responded = false;
|
|
720
|
+
this.emit("query.key", req.body, (result) => {
|
|
721
|
+
responded = true;
|
|
722
|
+
const handleResult = (result2) => {
|
|
723
|
+
if (!result2) {
|
|
724
|
+
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
// Implementation note: we could probably query the device keys from our storage if we wanted to.
|
|
728
|
+
res.status(200).json(result2);
|
|
729
|
+
};
|
|
730
|
+
Promise.resolve(result).then(r => handleResult(r)).catch(e => {
|
|
731
|
+
__1.LogService.error("Appservice", "Error handling key query API", e);
|
|
732
|
+
res.status(500).json({ errcode: "M_UNKNOWN", error: "Error handling key query API" });
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
if (!responded) {
|
|
736
|
+
res.status(404).json({ errcode: "M_UNRECOGNIZED", error: "Endpoint not implemented" });
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
onThirdpartyProtocol(req, res) {
|
|
740
|
+
if (!this.isAuthed(req)) {
|
|
741
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const protocol = req.params["protocol"];
|
|
745
|
+
if (!this.registration.protocols.includes(protocol)) {
|
|
746
|
+
res.status(404).json({
|
|
747
|
+
errcode: "PROTOCOL_NOT_HANDLED",
|
|
748
|
+
error: "Protocol is not handled by this appservice",
|
|
749
|
+
});
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
this.emit("thirdparty.protocol", protocol, (protocolResponse) => {
|
|
753
|
+
res.status(200).json(protocolResponse);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
handleThirdpartyObject(req, res, objType, matrixId) {
|
|
757
|
+
if (!this.isAuthed(req)) {
|
|
758
|
+
res.status(401).json({ errcode: "AUTH_FAILED", error: "Authentication failed" });
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const protocol = req.params["protocol"];
|
|
762
|
+
const responseFunc = (items) => {
|
|
763
|
+
if (items && items.length > 0) {
|
|
764
|
+
res.status(200).json(items);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
res.status(404).json({
|
|
768
|
+
errcode: "NO_MAPPING_FOUND",
|
|
769
|
+
error: "No mappings found",
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
// Lookup remote objects(s)
|
|
773
|
+
if (protocol) { // If protocol is given, we are looking up a objects based on fields
|
|
774
|
+
if (!this.registration.protocols.includes(protocol)) {
|
|
775
|
+
res.status(404).json({
|
|
776
|
+
errcode: "PROTOCOL_NOT_HANDLED",
|
|
777
|
+
error: "Protocol is not handled by this appservice",
|
|
778
|
+
});
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
// Remove the access_token
|
|
782
|
+
delete req.query.access_token;
|
|
783
|
+
this.emit(`thirdparty.${objType}.remote`, protocol, req.query, responseFunc);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
else if (matrixId) { // If a user ID is given, we are looking up a remote objects based on a id
|
|
787
|
+
this.emit(`thirdparty.${objType}.matrix`, matrixId, responseFunc);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
res.status(400).json({
|
|
791
|
+
errcode: "INVALID_PARAMETERS",
|
|
792
|
+
error: "Invalid parameters given",
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
onThirdpartyUser(req, res) {
|
|
796
|
+
return this.handleThirdpartyObject(req, res, "user", req.query["userid"]);
|
|
797
|
+
}
|
|
798
|
+
onThirdpartyLocation(req, res) {
|
|
799
|
+
return this.handleThirdpartyObject(req, res, "location", req.query["alias"]);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
exports.Appservice = Appservice;
|
|
803
|
+
//# sourceMappingURL=Appservice.js.map
|