mirror of
https://github.com/Skylar-Tech/node-red-contrib-matrix-chat.git
synced 2025-04-19 20:43:04 -06:00
- Upgrade matrix-js-sdk to ^34.5.0
- Change how matrix-js-sdk is loaded with it now being an ESM module
This commit is contained in:
parent
487241e097
commit
512b7320d5
177
package-lock.json
generated
177
package-lock.json
generated
@ -15,7 +15,7 @@
|
|||||||
"got": "^12.0.2",
|
"got": "^12.0.2",
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"isomorphic-webcrypto": "^2.3.8",
|
"isomorphic-webcrypto": "^2.3.8",
|
||||||
"matrix-js-sdk": "^28.0.0",
|
"matrix-js-sdk": "^34.5.0",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
"node-fetch": "^3.3.0",
|
"node-fetch": "^3.3.0",
|
||||||
"node-localstorage": "^2.2.1",
|
"node-localstorage": "^2.2.1",
|
||||||
@ -4189,13 +4189,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
|
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
|
||||||
"version": "1.2.2",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-7.0.0.tgz",
|
||||||
"integrity": "sha512-iSU2oel9xBZdl/q4ryE2bOZRhIe8x77X4fVY/8qVD5iTt4MwEgJMucb3aA1zGjZP4ptjomoxR0N0zWbx8GcSQw==",
|
"integrity": "sha512-MOencXiW/gI5MuTtCNsuojjwT5DXCrjMqv9xOslJC9h2tPdLFFFMGr58dY5Lis4DRd9MRWcgrGowUIHOqieWTA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@matrix-org/olm": {
|
||||||
|
"version": "3.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@matrix-org/olm/-/olm-3.2.15.tgz",
|
||||||
|
"integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q=="
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -5988,9 +5993,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/events": {
|
"node_modules/@types/events": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
|
||||||
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
|
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/http-cache-semantics": {
|
"node_modules/@types/http-cache-semantics": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
@ -6658,9 +6663,9 @@
|
|||||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||||
},
|
},
|
||||||
"node_modules/base-x": {
|
"node_modules/base-x": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz",
|
||||||
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
|
"integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ=="
|
||||||
},
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
@ -6867,11 +6872,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bs58": {
|
"node_modules/bs58": {
|
||||||
"version": "5.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
|
||||||
"integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
|
"integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base-x": "^4.0.0"
|
"base-x": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bser": {
|
"node_modules/bser": {
|
||||||
@ -7600,11 +7605,6 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/crypto-js": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
|
|
||||||
},
|
|
||||||
"node_modules/crypto-random-string": {
|
"node_modules/crypto-random-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||||
@ -10287,9 +10287,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jwt-decode": {
|
"node_modules/jwt-decode": {
|
||||||
"version": "3.1.2",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.3",
|
"version": "4.5.3",
|
||||||
@ -10900,41 +10903,46 @@
|
|||||||
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
|
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
|
||||||
},
|
},
|
||||||
"node_modules/matrix-js-sdk": {
|
"node_modules/matrix-js-sdk": {
|
||||||
"version": "28.0.0",
|
"version": "34.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-28.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-34.5.0.tgz",
|
||||||
"integrity": "sha512-AdvFETHFkAx/Hf94+JXrdPePWiPQOHu4OsqkVbhoOGDlScLCeafWJzdp5sKNx1Yq12ULXWBCgRO2AL1pU8ZY/A==",
|
"integrity": "sha512-pbp+IxAkSwGmefrlUGCrtrs3UWyqN2iWh4lKnJW+jFIlsksXq7A8vL4cS1z8LXmpcHQAg3mKNuj8n8uhm51t1A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@matrix-org/matrix-sdk-crypto-wasm": "^1.2.1",
|
"@matrix-org/matrix-sdk-crypto-wasm": "^7.0.0",
|
||||||
|
"@matrix-org/olm": "3.2.15",
|
||||||
"another-json": "^0.2.0",
|
"another-json": "^0.2.0",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^6.0.0",
|
||||||
"content-type": "^1.0.4",
|
"content-type": "^1.0.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^4.0.0",
|
||||||
"loglevel": "^1.7.1",
|
"loglevel": "^1.7.1",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-widget-api": "^1.5.0",
|
"matrix-widget-api": "^1.8.2",
|
||||||
"oidc-client-ts": "^2.2.4",
|
"oidc-client-ts": "^3.0.1",
|
||||||
"p-retry": "4",
|
"p-retry": "4",
|
||||||
"sdp-transform": "^2.14.1",
|
"sdp-transform": "^2.14.1",
|
||||||
"unhomoglyph": "^1.0.6",
|
"unhomoglyph": "^1.0.6",
|
||||||
"uuid": "9"
|
"uuid": "10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=20.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/matrix-js-sdk/node_modules/uuid": {
|
"node_modules/matrix-js-sdk/node_modules/uuid": {
|
||||||
"version": "9.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/matrix-widget-api": {
|
"node_modules/matrix-widget-api": {
|
||||||
"version": "1.6.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.9.0.tgz",
|
||||||
"integrity": "sha512-VXIJyAZ/WnBmT4C7ePqevgMYGneKMCP/0JuCOqntSsaNlCRHJvwvTxmqUU+ufOpzIF5gYNyIrAjbgrEbK3iqJQ==",
|
"integrity": "sha512-au8mqralNDqrEvaVAkU37bXOb8I9SCe+ACdPk11QWw58FKstVq31q2wRz+qWA6J+42KJ6s1DggWbG/S3fEs3jw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/events": "^3.0.0",
|
"@types/events": "^3.0.0",
|
||||||
"events": "^3.2.0"
|
"events": "^3.2.0"
|
||||||
@ -12468,15 +12476,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/oidc-client-ts": {
|
"node_modules/oidc-client-ts": {
|
||||||
"version": "2.2.5",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.1.tgz",
|
||||||
"integrity": "sha512-omAHoLdFcylnwZeHJahOnJBwd0r78JzhmVAmsQjLGrexAnQKiHW9Ilr9FlRD5qjMikmabvaucI4k49AbQLXhmQ==",
|
"integrity": "sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crypto-js": "^4.1.1",
|
"jwt-decode": "^4.0.0"
|
||||||
"jwt-decode": "^3.1.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.13.0"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/olm": {
|
"node_modules/olm": {
|
||||||
@ -18740,9 +18747,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@matrix-org/matrix-sdk-crypto-wasm": {
|
"@matrix-org/matrix-sdk-crypto-wasm": {
|
||||||
"version": "1.2.2",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-7.0.0.tgz",
|
||||||
"integrity": "sha512-iSU2oel9xBZdl/q4ryE2bOZRhIe8x77X4fVY/8qVD5iTt4MwEgJMucb3aA1zGjZP4ptjomoxR0N0zWbx8GcSQw=="
|
"integrity": "sha512-MOencXiW/gI5MuTtCNsuojjwT5DXCrjMqv9xOslJC9h2tPdLFFFMGr58dY5Lis4DRd9MRWcgrGowUIHOqieWTA=="
|
||||||
|
},
|
||||||
|
"@matrix-org/olm": {
|
||||||
|
"version": "3.2.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@matrix-org/olm/-/olm-3.2.15.tgz",
|
||||||
|
"integrity": "sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q=="
|
||||||
},
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
@ -20116,9 +20128,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/events": {
|
"@types/events": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
|
||||||
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
|
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
|
||||||
},
|
},
|
||||||
"@types/http-cache-semantics": {
|
"@types/http-cache-semantics": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
@ -20689,9 +20701,9 @@
|
|||||||
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||||
},
|
},
|
||||||
"base-x": {
|
"base-x": {
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz",
|
||||||
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
|
"integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ=="
|
||||||
},
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
@ -20849,11 +20861,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bs58": {
|
"bs58": {
|
||||||
"version": "5.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz",
|
||||||
"integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
|
"integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"base-x": "^4.0.0"
|
"base-x": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bser": {
|
"bser": {
|
||||||
@ -21422,11 +21434,6 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"crypto-js": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
|
|
||||||
},
|
|
||||||
"crypto-random-string": {
|
"crypto-random-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||||
@ -23544,9 +23551,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jwt-decode": {
|
"jwt-decode": {
|
||||||
"version": "3.1.2",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="
|
||||||
},
|
},
|
||||||
"keyv": {
|
"keyv": {
|
||||||
"version": "4.5.3",
|
"version": "4.5.3",
|
||||||
@ -23962,37 +23969,38 @@
|
|||||||
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
|
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
|
||||||
},
|
},
|
||||||
"matrix-js-sdk": {
|
"matrix-js-sdk": {
|
||||||
"version": "28.0.0",
|
"version": "34.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-28.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-34.5.0.tgz",
|
||||||
"integrity": "sha512-AdvFETHFkAx/Hf94+JXrdPePWiPQOHu4OsqkVbhoOGDlScLCeafWJzdp5sKNx1Yq12ULXWBCgRO2AL1pU8ZY/A==",
|
"integrity": "sha512-pbp+IxAkSwGmefrlUGCrtrs3UWyqN2iWh4lKnJW+jFIlsksXq7A8vL4cS1z8LXmpcHQAg3mKNuj8n8uhm51t1A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@matrix-org/matrix-sdk-crypto-wasm": "^1.2.1",
|
"@matrix-org/matrix-sdk-crypto-wasm": "^7.0.0",
|
||||||
|
"@matrix-org/olm": "3.2.15",
|
||||||
"another-json": "^0.2.0",
|
"another-json": "^0.2.0",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^6.0.0",
|
||||||
"content-type": "^1.0.4",
|
"content-type": "^1.0.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^4.0.0",
|
||||||
"loglevel": "^1.7.1",
|
"loglevel": "^1.7.1",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-widget-api": "^1.5.0",
|
"matrix-widget-api": "^1.8.2",
|
||||||
"oidc-client-ts": "^2.2.4",
|
"oidc-client-ts": "^3.0.1",
|
||||||
"p-retry": "4",
|
"p-retry": "4",
|
||||||
"sdp-transform": "^2.14.1",
|
"sdp-transform": "^2.14.1",
|
||||||
"unhomoglyph": "^1.0.6",
|
"unhomoglyph": "^1.0.6",
|
||||||
"uuid": "9"
|
"uuid": "10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "9.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"matrix-widget-api": {
|
"matrix-widget-api": {
|
||||||
"version": "1.6.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.9.0.tgz",
|
||||||
"integrity": "sha512-VXIJyAZ/WnBmT4C7ePqevgMYGneKMCP/0JuCOqntSsaNlCRHJvwvTxmqUU+ufOpzIF5gYNyIrAjbgrEbK3iqJQ==",
|
"integrity": "sha512-au8mqralNDqrEvaVAkU37bXOb8I9SCe+ACdPk11QWw58FKstVq31q2wRz+qWA6J+42KJ6s1DggWbG/S3fEs3jw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/events": "^3.0.0",
|
"@types/events": "^3.0.0",
|
||||||
"events": "^3.2.0"
|
"events": "^3.2.0"
|
||||||
@ -25202,12 +25210,11 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"oidc-client-ts": {
|
"oidc-client-ts": {
|
||||||
"version": "2.2.5",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.1.tgz",
|
||||||
"integrity": "sha512-omAHoLdFcylnwZeHJahOnJBwd0r78JzhmVAmsQjLGrexAnQKiHW9Ilr9FlRD5qjMikmabvaucI4k49AbQLXhmQ==",
|
"integrity": "sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"crypto-js": "^4.1.1",
|
"jwt-decode": "^4.0.0"
|
||||||
"jwt-decode": "^3.1.2"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"olm": {
|
"olm": {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"got": "^12.0.2",
|
"got": "^12.0.2",
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"isomorphic-webcrypto": "^2.3.8",
|
"isomorphic-webcrypto": "^2.3.8",
|
||||||
"matrix-js-sdk": "^28.0.0",
|
"matrix-js-sdk": "^34.5.0",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
"node-fetch": "^3.3.0",
|
"node-fetch": "^3.3.0",
|
||||||
"node-localstorage": "^2.2.1",
|
"node-localstorage": "^2.2.1",
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const {RelationType, EventType, Direction} = require("matrix-js-sdk");
|
|
||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
function MatrixFetchRelations(n) {
|
function MatrixFetchRelations(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
@ -43,6 +41,8 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.on("input", async function(msg) {
|
node.on("input", async function(msg) {
|
||||||
|
const {Direction} = await import("matrix-js-sdk");
|
||||||
|
|
||||||
if (!node.server || !node.server.matrixClient) {
|
if (!node.server || !node.server.matrixClient) {
|
||||||
node.error("No matrix server selected", msg);
|
node.error("No matrix server selected", msg);
|
||||||
return;
|
return;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const {TimelineWindow, RelationType, Filter} = require("matrix-js-sdk");
|
|
||||||
const crypto = require('crypto');
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
function MatrixReceiveMessage(n) {
|
function MatrixReceiveMessage(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const {TimelineWindow, RelationType, Filter} = require("matrix-js-sdk");
|
|
||||||
const crypto = require('crypto');
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
function MatrixReceiveMessage(n) {
|
function MatrixReceiveMessage(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
@ -34,6 +32,9 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.on("input", async function (msg) {
|
node.on("input", async function (msg) {
|
||||||
|
const {TimelineWindow, RelationType, Filter} = await import("matrix-js-sdk");
|
||||||
|
const crypto = await import('crypto');
|
||||||
|
|
||||||
if (! node.server || ! node.server.matrixClient) {
|
if (! node.server || ! node.server.matrixClient) {
|
||||||
node.error("No matrix server selected", msg);
|
node.error("No matrix server selected", msg);
|
||||||
return;
|
return;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const {RelationType} = require("matrix-js-sdk");
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
function MatrixReceiveMessage(n) {
|
function MatrixReceiveMessage(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const {RelationType} = require("matrix-js-sdk");
|
|
||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
function MatrixSendImage(n) {
|
function MatrixSendImage(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
@ -68,7 +66,8 @@ module.exports = function(RED) {
|
|||||||
node.status({ fill: "green", shape: "ring", text: "connected" });
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on("input", function (msg) {
|
node.on("input", async function (msg) {
|
||||||
|
const {RelationType} = await import("matrix-js-sdk");
|
||||||
function getToValue(msg, type, property) {
|
function getToValue(msg, type, property) {
|
||||||
let value = property;
|
let value = property;
|
||||||
if (type === "msg") {
|
if (type === "msg") {
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
const {RelationType, TimelineWindow} = require("matrix-js-sdk");
|
|
||||||
|
|
||||||
global.Olm = require('olm');
|
global.Olm = require('olm');
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const sdk = require("matrix-js-sdk");
|
|
||||||
const { resolve } = require('path');
|
const { resolve } = require('path');
|
||||||
const { LocalStorage } = require('node-localstorage');
|
const { LocalStorage } = require('node-localstorage');
|
||||||
const { LocalStorageCryptoStore } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
|
||||||
const {RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent, MemoryStore} = require("matrix-js-sdk");
|
|
||||||
const request = require("request");
|
const request = require("request");
|
||||||
require("abort-controller/polyfill"); // polyfill abort-controller if we don't have it
|
require("abort-controller/polyfill"); // polyfill abort-controller if we don't have it
|
||||||
|
|
||||||
if (!globalThis.fetch) {
|
if (!globalThis.fetch) {
|
||||||
// polyfill fetch if we don't have it
|
// polyfill fetch if we don't have it
|
||||||
if (!globalThis.fetch) {
|
if (!globalThis.fetch) {
|
||||||
@ -19,6 +15,11 @@ if (!globalThis.fetch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
|
// Prepare dynamic imports
|
||||||
|
const sdkPromise = import("matrix-js-sdk");
|
||||||
|
const LocalStorageCryptoStorePromise = import('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store.js');
|
||||||
|
const loggerPromise = import('matrix-js-sdk/lib/logger.js');
|
||||||
|
|
||||||
// disable logging if set to "off"
|
// disable logging if set to "off"
|
||||||
let loggingSettings = RED.settings.get('logging');
|
let loggingSettings = RED.settings.get('logging');
|
||||||
if(
|
if(
|
||||||
@ -26,8 +27,9 @@ module.exports = function(RED) {
|
|||||||
typeof loggingSettings.console.level !== 'undefined' &&
|
typeof loggingSettings.console.level !== 'undefined' &&
|
||||||
['info','debug','trace'].indexOf(loggingSettings.console.level.toLowerCase()) >= 0
|
['info','debug','trace'].indexOf(loggingSettings.console.level.toLowerCase()) >= 0
|
||||||
) {
|
) {
|
||||||
const { logger } = require('matrix-js-sdk/lib/logger');
|
loggerPromise.then(({ logger }) => {
|
||||||
logger.disableAll();
|
logger.disableAll();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function MatrixFolderNameFromUserId(name) {
|
function MatrixFolderNameFromUserId(name) {
|
||||||
@ -66,7 +68,7 @@ module.exports = function(RED) {
|
|||||||
node.deregister = function(consumerNode) {
|
node.deregister = function(consumerNode) {
|
||||||
delete node.users[consumerNode.id];
|
delete node.users[consumerNode.id];
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!this.userId) {
|
if(!this.userId) {
|
||||||
node.log("Matrix connection failed: missing user ID in configuration.");
|
node.log("Matrix connection failed: missing user ID in configuration.");
|
||||||
return;
|
return;
|
||||||
@ -83,7 +85,7 @@ module.exports = function(RED) {
|
|||||||
} else if(!this.url) {
|
} else if(!this.url) {
|
||||||
node.error("Matrix connection failed: missing server URL in configuration.", {});
|
node.error("Matrix connection failed: missing server URL in configuration.", {});
|
||||||
} else {
|
} else {
|
||||||
node.setConnected = async function(connected, cb) {
|
node.setConnected = function(connected, cb) {
|
||||||
if (node.connected !== connected) {
|
if (node.connected !== connected) {
|
||||||
node.connected = connected;
|
node.connected = connected;
|
||||||
if(typeof cb === 'function') {
|
if(typeof cb === 'function') {
|
||||||
@ -145,313 +147,289 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
fs.ensureDirSync(storageDir); // create storage directory if it doesn't exist
|
fs.ensureDirSync(storageDir); // create storage directory if it doesn't exist
|
||||||
upgradeDirectoryIfNecessary(node, storageDir);
|
upgradeDirectoryIfNecessary(node, storageDir);
|
||||||
node.matrixClient = sdk.createClient({
|
|
||||||
baseUrl: this.url,
|
|
||||||
accessToken: this.credentials.accessToken,
|
|
||||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
|
||||||
store: new MemoryStore({
|
|
||||||
localStorage: localStorage,
|
|
||||||
}),
|
|
||||||
userId: this.userId,
|
|
||||||
deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined,
|
|
||||||
request
|
|
||||||
// verificationMethods: ["m.sas.v1"]
|
|
||||||
});
|
|
||||||
|
|
||||||
node.debug(`hasLazyLoadMembersEnabled=${node.matrixClient.hasLazyLoadMembersEnabled()}`);
|
// Wait for the dynamic imports to resolve
|
||||||
|
Promise.all([sdkPromise, LocalStorageCryptoStorePromise]).then(([sdkModule, LocalStorageCryptoStoreModule]) => {
|
||||||
|
const sdk = sdkModule.default || sdkModule;
|
||||||
|
const { LocalStorageCryptoStore } = LocalStorageCryptoStoreModule;
|
||||||
|
|
||||||
// set globally if configured to do so
|
node.matrixClient = sdk.createClient({
|
||||||
if(this.globalAccess) {
|
baseUrl: this.url,
|
||||||
this.context().global.set('matrixClient["'+this.userId+'"]', node.matrixClient);
|
accessToken: this.credentials.accessToken,
|
||||||
}
|
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||||
|
store: new sdk.MemoryStore({
|
||||||
function stopClient() {
|
localStorage: localStorage,
|
||||||
if(node.matrixClient && node.matrixClient.clientRunning) {
|
}),
|
||||||
node.matrixClient.stopClient();
|
userId: this.userId,
|
||||||
node.setConnected(false);
|
deviceId: (this.deviceId || getStoredDeviceId(localStorage)) || undefined,
|
||||||
}
|
request
|
||||||
|
// verificationMethods: ["m.sas.v1"]
|
||||||
if(retryStartTimeout) {
|
|
||||||
clearTimeout(retryStartTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node.on('close', function(done) {
|
|
||||||
stopClient();
|
|
||||||
if(node.globalAccess) {
|
|
||||||
try {
|
|
||||||
node.context().global.set('matrixClient["'+node.userId+'"]', undefined);
|
|
||||||
} catch(e){
|
|
||||||
node.error(e.message, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
node.isConnected = function() {
|
|
||||||
return node.connected;
|
|
||||||
};
|
|
||||||
|
|
||||||
node.matrixClient.on(RoomEvent.Timeline, async function(event, room, toStartOfTimeline, removed, data) {
|
|
||||||
if (toStartOfTimeline) {
|
|
||||||
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: paginated result");
|
|
||||||
return; // ignore paginated results
|
|
||||||
}
|
|
||||||
if (!data || !data.liveEvent) {
|
|
||||||
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message");
|
|
||||||
return; // ignore old message (we only want live events)
|
|
||||||
}
|
|
||||||
if(node.initializedAt > event.getDate()) {
|
|
||||||
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message before init");
|
|
||||||
return; // skip events that occurred before our client initialized
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await node.matrixClient.decryptEventIfNeeded(event);
|
|
||||||
} catch (error) {
|
|
||||||
node.error(error, {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDmRoom = (room) => {
|
|
||||||
// Find out if this is a direct message room.
|
|
||||||
let isDM = !!room.getDMInviter();
|
|
||||||
const allMembers = room.currentState.getMembers();
|
|
||||||
if (!isDM && allMembers.length <= 2) {
|
|
||||||
// if not a DM, but there are 2 users only
|
|
||||||
// double check DM (needed because getDMInviter works only if you were invited, not if you invite)
|
|
||||||
// hence why we check for each member
|
|
||||||
if (allMembers.some((m) => m.getDMInviter())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allMembers.length <= 2 && isDM;
|
|
||||||
};
|
|
||||||
|
|
||||||
let msg = {
|
|
||||||
encrypted : event.isEncrypted(),
|
|
||||||
redacted : event.isRedacted(),
|
|
||||||
content : event.getContent(),
|
|
||||||
type : (event.getContent()['msgtype'] || event.getType()) || null,
|
|
||||||
payload : (event.getContent()['body'] || event.getContent()) || null,
|
|
||||||
isDM : isDmRoom(room),
|
|
||||||
isThread : event.getContent()?.['m.relates_to']?.rel_type === RelationType.Thread,
|
|
||||||
mentions : event.getContent()["m.mentions"] || null,
|
|
||||||
userId : event.getSender(),
|
|
||||||
user : node.matrixClient.getUser(event.getSender()),
|
|
||||||
topic : event.getRoomId(),
|
|
||||||
eventId : event.getId(),
|
|
||||||
event : event,
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove keys from user property that start with an underscore
|
|
||||||
Object.keys(msg.user).forEach(function (key) {
|
|
||||||
if (/^_/.test(key)) {
|
|
||||||
delete msg.user[key];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
node.log(`Received ${msg.encrypted ? 'encrypted ' : ''}timeline event [${msg.type}]: (${room.name}) ${event.getSender()} :: ${msg.content.body} ${toStartOfTimeline ? ' [PAGINATED]' : ''}`);
|
node.debug(`hasLazyLoadMembersEnabled=${node.matrixClient.hasLazyLoadMembersEnabled()}`);
|
||||||
node.emit("Room.timeline", event, room, toStartOfTimeline, removed, data, msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
// set globally if configured to do so
|
||||||
* Fires when we want to suggest to the user that they restore their megolm keys
|
if(this.globalAccess) {
|
||||||
* from backup or by cross-signing the device.
|
this.context().global.set('matrixClient["'+this.userId+'"]', node.matrixClient);
|
||||||
*
|
|
||||||
* @event module:client~MatrixClient#"crypto.suggestKeyRestore"
|
|
||||||
*/
|
|
||||||
// node.matrixClient.on("crypto.suggestKeyRestore", function(){
|
|
||||||
//
|
|
||||||
// });
|
|
||||||
|
|
||||||
// node.matrixClient.on("RoomMember.typing", async function(event, member) {
|
|
||||||
// let isTyping = member.typing;
|
|
||||||
// let roomId = member.roomId;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// node.matrixClient.on("RoomMember.powerLevel", async function(event, member) {
|
|
||||||
// let newPowerLevel = member.powerLevel;
|
|
||||||
// let newNormPowerLevel = member.powerLevelNorm;
|
|
||||||
// let roomId = member.roomId;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// node.matrixClient.on("RoomMember.name", async function(event, member) {
|
|
||||||
// let newName = member.name;
|
|
||||||
// let roomId = member.roomId;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// handle auto-joining rooms
|
|
||||||
|
|
||||||
node.matrixClient.on(RoomMemberEvent.Membership, async function(event, member) {
|
|
||||||
if(node.initializedAt > event.getDate()) {
|
|
||||||
return; // skip events that occurred before our client initialized
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member.membership === "invite" && member.userId === node.userId) {
|
function stopClient() {
|
||||||
node.log("Got invite to join room " + member.roomId);
|
if(node.matrixClient && node.matrixClient.clientRunning) {
|
||||||
if(node.autoAcceptRoomInvites) {
|
node.matrixClient.stopClient();
|
||||||
node.matrixClient.joinRoom(member.roomId).then(function() {
|
node.setConnected(false);
|
||||||
node.log("Automatically accepted invitation to join room " + member.roomId);
|
}
|
||||||
}).catch(function(e) {
|
|
||||||
node.warn("Cannot join room (could be from being kicked/banned) " + member.roomId + ": " + e);
|
if(retryStartTimeout) {
|
||||||
|
clearTimeout(retryStartTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.on('close', function(done) {
|
||||||
|
stopClient();
|
||||||
|
if(node.globalAccess) {
|
||||||
|
try {
|
||||||
|
node.context().global.set('matrixClient["'+node.userId+'"]', undefined);
|
||||||
|
} catch(e){
|
||||||
|
node.error(e.message, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
node.isConnected = function() {
|
||||||
|
return node.connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { RelationType, RoomEvent, RoomMemberEvent, HttpApiEvent, ClientEvent } = sdk;
|
||||||
|
|
||||||
|
node.matrixClient.on(RoomEvent.Timeline, async function(event, room, toStartOfTimeline, removed, data) {
|
||||||
|
if (toStartOfTimeline) {
|
||||||
|
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: paginated result");
|
||||||
|
return; // ignore paginated results
|
||||||
|
}
|
||||||
|
if (!data || !data.liveEvent) {
|
||||||
|
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message");
|
||||||
|
return; // ignore old message (we only want live events)
|
||||||
|
}
|
||||||
|
if(node.initializedAt > event.getDate()) {
|
||||||
|
node.log("Ignoring" + (event.isEncrypted() ? ' encrypted' : '') +" timeline event [" + (event.getContent()['msgtype'] || event.getType()) + "]: (" + room.name + ") " + event.getId() + " for reason: old message before init");
|
||||||
|
return; // skip events that occurred before our client initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await node.matrixClient.decryptEventIfNeeded(event);
|
||||||
|
} catch (error) {
|
||||||
|
node.error(error, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDmRoom = (room) => {
|
||||||
|
// Find out if this is a direct message room.
|
||||||
|
let isDM = !!room.getDMInviter();
|
||||||
|
const allMembers = room.currentState.getMembers();
|
||||||
|
if (!isDM && allMembers.length <= 2) {
|
||||||
|
// if not a DM, but there are 2 users only
|
||||||
|
// double check DM (needed because getDMInviter works only if you were invited, not if you invite)
|
||||||
|
// hence why we check for each member
|
||||||
|
if (allMembers.some((m) => m.getDMInviter())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allMembers.length <= 2 && isDM;
|
||||||
|
};
|
||||||
|
|
||||||
|
let msg = {
|
||||||
|
encrypted : event.isEncrypted(),
|
||||||
|
redacted : event.isRedacted(),
|
||||||
|
content : event.getContent(),
|
||||||
|
type : (event.getContent()['msgtype'] || event.getType()) || null,
|
||||||
|
payload : (event.getContent()['body'] || event.getContent()) || null,
|
||||||
|
isDM : isDmRoom(room),
|
||||||
|
isThread : event.getContent()?.['m.relates_to']?.rel_type === RelationType.Thread,
|
||||||
|
mentions : event.getContent()["m.mentions"] || null,
|
||||||
|
userId : event.getSender(),
|
||||||
|
user : node.matrixClient.getUser(event.getSender()),
|
||||||
|
topic : event.getRoomId(),
|
||||||
|
eventId : event.getId(),
|
||||||
|
event : event,
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove keys from user property that start with an underscore
|
||||||
|
if (msg.user) {
|
||||||
|
Object.keys(msg.user).forEach(function (key) {
|
||||||
|
if (/^_/.test(key)) {
|
||||||
|
delete msg.user[key];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let room = node.matrixClient.getRoom(event.getRoomId());
|
node.log(`Received ${msg.encrypted ? 'encrypted ' : ''}timeline event [${msg.type}]: (${room.name}) ${event.getSender()} :: ${msg.content.body} ${toStartOfTimeline ? ' [PAGINATED]' : ''}`);
|
||||||
node.emit("Room.invite", {
|
node.emit("Room.timeline", event, room, toStartOfTimeline, removed, data, msg);
|
||||||
type : 'm.room.member',
|
});
|
||||||
userId : event.getSender(),
|
|
||||||
topic : event.getRoomId(),
|
|
||||||
topicName : (room ? room.name : null) || null,
|
|
||||||
event : event,
|
|
||||||
eventId : event.getId(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
node.matrixClient.on(ClientEvent.Sync, async function(state, prevState, data) {
|
// handle auto-joining rooms
|
||||||
node.debug("SYNC [STATE=" + state + "] [PREVSTATE=" + prevState + "]");
|
|
||||||
if(prevState === null && state === "PREPARED" ) {
|
|
||||||
// Occurs when the initial sync is completed first time.
|
|
||||||
// This involves setting up filters and obtaining push rules.
|
|
||||||
node.setConnected(true, function(){
|
|
||||||
node.log("Matrix client connected");
|
|
||||||
});
|
|
||||||
} else if(prevState === null && state === "ERROR") {
|
|
||||||
// Occurs when the initial sync failed first time.
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Failed to connect to Matrix server", {});
|
|
||||||
});
|
|
||||||
} else if(prevState === "ERROR" && state === "PREPARED") {
|
|
||||||
// Occurs when the initial sync succeeds
|
|
||||||
// after previously failing.
|
|
||||||
node.setConnected(true, function(){
|
|
||||||
node.log("Matrix client connected");
|
|
||||||
});
|
|
||||||
} else if(prevState === "PREPARED" && state === "SYNCING") {
|
|
||||||
// Occurs immediately after transitioning to PREPARED.
|
|
||||||
// Starts listening for live updates rather than catching up.
|
|
||||||
node.setConnected(true, function(){
|
|
||||||
node.log("Matrix client connected");
|
|
||||||
});
|
|
||||||
} else if(prevState === "SYNCING" && state === "RECONNECTING") {
|
|
||||||
// Occurs when the live update fails.
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Connection to Matrix server lost", {});
|
|
||||||
});
|
|
||||||
} else if(prevState === "RECONNECTING" && state === "RECONNECTING") {
|
|
||||||
// Can occur if the update calls continue to fail,
|
|
||||||
// but the keepalive calls (to /versions) succeed.
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Connection to Matrix server lost", {});
|
|
||||||
});
|
|
||||||
} else if(prevState === "RECONNECTING" && state === "ERROR") {
|
|
||||||
// Occurs when the keepalive call also fails
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Connection to Matrix server lost", {});
|
|
||||||
});
|
|
||||||
} else if(prevState === "ERROR" && state === "SYNCING") {
|
|
||||||
// Occurs when the client has performed a
|
|
||||||
// live update after having previously failed.
|
|
||||||
node.setConnected(true, function(){
|
|
||||||
node.log("Matrix client connected");
|
|
||||||
});
|
|
||||||
} else if(prevState === "ERROR" && state === "ERROR") {
|
|
||||||
// Occurs when the client has failed to
|
|
||||||
// keepalive for a second time or more.
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Connection to Matrix server lost", {});
|
|
||||||
});
|
|
||||||
} else if(prevState === "SYNCING" && state === "SYNCING") {
|
|
||||||
// Occurs when the client has performed a live update.
|
|
||||||
// This is called <i>after</i> processing.
|
|
||||||
node.setConnected(true, function(){
|
|
||||||
node.log("Matrix client connected");
|
|
||||||
});
|
|
||||||
} else if(state === "STOPPED") {
|
|
||||||
// Occurs once the client has stopped syncing or
|
|
||||||
// trying to sync after stopClient has been called.
|
|
||||||
node.setConnected(false, function(){
|
|
||||||
node.error("Connection to Matrix server lost", {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
node.matrixClient.on(RoomMemberEvent.Membership, async function(event, member) {
|
||||||
node.matrixClient.on(HttpApiEvent.SessionLoggedOut, async function(errorObj){
|
if(node.initializedAt > event.getDate()) {
|
||||||
// Example if user auth token incorrect:
|
return; // skip events that occurred before our client initialized
|
||||||
// {
|
|
||||||
// errcode: 'M_UNKNOWN_TOKEN',
|
|
||||||
// data: {
|
|
||||||
// errcode: 'M_UNKNOWN_TOKEN',
|
|
||||||
// error: 'Invalid macaroon passed.',
|
|
||||||
// soft_logout: false
|
|
||||||
// },
|
|
||||||
// httpStatus: 401
|
|
||||||
// }
|
|
||||||
|
|
||||||
node.error("Authentication failure: " + errorObj, {});
|
|
||||||
stopClient();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
try {
|
|
||||||
if(node.e2ee){
|
|
||||||
node.log("Initializing crypto...");
|
|
||||||
await node.matrixClient.initCrypto();
|
|
||||||
node.matrixClient.getCrypto().globalBlacklistUnverifiedDevices = false; // prevent errors from unverified devices
|
|
||||||
}
|
}
|
||||||
node.log("Connecting to Matrix server...");
|
|
||||||
await node.matrixClient.startClient({
|
|
||||||
initialSyncLimit: node.initialSyncLimit
|
|
||||||
});
|
|
||||||
} catch(error) {
|
|
||||||
node.error(error, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do an authed request and only continue if we don't get an error
|
if (member.membership === "invite" && member.userId === node.userId) {
|
||||||
// this prevent the matrix client from crashing Node-RED on invalid auth token
|
node.log("Got invite to join room " + member.roomId);
|
||||||
(function checkAuthTokenThenStart() {
|
if(node.autoAcceptRoomInvites) {
|
||||||
if(node.matrixClient.clientRunning) {
|
node.matrixClient.joinRoom(member.roomId).then(function() {
|
||||||
return;
|
node.log("Automatically accepted invitation to join room " + member.roomId);
|
||||||
}
|
}).catch(function(e) {
|
||||||
|
node.warn("Cannot join room (could be from being kicked/banned) " + member.roomId + ": " + e);
|
||||||
/**
|
});
|
||||||
* We do a /whoami request before starting for a few reasons:
|
|
||||||
* - validate our auth token
|
|
||||||
* - make sure auth token belongs to provided node.userId
|
|
||||||
* - fetch device_id if possible (only available on Synapse >= v1.40.0 under MSC2033)
|
|
||||||
*/
|
|
||||||
node.matrixClient.whoami()
|
|
||||||
.then(
|
|
||||||
function(data) {
|
|
||||||
if((typeof data['device_id'] === undefined || !data['device_id']) && !node.deviceId && !getStoredDeviceId(localStorage)) {
|
|
||||||
node.error("/whoami request did not return device_id. You will need to manually set one in your configuration because this cannot be automatically fetched.", {});
|
|
||||||
}
|
|
||||||
if('device_id' in data && data['device_id'] && !node.deviceId) {
|
|
||||||
// if we have no device_id configured lets use the one
|
|
||||||
// returned by /whoami for this access_token
|
|
||||||
node.matrixClient.deviceId = data['device_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure our userId matches the access token's
|
|
||||||
if(data['user_id'].toLowerCase() !== node.userId.toLowerCase()) {
|
|
||||||
node.error(`User ID provided is ${node.userId} but token belongs to ${data['user_id']}`, {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
run().catch((error) => node.error(error));
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
// if the error isn't authentication related retry in a little bit
|
|
||||||
if(err.code !== "M_UNKNOWN_TOKEN") {
|
|
||||||
retryStartTimeout = setTimeout(checkAuthTokenThenStart, 15000);
|
|
||||||
node.error("Auth check failed: " + err, {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
})();
|
let room = node.matrixClient.getRoom(event.getRoomId());
|
||||||
|
node.emit("Room.invite", {
|
||||||
|
type : 'm.room.member',
|
||||||
|
userId : event.getSender(),
|
||||||
|
topic : event.getRoomId(),
|
||||||
|
topicName : (room ? room.name : null) || null,
|
||||||
|
event : event,
|
||||||
|
eventId : event.getId(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
node.matrixClient.on(ClientEvent.Sync, async function(state, prevState, data) {
|
||||||
|
node.debug("SYNC [STATE=" + state + "] [PREVSTATE=" + prevState + "]");
|
||||||
|
if(prevState === null && state === "PREPARED" ) {
|
||||||
|
// Occurs when the initial sync is completed first time.
|
||||||
|
// This involves setting up filters and obtaining push rules.
|
||||||
|
node.setConnected(true, function(){
|
||||||
|
node.log("Matrix client connected");
|
||||||
|
});
|
||||||
|
} else if(prevState === null && state === "ERROR") {
|
||||||
|
// Occurs when the initial sync failed first time.
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Failed to connect to Matrix server", {});
|
||||||
|
});
|
||||||
|
} else if(prevState === "ERROR" && state === "PREPARED") {
|
||||||
|
// Occurs when the initial sync succeeds
|
||||||
|
// after previously failing.
|
||||||
|
node.setConnected(true, function(){
|
||||||
|
node.log("Matrix client connected");
|
||||||
|
});
|
||||||
|
} else if(prevState === "PREPARED" && state === "SYNCING") {
|
||||||
|
// Occurs immediately after transitioning to PREPARED.
|
||||||
|
// Starts listening for live updates rather than catching up.
|
||||||
|
node.setConnected(true, function(){
|
||||||
|
node.log("Matrix client connected");
|
||||||
|
});
|
||||||
|
} else if(prevState === "SYNCING" && state === "RECONNECTING") {
|
||||||
|
// Occurs when the live update fails.
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Connection to Matrix server lost", {});
|
||||||
|
});
|
||||||
|
} else if(prevState === "RECONNECTING" && state === "RECONNECTING") {
|
||||||
|
// Can occur if the update calls continue to fail,
|
||||||
|
// but the keepalive calls (to /versions) succeed.
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Connection to Matrix server lost", {});
|
||||||
|
});
|
||||||
|
} else if(prevState === "RECONNECTING" && state === "ERROR") {
|
||||||
|
// Occurs when the keepalive call also fails
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Connection to Matrix server lost", {});
|
||||||
|
});
|
||||||
|
} else if(prevState === "ERROR" && state === "SYNCING") {
|
||||||
|
// Occurs when the client has performed a
|
||||||
|
// live update after having previously failed.
|
||||||
|
node.setConnected(true, function(){
|
||||||
|
node.log("Matrix client connected");
|
||||||
|
});
|
||||||
|
} else if(prevState === "ERROR" && state === "ERROR") {
|
||||||
|
// Occurs when the client has failed to
|
||||||
|
// keepalive for a second time or more.
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Connection to Matrix server lost", {});
|
||||||
|
});
|
||||||
|
} else if(prevState === "SYNCING" && state === "SYNCING") {
|
||||||
|
// Occurs when the client has performed a live update.
|
||||||
|
// This is called <i>after</i> processing.
|
||||||
|
node.setConnected(true, function(){
|
||||||
|
node.log("Matrix client connected");
|
||||||
|
});
|
||||||
|
} else if(state === "STOPPED") {
|
||||||
|
// Occurs once the client has stopped syncing or
|
||||||
|
// trying to sync after stopClient has been called.
|
||||||
|
node.setConnected(false, function(){
|
||||||
|
node.error("Connection to Matrix server lost", {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
node.matrixClient.on(HttpApiEvent.SessionLoggedOut, async function(errorObj){
|
||||||
|
node.error("Authentication failure: " + errorObj, {});
|
||||||
|
stopClient();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
if(node.e2ee){
|
||||||
|
node.log("Initializing crypto...");
|
||||||
|
await node.matrixClient.initCrypto();
|
||||||
|
node.matrixClient.getCrypto().globalBlacklistUnverifiedDevices = false; // prevent errors from unverified devices
|
||||||
|
}
|
||||||
|
node.log("Connecting to Matrix server...");
|
||||||
|
await node.matrixClient.startClient({
|
||||||
|
initialSyncLimit: node.initialSyncLimit
|
||||||
|
});
|
||||||
|
} catch(error) {
|
||||||
|
node.error(error, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do an authed request and only continue if we don't get an error
|
||||||
|
// this prevent the matrix client from crashing Node-RED on invalid auth token
|
||||||
|
(function checkAuthTokenThenStart() {
|
||||||
|
if(node.matrixClient.clientRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We do a /whoami request before starting for a few reasons:
|
||||||
|
* - validate our auth token
|
||||||
|
* - make sure auth token belongs to provided node.userId
|
||||||
|
* - fetch device_id if possible (only available on Synapse >= v1.40.0 under MSC2033)
|
||||||
|
*/
|
||||||
|
node.matrixClient.whoami()
|
||||||
|
.then(
|
||||||
|
function(data) {
|
||||||
|
if((typeof data['device_id'] === undefined || !data['device_id']) && !node.deviceId && !getStoredDeviceId(localStorage)) {
|
||||||
|
node.error("/whoami request did not return device_id. You will need to manually set one in your configuration because this cannot be automatically fetched.", {});
|
||||||
|
}
|
||||||
|
if('device_id' in data && data['device_id'] && !node.deviceId) {
|
||||||
|
// if we have no device_id configured lets use the one
|
||||||
|
// returned by /whoami for this access_token
|
||||||
|
node.matrixClient.deviceId = data['device_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure our userId matches the access token's
|
||||||
|
if(data['user_id'].toLowerCase() !== node.userId.toLowerCase()) {
|
||||||
|
node.error(`User ID provided is ${node.userId} but token belongs to ${data['user_id']}`, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
run().catch((error) => node.error(error));
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
// if the error isn't authentication related retry in a little bit
|
||||||
|
if(err.code !== "M_UNKNOWN_TOKEN") {
|
||||||
|
retryStartTimeout = setTimeout(checkAuthTokenThenStart, 15000);
|
||||||
|
node.error("Auth check failed: " + err, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})();
|
||||||
|
}).catch((error) => {
|
||||||
|
node.error("Failed to load Matrix SDK modules: " + error, {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,43 +453,50 @@ module.exports = function(RED) {
|
|||||||
deviceId = req.body.deviceId || undefined,
|
deviceId = req.body.deviceId || undefined,
|
||||||
displayName = req.body.displayName || undefined;
|
displayName = req.body.displayName || undefined;
|
||||||
|
|
||||||
const matrixClient = sdk.createClient({
|
sdkPromise.then((sdkModule) => {
|
||||||
baseUrl: baseUrl,
|
const sdk = sdkModule.default || sdk;
|
||||||
deviceId: deviceId,
|
|
||||||
timelineSupport: true,
|
const matrixClient = sdk.createClient({
|
||||||
localTimeoutMs: '30000',
|
baseUrl: baseUrl,
|
||||||
request
|
deviceId: deviceId,
|
||||||
|
timelineSupport: true,
|
||||||
|
localTimeoutMs: '30000',
|
||||||
|
request
|
||||||
|
});
|
||||||
|
|
||||||
|
matrixClient.timelineSupport = true;
|
||||||
|
|
||||||
|
matrixClient.login(
|
||||||
|
'm.login.password', {
|
||||||
|
identifier: {
|
||||||
|
type: 'm.id.user',
|
||||||
|
user: userId,
|
||||||
|
},
|
||||||
|
password: password,
|
||||||
|
initial_device_display_name: displayName
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function(response) {
|
||||||
|
res.json({
|
||||||
|
'result': 'ok',
|
||||||
|
'token': response.access_token,
|
||||||
|
'device_id': response.device_id,
|
||||||
|
'user_id': response.user_id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
res.json({
|
||||||
|
'result': 'error',
|
||||||
|
'message': err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}).catch((error) => {
|
||||||
|
res.json({
|
||||||
|
'result': 'error',
|
||||||
|
'message': "Failed to load Matrix SDK: " + error
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
new TimelineWindow()
|
|
||||||
|
|
||||||
matrixClient.timelineSupport = true;
|
|
||||||
|
|
||||||
matrixClient.login(
|
|
||||||
'm.login.password', {
|
|
||||||
identifier: {
|
|
||||||
type: 'm.id.user',
|
|
||||||
user: userId,
|
|
||||||
},
|
|
||||||
password: password,
|
|
||||||
initial_device_display_name: displayName
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
function(response) {
|
|
||||||
res.json({
|
|
||||||
'result': 'ok',
|
|
||||||
'token': response.access_token,
|
|
||||||
'device_id': response.device_id,
|
|
||||||
'user_id': response.user_id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
res.json({
|
|
||||||
'result': 'error',
|
|
||||||
'message': err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function upgradeDirectoryIfNecessary(node, storageDir) {
|
function upgradeDirectoryIfNecessary(node, storageDir) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user