diff --git a/package-lock.json b/package-lock.json index a03e54f..ef3a926 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "node-red-contrib-matrix-chat", - "version": "0.7.1", + "version": "0.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "node-red-contrib-matrix-chat", - "version": "0.7.1", + "version": "0.8.0", "license": "SEE LICENSE FILE", "dependencies": { "abort-controller": "^3.0.0", @@ -21,6 +21,7 @@ "node-localstorage": "^2.2.1", "olm": "https://gitlab.matrix.org/matrix-org/olm/-/package_files/2572/download", "request": "^2.88.2", + "sharp": "^0.33.4", "tmp": "^0.2.1", "utf8": "^3.0.0" }, @@ -2644,6 +2645,15 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@expo/bunyan": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.0.tgz", @@ -3522,6 +3532,437 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", + "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", + "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=10.13", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", + "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.28", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", + "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", + "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.31", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", + "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", + "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", + "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "musl": ">=1.2.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", + "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.1.1" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", + "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", + "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jest/create-cache-key-function": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.6.3.tgz", @@ -6851,11 +7292,22 @@ "node": ">=6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -6866,8 +7318,16 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "optional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, "node_modules/colorette": { "version": "1.4.0", @@ -13568,14 +14028,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "optional": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -13583,26 +14038,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true, - "peer": true - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -13754,6 +14189,53 @@ "node": ">=8" } }, + "node_modules/sharp": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", + "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.0" + }, + "engines": { + "libvips": ">=8.15.2", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.4", + "@img/sharp-darwin-x64": "0.33.4", + "@img/sharp-libvips-darwin-arm64": "1.0.2", + "@img/sharp-libvips-darwin-x64": "1.0.2", + "@img/sharp-libvips-linux-arm": "1.0.2", + "@img/sharp-libvips-linux-arm64": "1.0.2", + "@img/sharp-libvips-linux-s390x": "1.0.2", + "@img/sharp-libvips-linux-x64": "1.0.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", + "@img/sharp-libvips-linuxmusl-x64": "1.0.2", + "@img/sharp-linux-arm": "0.33.4", + "@img/sharp-linux-arm64": "0.33.4", + "@img/sharp-linux-s390x": "0.33.4", + "@img/sharp-linux-x64": "0.33.4", + "@img/sharp-linuxmusl-arm64": "0.33.4", + "@img/sharp-linuxmusl-x64": "0.33.4", + "@img/sharp-wasm32": "0.33.4", + "@img/sharp-win32-ia32": "0.33.4", + "@img/sharp-win32-x64": "0.33.4" + } + }, + "node_modules/sharp/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -13834,6 +14316,19 @@ "node": ">= 5.10.0" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -17147,6 +17642,15 @@ "to-fast-properties": "^2.0.0" } }, + "@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, "@expo/bunyan": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.0.tgz", @@ -17896,6 +18400,147 @@ "@hapi/hoek": "^9.0.0" } }, + "@img/sharp-darwin-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz", + "integrity": "sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.0.2" + } + }, + "@img/sharp-darwin-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz", + "integrity": "sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-x64": "1.0.2" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", + "optional": true + }, + "@img/sharp-libvips-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", + "optional": true + }, + "@img/sharp-libvips-linux-arm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", + "optional": true + }, + "@img/sharp-libvips-linux-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", + "optional": true + }, + "@img/sharp-libvips-linux-s390x": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", + "optional": true + }, + "@img/sharp-libvips-linux-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", + "optional": true + }, + "@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", + "optional": true + }, + "@img/sharp-linux-arm": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz", + "integrity": "sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm": "1.0.2" + } + }, + "@img/sharp-linux-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz", + "integrity": "sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-arm64": "1.0.2" + } + }, + "@img/sharp-linux-s390x": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz", + "integrity": "sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-s390x": "1.0.2" + } + }, + "@img/sharp-linux-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz", + "integrity": "sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linux-x64": "1.0.2" + } + }, + "@img/sharp-linuxmusl-arm64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz", + "integrity": "sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" + } + }, + "@img/sharp-linuxmusl-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz", + "integrity": "sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==", + "optional": true, + "requires": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.2" + } + }, + "@img/sharp-wasm32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz", + "integrity": "sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==", + "optional": true, + "requires": { + "@emnapi/runtime": "^1.1.1" + } + }, + "@img/sharp-win32-ia32": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz", + "integrity": "sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==", + "optional": true + }, + "@img/sharp-win32-x64": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz", + "integrity": "sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==", + "optional": true + }, "@jest/create-cache-key-function": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.6.3.tgz", @@ -20515,11 +21160,19 @@ "shallow-clone": "^3.0.0" } }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, "requires": { "color-name": "~1.1.4" } @@ -20527,8 +21180,16 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "optional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, "colorette": { "version": "1.4.0", @@ -25752,33 +26413,9 @@ "integrity": "sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw==" }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "optional": true, - "peer": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, - "peer": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true, - "peer": true - } - } + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==" }, "send": { "version": "0.18.0", @@ -25907,6 +26544,42 @@ "kind-of": "^6.0.2" } }, + "sharp": { + "version": "0.33.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", + "integrity": "sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==", + "requires": { + "@img/sharp-darwin-arm64": "0.33.4", + "@img/sharp-darwin-x64": "0.33.4", + "@img/sharp-libvips-darwin-arm64": "1.0.2", + "@img/sharp-libvips-darwin-x64": "1.0.2", + "@img/sharp-libvips-linux-arm": "1.0.2", + "@img/sharp-libvips-linux-arm64": "1.0.2", + "@img/sharp-libvips-linux-s390x": "1.0.2", + "@img/sharp-libvips-linux-x64": "1.0.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", + "@img/sharp-libvips-linuxmusl-x64": "1.0.2", + "@img/sharp-linux-arm": "0.33.4", + "@img/sharp-linux-arm64": "0.33.4", + "@img/sharp-linux-s390x": "0.33.4", + "@img/sharp-linux-x64": "0.33.4", + "@img/sharp-linuxmusl-arm64": "0.33.4", + "@img/sharp-linuxmusl-x64": "0.33.4", + "@img/sharp-wasm32": "0.33.4", + "@img/sharp-win32-ia32": "0.33.4", + "@img/sharp-win32-x64": "0.33.4", + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.0" + }, + "dependencies": { + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" + } + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -25974,6 +26647,21 @@ } } }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/package.json b/package.json index 54be1e4..54afa7c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "node-localstorage": "^2.2.1", "olm": "https://gitlab.matrix.org/matrix-org/olm/-/package_files/2572/download", "request": "^2.88.2", + "sharp": "^0.33.4", "tmp": "^0.2.1", "utf8": "^3.0.0" }, diff --git a/src/matrix-receive.js b/src/matrix-receive.js index cf3da36..a6dd836 100644 --- a/src/matrix-receive.js +++ b/src/matrix-receive.js @@ -18,7 +18,7 @@ module.exports = function(RED) { this.acceptVideos = n.acceptVideos; this.acceptLocations = n.acceptLocations; this.roomId = n.roomId; - this.roomIds = this.roomId ? this.roomId.split(',') : []; + this.roomIds = this.roomId ? this.roomId.split(',').map(s => s.trim()) : []; node.status({ fill: "red", shape: "ring", text: "disconnected" }); @@ -38,120 +38,94 @@ module.exports = function(RED) { node.server.on("Room.timeline", async function(event, room, toStartOfTimeline, removed, data, msg) { // if node has a room ID set we only listen on that room - if(node.roomIds.length && node.roomIds.indexOf(room.roomId) === -1) { + if (node.roomIds.length && !node.roomIds.includes(room.roomId)) { return; } - if (!node.acceptOwnEvents && ( !event.getSender() || event.getSender().toLowerCase() === node.server.matrixClient.getUserId().toLowerCase()) ) { - node.log("Ignoring" + (msg.encrypted ? ' encrypted' : '') +" timeline event [" + msg.type + "]: (" + room.name + ") " + event.getId() + " for reason: own event"); + if (!node.acceptOwnEvents && (!event.getSender() || event.getSender().toLowerCase() === node.server.matrixClient.getUserId().toLowerCase())) { + node.log(`Ignoring${msg.encrypted ? ' encrypted' : ''} timeline event [${msg.type}]: (${room.name}) ${event.getId()} for reason: own event`); return; } - switch(msg.type) { + const setUrls = (urlKey, encryptedKey) => { + const url = msg.encrypted ? msg.content[encryptedKey]?.url : msg.content[urlKey]; + if (url) { + msg.url = node.server.matrixClient.mxcUrlToHttp(url); + msg.mxc_url = url; + } + }; + + const setThumbnailUrls = (infoKey) => { + const thumbnailFile = msg.content.info?.[infoKey]; + const thumbnailUrl = thumbnailFile?.url; + if (thumbnailUrl) { + msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(thumbnailUrl); + msg.thumbnail_mxc_url = thumbnailUrl; + } + }; + + switch (msg.type) { case 'm.emote': - if(!node.acceptEmotes) return; + if (!node.acceptEmotes) return; break; case 'm.text': - if(!node.acceptText) return; + if (!node.acceptText) return; break; case 'm.sticker': - if(!node.acceptStickers) return; - if(msg.content.info) { - if(msg.content.info.thumbnail_url) { - msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_url); - msg.thumbnail_mxc_url = msg.content.info.thumbnail_url; - } - - if(msg.content.url) { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url); - msg.mxc_url = msg.content.url; - } - } + if (!node.acceptStickers) return; + setThumbnailUrls('thumbnail_url'); + setUrls('url', 'url'); break; case 'm.file': - if(!node.acceptFiles) return; + if (!node.acceptFiles) return; msg.filename = msg.content.filename || msg.content.body; - if(msg.encrypted) { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url); - msg.mxc_url = msg.content.file.url; - } else { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url); - msg.mxc_url = msg.content.url; - } + setUrls('url', 'file'); break; case 'm.audio': - if(!node.acceptAudio) return; - if(msg.encrypted) { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url); - msg.mxc_url = msg.content.file.url; - } else { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url); - msg.mxc_url = msg.content.url; - } - - if('org.matrix.msc1767.file' in msg.content) { + if (!node.acceptAudio) return; + setUrls('url', 'file'); + if ('org.matrix.msc1767.file' in msg.content) { msg.filename = msg.content['org.matrix.msc1767.file'].name; msg.mimetype = msg.content['org.matrix.msc1767.file'].mimetype; } - - if('org.matrix.msc1767.audio' in msg.content) { + if ('org.matrix.msc1767.audio' in msg.content) { msg.duration = msg.content['org.matrix.msc1767.audio'].duration; msg.waveform = msg.content['org.matrix.msc1767.audio'].waveform; } break; case 'm.image': - if(!node.acceptImages) return; + if (!node.acceptImages) return; msg.filename = msg.content.filename || msg.content.body; - if(msg.encrypted) { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url); - msg.mxc_url = msg.content.file.url; - msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_file.url); - msg.thumbnail_mxc_url = msg.content.info.thumbnail_file.url; - } else { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url); - msg.mxc_url = msg.content.url; - msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_url); - msg.thumbnail_mxc_url = msg.content.info.thumbnail_url; - } + setUrls('url', 'file'); + setThumbnailUrls('thumbnail_file'); break; - case 'm.video': - if(!node.acceptVideos) return; + if (!node.acceptVideos) return; msg.filename = msg.content.filename || msg.content.body; - if(msg.encrypted) { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.file.url); - msg.mxc_url = msg.content.file.url; - msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_file.url); - msg.thumbnail_mxc_url = msg.content.info.thumbnail_file.url; - } else { - msg.url = node.server.matrixClient.mxcUrlToHttp(msg.content.url); - msg.mxc_url = msg.content.url; - msg.thumbnail_url = node.server.matrixClient.mxcUrlToHttp(msg.content.info.thumbnail_url); - msg.thumbnail_mxc_url = msg.content.info.thumbnail_url; - } + setUrls('url', 'file'); + setThumbnailUrls('thumbnail_file'); break; case 'm.location': - if(!node.acceptLocations) return; + if (!node.acceptLocations) return; msg.geo_uri = msg.content.geo_uri; msg.payload = msg.content.body; break; case 'm.reaction': - if(!node.acceptReactions) return; + if (!node.acceptReactions) return; msg.info = msg.content["m.relates_to"].info; msg.referenceEventId = msg.content["m.relates_to"].event_id; msg.payload = msg.content["m.relates_to"].key; break; default: - // node.warn("Unknown event type: " + msg.type); return; } diff --git a/src/matrix-send-message.js b/src/matrix-send-message.js index d396478..915b173 100644 --- a/src/matrix-send-message.js +++ b/src/matrix-send-message.js @@ -191,7 +191,7 @@ module.exports = function(RED) { node.send([msg, null]); }) .catch(function(e){ - node.warn("Error sending message " + e); + node.error("Error sending message: " + e, {}); msg.error = e; node.send([null, msg]); }); diff --git a/src/matrix-upload-file.js b/src/matrix-upload-file.js index a203615..80495d4 100644 --- a/src/matrix-upload-file.js +++ b/src/matrix-upload-file.js @@ -1,5 +1,6 @@ const crypto = require("isomorphic-webcrypto"); const ffmpeg = require('fluent-ffmpeg'); +const sharp = require('sharp'); const getImageSize = require('image-size'); const tmp = require('tmp'); const fs = require('fs'); @@ -35,274 +36,336 @@ module.exports = function(RED) { node.status({ fill: "green", shape: "ring", text: "connected" }); }); - async function detectFileType(filename, bufferOrPath) - { + async function detectFileType(filename, bufferOrPath, msg) { const Mime = require('mime'); let file = Buffer.isBuffer(bufferOrPath) ? filename : bufferOrPath; - if(file) - { - let type = Mime.getType(file); - let ext = Mime.getExtension(file); - if(type) { - return {ext: ext, mime: type} + try { + if(file) { + let type = Mime.getType(file); + let ext = Mime.getExtension(file); + if(type) { + return {ext: ext, mime: type}; + } } + } catch (error) { + node.error(`Error detecting file type for ${filename}: ${error.message}`, msg); } + return null; } - function getFileBuffer(data) - { - if(Buffer.isBuffer(data)) { - return data; - } + function getFileBuffer(data, msg) { + try { + if (Buffer.isBuffer(data)) { + return data; + } - if (data && RED.settings.fileWorkingDirectory && !path.isAbsolute(data)) { - return fs.readFileSync(path.resolve(path.join(RED.settings.fileWorkingDirectory,data))); + if (data && RED.settings.fileWorkingDirectory && !path.isAbsolute(data)) { + return fs.readFileSync(path.resolve(path.join(RED.settings.fileWorkingDirectory, data))); + } + return fs.readFileSync(data); + } catch (error) { + node.error(`Error reading file buffer: ${error.message}`, msg); + throw error; } - return fs.readFileSync(data); } function getToValue(msg, type, property) { let value = property; - if (type === "msg") { - value = RED.util.getMessageProperty(msg, property); - } else if ((type === 'flow') || (type === 'global')) { - try { - value = RED.util.evaluateNodeProperty(property, type, node, msg); - } catch(e2) { - throw new Error("Invalid value evaluation"); + try { + if (type === "msg") { + value = RED.util.getMessageProperty(msg, property); + } else if ((type === 'flow') || (type === 'global')) { + try { + value = RED.util.evaluateNodeProperty(property, type, node, msg); + } catch(e2) { + throw new Error("Invalid value evaluation"); + } + } else if(type === "bool") { + value = (property === 'true'); + } else if(type === "num") { + value = Number(property); } - } else if(type === "bool") { - value = (property === 'true'); - } else if(type === "num") { - value = Number(property); + } catch (error) { + node.error(`Error evaluating value for type ${type}: ${error.message}`); + throw new Error("Invalid value evaluation"); } return value; } node.on("input", onInput); - async function onInput(msg) - { - if (! node.server || ! node.server.matrixClient) { - node.warn("No matrix server selected"); - return; - } + async function onInput(msg) { + try { + if (!node.server || !node.server.matrixClient) { + node.warn("No matrix server selected"); + return; + } - if(!node.server.isConnected()) { - node.error("Matrix server connection is currently closed", msg); - msg.error = "Matrix server connection is currently closed"; - node.send([null, msg]); - return; - } - - let bufferOrPath = getToValue(msg, node.inputType, node.inputValue); - if(!bufferOrPath) { - node.error('Missing file path/buffer input', msg); - msg.error = 'Missing file path/buffer input'; - node.send([null, msg]); - return; - } - - let filename = getToValue(msg, node.fileNameType, node.fileNameValue); - if(!filename || typeof filename !== 'string') { - if(!Buffer.isBuffer(bufferOrPath)) { - filename = path.basename(bufferOrPath); - } else { - node.error('Missing filename, this is required if input is a file buffer', msg); - msg.error = 'Missing filename, this is required if input is a file buffer'; + if (!node.server.isConnected()) { + node.error("Matrix server connection is currently closed", msg); + msg.error = "Matrix server connection is currently closed"; node.send([null, msg]); return; } - } - msg.contentType = node.contentType || msg.contentType || null; - let detectedFileType = await detectFileType(filename, bufferOrPath); - node.log("Detected file type " + JSON.stringify(detectedFileType) + " for " + (Buffer.isBuffer(bufferOrPath) ? 'buffer' : `file ${bufferOrPath}`), msg); - - let contentType = msg.contentType || detectedFileType?.mime || null, - msgtype = msg.msgtype || null; - if(!contentType) { - node.warn("Content-type failed to detect, falling back to text/plain", msg); - contentType = 'text/plain'; - } - if(!msgtype) { - msgtype = autoDetectMatrixMessageType(detectedFileType); - } - - let encryptedFile = null; - if(msg.encrypted) { - encryptedFile = await encryptAttachment(getFileBuffer(bufferOrPath)); - } - - node.log("Uploading file ", msg); - let file; - try { - file = await node.server.matrixClient.uploadContent( - encryptedFile?.data || getFileBuffer(bufferOrPath), - { - name: filename, // Name to give the file on the server. - rawResponse: false, // Return the raw body, rather than parsing the JSON. - type: contentType, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream. - onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true. - }); - } catch(e) { - node.error("Upload content error " + e); - msg.error = e; - node.send([null, msg]); - return; - } - - // we call this method when we need a file and cannot use the buffer - // so if we get passed a buffer we write it to a tmp file and return that - // otherwise we just return the string because it's already a file - let tempFile = null; - function getFile(bufferOrFile) { - if(!Buffer.isBuffer(bufferOrFile)) { - return bufferOrFile; // already a file + let bufferOrPath = getToValue(msg, node.inputType, node.inputValue); + if (!bufferOrPath) { + node.error('Missing file path/buffer input', msg); + msg.error = 'Missing file path/buffer input'; + node.send([null, msg]); + return; } - if(tempFile) { - return tempFile; + let filename = getToValue(msg, node.fileNameType, node.fileNameValue); + if (!filename || typeof filename !== 'string') { + if (!Buffer.isBuffer(bufferOrPath)) { + filename = path.basename(bufferOrPath); + } else { + node.error('Missing filename, this is required if input is a file buffer', msg); + msg.error = 'Missing filename, this is required if input is a file buffer'; + node.send([null, msg]); + return; + } } - // write buffer to tmp file and return path - let tmpObj = tmp.fileSync({ postfix: `.${detectedFileType.ext}` }); - fs.writeFileSync(tmpObj.name, bufferOrFile); - tempFile = tmpObj.name; - return tmpObj.name; - } + msg.contentType = node.contentType || msg.contentType || null; + let detectedFileType = await detectFileType(filename, bufferOrPath, msg); + node.log("Detected file type " + JSON.stringify(detectedFileType) + " for " + (Buffer.isBuffer(bufferOrPath) ? 'buffer' : `file ${bufferOrPath}`), msg); - function deleteTempFile() { - if(!tempFile) return null; - fs.rmSync(tempFile); - } - - // get size of a buffer or file in bytes - function getFileSize(bufferOrPath) { - if(Buffer.isBuffer(bufferOrPath)) { - return Buffer.byteLength(bufferOrPath); + let contentType = msg.contentType || detectedFileType?.mime || null, + msgtype = msg.msgtype || null; + if (!contentType) { + node.warn("Content-type failed to detect, falling back to text/plain", msg); + contentType = 'text/plain'; + } + if (!msgtype) { + msgtype = autoDetectMatrixMessageType(detectedFileType); } - return fs.statSync(bufferOrPath).size; - } - - async function addThumbnail(buffer) { - let imageSize = getImageSize(Buffer.isBuffer(buffer) ? buffer : buffer.data); - msg.payload.info.thumbnail_info = { - w: imageSize.width, - h: imageSize.height, - size: getFileSize(Buffer.isBuffer(buffer) ? buffer : buffer.data) + let encryptedFile = null; + if (msg.encrypted) { + encryptedFile = await encryptAttachment(getFileBuffer(bufferOrPath, msg)); } - let uploadedThumbnail = await node.server.matrixClient.uploadContent( - Buffer.isBuffer(buffer) ? buffer : buffer.data, - { - name: "thumbnail.png", // Name to give the file on the server. - rawResponse: false, // Return the raw body, rather than parsing the JSON. - type: "image/png", // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream. - onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true. - }); - // delete local file - if(msg.encrypted) { - msg.payload.info.thumbnail_file.url = uploadedThumbnail.content_uri; - } else { - msg.payload.info.thumbnail_url = uploadedThumbnail.content_uri; - } - } - function _ffmpegVideoThumbnail(filepath){ - return new Promise((resolve,reject) => { - let filename = `${msg._msgid}-screenshot.png`; - ffmpeg(filepath) - .on('end', async function() { - let path = `/tmp/${filename}`; - let buffer = getFileBuffer(path); - let encryptedThumbnail = null; - if(msg.encrypted) { - encryptedThumbnail = await encryptAttachment(buffer); - msg.payload.info.thumbnail_file = encryptedFile.info; - } - try { - await addThumbnail(encryptedThumbnail || buffer); - fs.rmSync(path); // delete temporary thumbnail file - resolve(); - } catch(e) { - return reject(new Error("Thumbnail upload failure: " + e)); - } - }) - .on('error', function(err) { - return reject(err); - }) - .screenshots({ - timestamps: [0], - filename: filename, - folder: '/tmp', - size: '320x?' + node.log("Uploading file ", msg); + let file; + try { + file = await node.server.matrixClient.uploadContent( + encryptedFile?.data || getFileBuffer(bufferOrPath, msg), + { + name: filename, // Name to give the file on the server. + rawResponse: false, // Return the raw body, rather than parsing the JSON. + type: contentType, // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream. + onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true. }); - }); - } - - msg.payload = {}; - if(msg.encrypted) { - msg.payload.file = encryptedFile?.info || {}; - msg.payload.file.url = file.content_uri; - } else { - msg.payload.url = file.content_uri; - } - msg.payload.msgtype = msgtype; - msg.payload.body = msg.body || msg.filename || ""; - msg.payload.info = { - "mimetype": contentType, - "size": getFileSize(bufferOrPath), - }; - if(msgtype === 'm.image') { - // detect size of image - try { - let imageSize = getImageSize(buffer); - msg.payload.info.h = imageSize.height; - msg.payload.info.w = imageSize.width; - } catch(e) { - node.error("Failed to get image size: " + e, msg); - } - } else if(msgtype === 'm.audio' && detectedFileType) { - try { - // detect duration of audio clip - let filepath = getFile(bufferOrPath); - let metadata = await _ffprobe(filepath); - let audioStream = metadata?.streams.filter(function(stream){return stream.codec_type === "audio" || false;})[0]; - if(audioStream?.duration) { - msg.payload.info.duration = audioStream?.duration * 1000; - } - } catch(e) { - node.error(e, msg); - } - deleteTempFile(); - } else if(msgtype === 'm.video' && detectedFileType) { - let filepath = getFile(bufferOrPath); - - try { - // detect duration & width/height of video clip - let metadata = await _ffprobe(filepath); - let videoStream = metadata?.streams.filter(function(stream){return stream.codec_type === "video" || false;})[0]; - if(videoStream) { - msg.payload.info.duration = videoStream.duration * 1000; - msg.payload.info.w = videoStream.width; - msg.payload.info.h = videoStream.height; - } - } catch(e) { - node.error("ffprobe error: " + e); + } catch (e) { + node.error("Upload content error " + e, msg); + msg.error = e; + node.send([null, msg]); + return; } - if(node.generateThumbnails) { + // we call this method when we need a file and cannot use the buffer + // so if we get passed a buffer we write it to a tmp file and return that + // otherwise we just return the string because it's already a file + let tempFile = null; + + function getFile(bufferOrFile) { try { - await _ffmpegVideoThumbnail(filepath); - } catch(e) { - node.error("Screenshot generation error: " + e); + if (!Buffer.isBuffer(bufferOrFile)) { + return bufferOrFile; // already a file + } + + if (tempFile) { + return tempFile; + } + + // write buffer to tmp file and return path + let tmpObj = tmp.fileSync({postfix: `.${detectedFileType.ext}`}); + fs.writeFileSync(tmpObj.name, bufferOrFile); + tempFile = tmpObj.name; + return tmpObj.name; + } catch (error) { + node.error(`Error creating temp file: ${error.message}`, msg); + throw error; } } - deleteTempFile(); - } - node.send(msg, null); + function deleteTempFile() { + if (!tempFile) return null; + try { + fs.rmSync(tempFile); + } catch (error) { + node.error(`Error deleting temp file: ${error.message}`, msg); + } + } + + // get size of a buffer or file in bytes + function getFileSize(bufferOrPath) { + try { + if (Buffer.isBuffer(bufferOrPath)) { + return Buffer.byteLength(bufferOrPath); + } + + return fs.statSync(bufferOrPath).size; + } catch (error) { + node.error(`Error getting file size: ${error.message}`, msg); + throw error; + } + } + + async function addThumbnail(buffer) { + try { + let imageSize = getImageSize(Buffer.isBuffer(buffer) ? buffer : buffer.data); + msg.payload.info.thumbnail_info = { + w: imageSize.width, + h: imageSize.height, + size: getFileSize(Buffer.isBuffer(buffer) ? buffer : buffer.data) + } + let uploadedThumbnail = await node.server.matrixClient.uploadContent( + Buffer.isBuffer(buffer) ? buffer : buffer.data, + { + name: "thumbnail.png", // Name to give the file on the server. + rawResponse: false, // Return the raw body, rather than parsing the JSON. + type: "image/png", // Content-type for the upload. Defaults to file.type, or applicaton/octet-stream. + onlyContentUri: false // Just return the content URI, rather than the whole body. Defaults to false. Ignored if opts.rawResponse is true. + }); + // delete local file + if (msg.encrypted) { + msg.payload.info.thumbnail_file.url = uploadedThumbnail.content_uri; + } else { + msg.payload.info.thumbnail_url = uploadedThumbnail.content_uri; + } + } catch (error) { + node.error(`Error adding thumbnail: ${error.message}`, msg); + } + } + + async function createImageThumbnail(bufferOrPath) { + try { + if (Buffer.isBuffer(bufferOrPath)) { + return sharp(bufferOrPath).resize({width: 320}).toBuffer(); + } + return sharp(fs.readFileSync(bufferOrPath)).resize({width: 320}).toBuffer(); + } catch (error) { + node.error(`Error creating image thumbnail: ${error.message}`); + throw error; + } + } + + function _ffmpegVideoThumbnail(filepath) { + return new Promise((resolve, reject) => { + let filename = `${msg._msgid}-screenshot.png`; + ffmpeg(filepath) + .on('end', async function () { + let path = `/tmp/${filename}`; + let buffer = getFileBuffer(path, msg); + let encryptedThumbnail = null; + if (msg.encrypted) { + encryptedThumbnail = await encryptAttachment(buffer); + msg.payload.info.thumbnail_file = encryptedFile.info; + } + try { + await addThumbnail(encryptedThumbnail || buffer); + fs.rmSync(path); // delete temporary thumbnail file + resolve(); + } catch (e) { + return reject(new Error("Thumbnail upload failure: " + e)); + } + }) + .on('error', function (err) { + return reject(err); + }) + .screenshots({ + timestamps: [0], + filename: filename, + folder: '/tmp', + size: '320x?' + }); + }); + } + + msg.payload = {}; + if (msg.encrypted) { + msg.payload.file = encryptedFile?.info || {}; + msg.payload.file.url = file.content_uri; + } else { + msg.payload.url = file.content_uri; + } + msg.payload.msgtype = msgtype; + msg.payload.body = msg.body || msg.filename || ""; + msg.payload.info = { + "mimetype": contentType, + "size": getFileSize(bufferOrPath), + }; + if (msgtype === 'm.image') { + // detect size of image + try { + let imageSize = getImageSize(bufferOrPath); + msg.payload.info.h = imageSize.height; + msg.payload.info.w = imageSize.width; + + // Generate thumbnail for image + if (node.generateThumbnails) { + let thumbnailBuffer = await createImageThumbnail(bufferOrPath); + let encryptedThumbnail = null; + if (msg.encrypted) { + encryptedThumbnail = await encryptAttachment(thumbnailBuffer); + msg.payload.info.thumbnail_file = encryptedThumbnail.info; + } + await addThumbnail(encryptedThumbnail || thumbnailBuffer); + } + } catch (e) { + node.error("thumbnail error: " + e, msg); + } + } else if (msgtype === 'm.audio' && detectedFileType) { + try { + // detect duration of audio clip + let filepath = getFile(bufferOrPath); + let metadata = await _ffprobe(filepath); + let audioStream = metadata?.streams.filter(function (stream) { + return stream.codec_type === "audio" || false; + })[0]; + if (audioStream?.duration) { + msg.payload.info.duration = audioStream?.duration * 1000; + } + } catch (e) { + node.error(e, msg); + } + deleteTempFile(); + } else if (msgtype === 'm.video' && detectedFileType) { + let filepath = getFile(bufferOrPath); + + try { + // detect duration & width/height of video clip + let metadata = await _ffprobe(filepath); + let videoStream = metadata?.streams.filter(function (stream) { + return stream.codec_type === "video" || false; + })[0]; + if (videoStream) { + msg.payload.info.duration = videoStream.duration * 1000; + msg.payload.info.w = videoStream.width; + msg.payload.info.h = videoStream.height; + } + } catch (e) { + node.error("ffprobe error: " + e, msg); + } + + if (node.generateThumbnails) { + try { + await _ffmpegVideoThumbnail(filepath); + } catch (e) { + node.error("Screenshot generation error: " + e, msg); + } + } + deleteTempFile(); + } + + node.send(msg, null); + } catch (error) { + node.error(`Unhandled error: ${error.message}`, msg); + node.log(error, msg); + } } node.on("close", function() {