diff --git a/.env.example b/.env.example index c447e32..52181c2 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,2 @@ REGISTRATION_SHARED_SECRET='look in your synapses homeserver.yaml' -EXCLUDED_USERS='rocket.cat' # Comma-separated list +EXCLUDED_USERS='rocket.cat' # Comma-separated list of usernames or IDs diff --git a/package-lock.json b/package-lock.json index 36070a2..8e2a6cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,20 +10,20 @@ "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^1.4.0", - "dotenv": "^16.1.4", + "dotenv": "^16.3.1", "n-readlines": "^1.0.1", "reflect-metadata": "^0.1.13", "sqlite3": "^5.1.6", - "typeorm": "^0.3.16", + "typeorm": "^0.3.17", "winston": "^3.9.0" }, "devDependencies": { "@jest/globals": "^29.5.0", "@types/n-readlines": "^1.0.3", "@types/node": "^20.3.1", - "@typescript-eslint/eslint-plugin": "^5.59.11", - "@typescript-eslint/parser": "^5.59.11", - "eslint": "^8.42.0", + "@typescript-eslint/eslint-plugin": "^5.60.0", + "@typescript-eslint/parser": "^5.60.0", + "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-config-standard-with-typescript": "^34.0.1", "eslint-plugin-import": "^2.27.5", @@ -714,9 +714,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1301,9 +1301,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.1.0.tgz", - "integrity": "sha512-w1qd368vtrwttm1PRJWPW1QHlbmHrVDGs1eBH/jZvRPUFS4MNXV9Q33EQdjOdeAxZ7O8+3wM7zxztm2nfUSyKw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0" @@ -1466,15 +1466,15 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", - "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", + "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/type-utils": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/type-utils": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -1500,14 +1500,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", - "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", + "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "debug": "^4.3.4" }, "engines": { @@ -1527,13 +1527,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", - "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", + "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11" + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1544,13 +1544,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", - "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", + "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1571,9 +1571,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", - "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", + "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1584,13 +1584,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", - "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", + "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1611,17 +1611,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", - "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", + "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -1637,12 +1637,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", - "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", + "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/types": "5.60.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1659,9 +1659,9 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2089,9 +2089,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.8.tgz", - "integrity": "sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -2108,8 +2108,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001502", - "electron-to-chromium": "^1.4.428", + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", "node-releases": "^2.0.12", "update-browserslist-db": "^1.0.11" }, @@ -2259,9 +2259,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001503", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz", - "integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true, "funding": [ { @@ -2772,9 +2772,9 @@ } }, "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "engines": { "node": ">=12" }, @@ -2789,9 +2789,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.430", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.430.tgz", - "integrity": "sha512-FytjTbGwz///F+ToZ5XSeXbbSaXalsVRXsz2mHityI5gfxft7ieW3HqFLkU5V1aIrY42aflICqbmFoDxW10etg==", + "version": "1.4.440", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", + "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", "dev": true }, "node_modules/emittery": { @@ -2961,15 +2961,15 @@ } }, "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", + "@eslint/js": "8.43.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -6462,9 +6462,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -6942,9 +6942,9 @@ "optional": true }, "node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -7665,9 +7665,9 @@ } }, "node_modules/typeorm": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.16.tgz", - "integrity": "sha512-wJ4Qy1oqRKNDdZiBTTaVMqwo/XxC52Q7uNPTjltPgLhvIW173bL6Iad0lhptMOsFlpixFPaUu3PNziaRBwX2Zw==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.17.tgz", + "integrity": "sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==", "dependencies": { "@sqltools/formatter": "^1.2.5", "app-root-path": "^3.1.0", @@ -8749,9 +8749,9 @@ } }, "@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "dev": true }, "@gar/promisify": { @@ -9217,9 +9217,9 @@ } }, "@sinonjs/fake-timers": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.1.0.tgz", - "integrity": "sha512-w1qd368vtrwttm1PRJWPW1QHlbmHrVDGs1eBH/jZvRPUFS4MNXV9Q33EQdjOdeAxZ7O8+3wM7zxztm2nfUSyKw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.0" @@ -9379,15 +9379,15 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", - "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", + "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/type-utils": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/type-utils": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -9397,53 +9397,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", - "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", + "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", - "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", + "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11" + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0" } }, "@typescript-eslint/type-utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", - "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", + "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", - "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", + "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", - "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", + "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -9452,28 +9452,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", - "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", + "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", - "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", + "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/types": "5.60.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -9483,9 +9483,9 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true }, "acorn-jsx": { @@ -9793,13 +9793,13 @@ } }, "browserslist": { - "version": "4.21.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.8.tgz", - "integrity": "sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001502", - "electron-to-chromium": "^1.4.428", + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", "node-releases": "^2.0.12", "update-browserslist-db": "^1.0.11" } @@ -9913,9 +9913,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001503", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz", - "integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true }, "chalk": { @@ -10293,9 +10293,9 @@ } }, "dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==" + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" }, "eastasianwidth": { "version": "0.2.0", @@ -10304,9 +10304,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.430", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.430.tgz", - "integrity": "sha512-FytjTbGwz///F+ToZ5XSeXbbSaXalsVRXsz2mHityI5gfxft7ieW3HqFLkU5V1aIrY42aflICqbmFoDxW10etg==", + "version": "1.4.440", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", + "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", "dev": true }, "emittery": { @@ -10443,15 +10443,15 @@ "dev": true }, "eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", + "@eslint/js": "8.43.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -13009,9 +13009,9 @@ "dev": true }, "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true }, "pkg-dir": { @@ -13331,9 +13331,9 @@ "optional": true }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "requires": { "lru-cache": "^6.0.0" }, @@ -13865,9 +13865,9 @@ } }, "typeorm": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.16.tgz", - "integrity": "sha512-wJ4Qy1oqRKNDdZiBTTaVMqwo/XxC52Q7uNPTjltPgLhvIW173bL6Iad0lhptMOsFlpixFPaUu3PNziaRBwX2Zw==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.17.tgz", + "integrity": "sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==", "requires": { "@sqltools/formatter": "^1.2.5", "app-root-path": "^3.1.0", diff --git a/package.json b/package.json index 4815f8d..de6177c 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,9 @@ "@jest/globals": "^29.5.0", "@types/n-readlines": "^1.0.3", "@types/node": "^20.3.1", - "@typescript-eslint/eslint-plugin": "^5.59.11", - "@typescript-eslint/parser": "^5.59.11", - "eslint": "^8.42.0", + "@typescript-eslint/eslint-plugin": "^5.60.0", + "@typescript-eslint/parser": "^5.60.0", + "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-config-standard-with-typescript": "^34.0.1", "eslint-plugin-import": "^2.27.5", @@ -48,11 +48,11 @@ }, "dependencies": { "axios": "^1.4.0", - "dotenv": "^16.1.4", + "dotenv": "^16.3.1", "n-readlines": "^1.0.1", "reflect-metadata": "^0.1.13", "sqlite3": "^5.1.6", - "typeorm": "^0.3.16", + "typeorm": "^0.3.17", "winston": "^3.9.0" } } diff --git a/src/Entities.ts b/src/Entities.ts new file mode 100644 index 0000000..331b44e --- /dev/null +++ b/src/Entities.ts @@ -0,0 +1,27 @@ +export const enum Entity { + Users = 'users', + Rooms = 'rooms', + Messages = 'messages', +} + +type EntityConfig = { + filename: string + mappingType: number +} + +export const entities: { + [key in Entity]: EntityConfig +} = { + users: { + filename: 'users.json', + mappingType: 0, + }, + rooms: { + filename: 'rocketchat_room.json', + mappingType: 1, + }, + messages: { + filename: 'rocketchat_message.json', + mappingType: 2, + }, +} as const diff --git a/src/app.ts b/src/app.ts index f50a1c3..2661191 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,114 +2,31 @@ import dotenv from 'dotenv' dotenv.config() import lineByLine from 'n-readlines' import 'reflect-metadata' -import { IdMapping } from './entity/IdMapping' -import { RcUser, createUser } from './handlers/users' +import { handle as handleRoom } from './handlers/rooms' +import { handle as handleUser } from './handlers/users' import log from './helpers/logger' -import { - createMembership, - getMapping, - initStorage, - save, -} from './helpers/storage' +import { initStorage } from './helpers/storage' import { whoami } from './helpers/synapse' -import { RcRoom, createRoom } from './handlers/rooms' +import { Entity, entities } from './Entities' log.info('rocketchat2matrix starts.') -const enum Entities { - Users = 'users', - Rooms = 'rooms', - Messages = 'messages', -} - -type EntityConfig = { - filename: string - mappingType: number -} - -const entities: { [key in Entities]: EntityConfig } = { - users: { - filename: 'users.json', - mappingType: 0, - }, - rooms: { - filename: 'rocketchat_room.json', - mappingType: 1, - }, - messages: { - filename: 'rocketchat_message.json', - mappingType: 2, - }, -} - -async function loadRcExport(entity: Entities) { +async function loadRcExport(entity: Entity) { const rl = new lineByLine(`./inputs/${entities[entity].filename}`) let line: false | Buffer while ((line = rl.next())) { const item = JSON.parse(line.toString()) switch (entity) { - case Entities.Users: - const rcUser: RcUser = item - log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`) - - // Check for exclusion - if ( - rcUser.roles.some((e) => ['app', 'bot'].includes(e)) || - (process.env.EXCLUDED_USERS || '').split(',').includes(rcUser._id) - ) { - log.debug('User excluded. Skipping.') - break - } - - let mapping = await getMapping(rcUser._id, entities[entity].mappingType) - if (mapping && mapping.matrixId) { - log.debug('Mapping exists:', mapping) - } else { - const matrixUser = await createUser(rcUser) - mapping = new IdMapping() - mapping.rcId = rcUser._id - mapping.matrixId = matrixUser.user_id - mapping.type = entities[entity].mappingType - mapping.accessToken = matrixUser.access_token - - await save(mapping) - log.debug('Mapping added:', mapping) - - // Add user to room mapping (specific to users) - await Promise.all( - rcUser.__rooms.map(async (rcRoomId: string) => { - await createMembership(rcRoomId, rcUser._id) - log.debug(`${rcUser.username} membership for ${rcRoomId} created`) - }) - ) - } - + case Entity.Users: + await handleUser(item) break - case Entities.Rooms: - const rcRoom: RcRoom = item - log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`) - - let roomMapping = await getMapping( - rcRoom._id, - entities[entity].mappingType - ) - if (roomMapping && roomMapping.matrixId) { - log.debug('Mapping exists:', roomMapping) - } else { - const matrixRoom = await createRoom(rcRoom) - roomMapping = new IdMapping() - roomMapping.rcId = rcRoom._id - roomMapping.matrixId = matrixRoom.room_id - roomMapping.type = entities[entity].mappingType - - await save(roomMapping) - log.debug('Mapping added:', roomMapping) - } + case Entity.Rooms: + await handleRoom(item) break - case Entities.Messages: + case Entity.Messages: log.debug(`Message: ${item.name}`) break @@ -124,9 +41,9 @@ async function main() { await whoami() await initStorage() log.info('Parsing users') - await loadRcExport(Entities.Users) + await loadRcExport(Entity.Users) log.info('Parsing rooms') - await loadRcExport(Entities.Rooms) + await loadRcExport(Entity.Rooms) log.info('Done.') } catch (error) { log.error(`Encountered an error while booting up: ${error}`, error) diff --git a/src/handlers/rooms.test.ts b/src/handlers/rooms.test.ts index bf3e80a..d2f3fc6 100644 --- a/src/handlers/rooms.test.ts +++ b/src/handlers/rooms.test.ts @@ -6,15 +6,18 @@ import { SessionOptions } from '../helpers/synapse' import { MatrixRoomPresets, MatrixRoomVisibility, + RcRoom, RcRoomTypes, acceptInvitation, + createMapping, getCreator, getFilteredMembers, inviteMember, mapRoom, - parseMemberships, + createDirectChatMemberships, registerRoom, } from './rooms' +import { Entity, entities } from '../Entities' jest.mock('axios') const mockedAxios = axios as jest.Mocked @@ -110,10 +113,13 @@ test('getting creator', () => { expect(getCreator(rcDirectChat)).toBe('aliceid') expect(getCreator(rcPublicRoom)).toBe(roomCreator._id) expect(getCreator(rcPrivateRoom)).toBe(roomCreator._id) + expect(getCreator({} as RcRoom)).toBe('') }) test('creating memberships for direct chats', async () => { - await expect(parseMemberships(rcDirectChat)).resolves.toBe(undefined) + await expect(createDirectChatMemberships(rcDirectChat)).resolves.toBe( + undefined + ) expect(mockedStorage.createMembership).toHaveBeenCalledWith( rcDirectChat._id, rcDirectChat.uids[0] @@ -127,7 +133,11 @@ test('creating memberships for direct chats', async () => { mockedStorage.createMembership.mockClear() await expect( - parseMemberships({ ...rcDirectChat, _id: 'hoihoi', uids: ['hoi', 'hoi'] }) + createDirectChatMemberships({ + ...rcDirectChat, + _id: 'hoihoi', + uids: ['hoi', 'hoi'], + }) ).resolves.toBe(undefined) expect(mockedStorage.createMembership).toHaveBeenCalledWith('hoihoi', 'hoi') @@ -168,7 +178,7 @@ test('accepting invitation by joining the room', async () => { rcId: 'whatever', matrixId: 'Neo', accessToken: 'secretAuthToken', - type: 0, + type: entities[Entity.Users].mappingType, }, room_id ) @@ -193,7 +203,7 @@ test('filtering members', async () => { return { rcId, matrixId: `@${rcId}:matrix`, - type: type || 0, + type: type || entities[Entity.Users].mappingType, accessToken: 'accessToken', } } @@ -206,7 +216,28 @@ test('filtering members', async () => { mockMapping('existingUser'), mockMapping('otherExistingUser'), ]) - expect(mockedStorage.getMapping).toBeCalledWith('existingUser', 0) - expect(mockedStorage.getMapping).toBeCalledWith('otherExistingUser', 0) - expect(mockedStorage.getMapping).toBeCalledWith('excludedUser', 0) + expect(mockedStorage.getMapping).toBeCalledWith( + 'existingUser', + entities[Entity.Users].mappingType + ) + expect(mockedStorage.getMapping).toBeCalledWith( + 'otherExistingUser', + entities[Entity.Users].mappingType + ) + expect(mockedStorage.getMapping).toBeCalledWith( + 'excludedUser', + entities[Entity.Users].mappingType + ) +}) + +test('creating mapping', async () => { + await expect( + createMapping(rcPublicRoom._id, { ...mapRoom(rcPublicRoom), room_id }) + ).resolves.toBe(undefined) + expect(mockedStorage.save).toHaveBeenCalledWith({ + rcId: rcPublicRoom._id, + matrixId: room_id, + type: entities[Entity.Rooms].mappingType, + accessToken: undefined, + } as IdMapping) }) diff --git a/src/handlers/rooms.ts b/src/handlers/rooms.ts index 0ed2897..9568372 100644 --- a/src/handlers/rooms.ts +++ b/src/handlers/rooms.ts @@ -1,9 +1,12 @@ +import { Entity, entities } from '../Entities' import { IdMapping } from '../entity/IdMapping' import log from '../helpers/logger' import { createMembership, getMapping, getMemberships, + getRoomId, + save, } from '../helpers/storage' import { SessionOptions, @@ -101,7 +104,9 @@ export function getCreator(rcRoom: RcRoom): string { } } -export async function parseMemberships(rcRoom: RcRoom): Promise { +export async function createDirectChatMemberships( + rcRoom: RcRoom +): Promise { if (rcRoom.t == RcRoomTypes.direct && rcRoom.uids) { await Promise.all( [...new Set(rcRoom.uids)] // Deduplicate users @@ -182,19 +187,45 @@ export async function getFilteredMembers( return memberMappings } +export async function createMapping( + rcId: string, + matrixRoom: MatrixRoom +): Promise { + const roomMapping = new IdMapping() + roomMapping.rcId = rcId + roomMapping.matrixId = matrixRoom.room_id + roomMapping.type = entities[Entity.Rooms].mappingType + + await save(roomMapping) + log.debug('Mapping added:', roomMapping) +} + export async function createRoom(rcRoom: RcRoom): Promise { const room: MatrixRoom = mapRoom(rcRoom) const creatorId = getCreator(rcRoom) - await parseMemberships(rcRoom) + await createDirectChatMemberships(rcRoom) const creatorSessionOptions = await getCreatorSessionOptions(creatorId) log.debug('Creating room:', room) room.room_id = await registerRoom(room, creatorSessionOptions) - const rcMemberIds = await getMemberships(rcRoom._id) + await handleMemberships(rcRoom._id, room, creatorId, creatorSessionOptions) + + return room +} + +async function handleMemberships( + rcRoomId: string, + room: MatrixRoom, + creatorId: string, + creatorSessionOptions: object | SessionOptions +) { + const rcMemberIds = await getMemberships(rcRoomId) const memberMappings = await getFilteredMembers(rcMemberIds, creatorId) log.info( - `Inviting members to room ${rcRoom._id}:`, + `Inviting members to room ${ + room.room_alias_name || room.name || room.room_id + }:`, memberMappings.map((mapping) => mapping.matrixId) ) log.debug( @@ -214,6 +245,16 @@ export async function createRoom(rcRoom: RcRoom): Promise { await acceptInvitation(memberMapping, room.room_id || '') }) ) - - return room +} + +export async function handle(rcRoom: RcRoom): Promise { + log.info(`Parsing room ${rcRoom.name || 'with ID: ' + rcRoom._id}`) + + const matrixRoomId = await getRoomId(rcRoom._id) + if (matrixRoomId) { + log.debug(`Mapping exists: ${rcRoom._id} -> ${matrixRoomId}`) + } else { + const matrixRoom = await createRoom(rcRoom) + await createMapping(rcRoom._id, matrixRoom) + } } diff --git a/src/handlers/users.test.ts b/src/handlers/users.test.ts index 2882fa8..612aad2 100644 --- a/src/handlers/users.test.ts +++ b/src/handlers/users.test.ts @@ -1,23 +1,32 @@ process.env.REGISTRATION_SHARED_SECRET = 'ThisIsSoSecretWow' +process.env.EXCLUDED_USERS = 'excludedUser1,excludedUser2' import { expect, jest, test } from '@jest/globals' import axios from 'axios' +import * as storage from '../helpers/storage' import { MatrixUser, RcUser, + createMapping, createUser, generateHmac, mapUser, + userIsExcluded, } from '../handlers/users' +import { IdMapping } from '../entity/IdMapping' +import { Entity, entities } from '../Entities' jest.mock('axios') const mockedAxios = axios as jest.Mocked +jest.mock('../helpers/storage') +const mockedStorage = storage as jest.Mocked + const rcUser: RcUser = { _id: 'testRc', name: 'Tester McDelme', username: 'testuser', roles: ['user'], - __rooms: [], + __rooms: ['room0', 'room1'], } const matrixUser: MatrixUser = { @@ -29,15 +38,14 @@ const matrixUser: MatrixUser = { } const nonce = 'test-nonce' +const mac = 'be0537407ab3c82de908c5763185556e98a7211c' test('mapping users', () => { expect(mapUser(rcUser)).toStrictEqual(matrixUser) }) test('generating correct hmac', () => { - expect(generateHmac({ ...matrixUser, nonce })).toStrictEqual( - 'be0537407ab3c82de908c5763185556e98a7211c' - ) + expect(generateHmac({ ...matrixUser, nonce })).toStrictEqual(mac) }) test('creating users', async () => { @@ -57,12 +65,47 @@ test('creating users', async () => { }) expect(mockedAxios.get).toHaveBeenCalledWith('/_synapse/admin/v1/register') - expect(mockedAxios.post).toHaveBeenCalled() - // The following test fails with an incorrect return value, for whatever reason. - // Probably because of mutated call logs in jest due to the `delete` or sth. - // expect(mockedAxios.post).toHaveBeenCalledWith('/_synapse/admin/v1/register', { - // ...matrixUser, - // nonce, - // mac: 'be0537407ab3c82de908c5763185556e98a7211c', - // }) + expect(mockedAxios.post).toHaveBeenCalledWith('/_synapse/admin/v1/register', { + ...matrixUser, + nonce, + mac, + }) + + expect(mockedStorage.createMembership).toHaveBeenCalledWith( + rcUser.__rooms[0], + rcUser._id + ) + expect(mockedStorage.createMembership).toHaveBeenCalledWith( + rcUser.__rooms[1], + rcUser._id + ) + expect(mockedStorage.createMembership).toHaveBeenCalledTimes(2) +}) + +test('users are excluded', () => { + expect(userIsExcluded(rcUser)).toBeFalsy() + expect(userIsExcluded({ ...rcUser, _id: 'excludedUser1' })).toBeTruthy() + expect(userIsExcluded({ ...rcUser, username: 'excludedUser2' })).toBeTruthy() + expect(userIsExcluded({ ...rcUser, roles: ['bot'] })).toBeTruthy() + expect( + userIsExcluded({ ...rcUser, roles: [...rcUser.__rooms, 'app'] }) + ).toBeTruthy() + expect( + userIsExcluded({ + ...rcUser, + _id: 'excludedUser2', + username: 'excludedUser1', + roles: [...rcUser.__rooms, 'app', 'bot'], + }) + ).toBeTruthy() +}) + +test('creating mapping', async () => { + await expect(createMapping(rcUser._id, matrixUser)).resolves.toBe(undefined) + expect(mockedStorage.save).toHaveBeenCalledWith({ + rcId: rcUser._id, + matrixId: matrixUser.user_id, + type: entities[Entity.Users].mappingType, + accessToken: matrixUser.access_token, + } as IdMapping) }) diff --git a/src/handlers/users.ts b/src/handlers/users.ts index c6867ed..9c3ae1c 100644 --- a/src/handlers/users.ts +++ b/src/handlers/users.ts @@ -1,6 +1,9 @@ import { createHmac } from 'node:crypto' import log from '../helpers/logger' import { axios } from '../helpers/synapse' +import { createMembership, getUserId, save } from '../helpers/storage' +import { IdMapping } from '../entity/IdMapping' +import { Entity, entities } from '../Entities' export type RcUser = { _id: string @@ -64,17 +67,72 @@ async function registerUser(user: MatrixUser): Promise { return (await axios.post('/_synapse/admin/v1/register', user)).data } +async function parseUserMemberships(rcUser: RcUser): Promise { + await Promise.all( + rcUser.__rooms.map(async (rcRoomId: string) => { + await createMembership(rcRoomId, rcUser._id) + log.debug(`${rcUser.username} membership for ${rcRoomId} created`) + }) + ) +} + +export function userIsExcluded(rcUser: RcUser): boolean { + const reasons: string[] = [] + const excludedUsers = (process.env.EXCLUDED_USERS || '').split(',') + if (rcUser.roles.includes('app')) reasons.push('has role "app"') + if (rcUser.roles.includes('bot')) reasons.push('has role "bot"') + if (excludedUsers.includes(rcUser._id)) + reasons.push(`id "${rcUser._id}" is on exclusion list`) + if (excludedUsers.includes(rcUser.username)) + reasons.push(`username "${rcUser.username}" is on exclusion list`) + + if (reasons.length > 0) { + log.debug(`User ${rcUser.name} is excluded: ${reasons.join(', ')}`) + return true + } + return false +} + +export async function createMapping( + rcId: string, + matrixUser: MatrixUser +): Promise { + const mapping = new IdMapping() + mapping.rcId = rcId + mapping.matrixId = matrixUser.user_id + mapping.type = entities[Entity.Users].mappingType + mapping.accessToken = matrixUser.access_token + + await save(mapping) + log.debug('Mapping added:', mapping) +} + export async function createUser(rcUser: RcUser): Promise { const user = mapUser(rcUser) - user.nonce = await getUserRegistrationNonce() - user.mac = generateHmac(user) - const accessToken = await registerUser(user) + const nonce = await getUserRegistrationNonce() + const mac = generateHmac({ ...user, nonce }) + const accessToken = await registerUser({ ...user, nonce, mac }) user.user_id = accessToken.user_id user.access_token = accessToken.access_token log.info(`User ${rcUser.username} created:`, user) - delete user.nonce - delete user.mac + await parseUserMemberships(rcUser) return user } + +export async function handle(rcUser: RcUser): Promise { + log.info(`Parsing user: ${rcUser.name}: ${rcUser._id}`) + + if (userIsExcluded(rcUser)) { + return undefined + } + + const matrixId = await getUserId(rcUser._id) + if (matrixId) { + log.debug(`Mapping exists: ${rcUser._id} -> ${matrixId}`) + } else { + const matrixUser = await createUser(rcUser) + await createMapping(rcUser._id, matrixUser) + } +} diff --git a/src/helpers/storage.test.ts b/src/helpers/storage.test.ts index 81bfb8d..b4de1cf 100644 --- a/src/helpers/storage.test.ts +++ b/src/helpers/storage.test.ts @@ -5,16 +5,20 @@ import { getAccessToken, getMapping, getMemberships, + getMessageId, + getRoomId, + getUserId, initStorage, save, } from './storage' import { IdMapping } from '../entity/IdMapping' import { Membership } from '../entity/Membership' +import { Entity, entities } from '../Entities' const mapping = new IdMapping() mapping.rcId = 'rcId' mapping.matrixId = 'matrixId' -mapping.type = 0 +mapping.type = entities[Entity.Users].mappingType mapping.accessToken = 'accessToken' const membership = new Membership() @@ -25,7 +29,7 @@ beforeAll(async () => { await initStorage() }) -test('save mapping', async () => { +test('create mapping', async () => { await expect(save(mapping)).resolves.toBe(undefined) }) @@ -60,3 +64,30 @@ test('get membership', async () => { await expect(getMemberships('inexistent')).resolves.toStrictEqual([]) }) + +test('get member by id', async () => { + await expect(getUserId(mapping.rcId)).resolves.toBe(mapping.matrixId) + await expect(getUserId('inexistent')).resolves.toBeFalsy() +}) + +test('get room by id', async () => { + const room = new IdMapping() + room.rcId = 'rcRoom' + room.matrixId = 'matrixRoom' + room.type = entities[Entity.Rooms].mappingType + await save(room) + + await expect(getRoomId(room.rcId)).resolves.toBe(room.matrixId) + await expect(getRoomId('inexistent')).resolves.toBeFalsy() +}) + +test('get message by id', async () => { + const message = new IdMapping() + message.rcId = 'rcMessage' + message.matrixId = 'matrixMessage' + message.type = entities[Entity.Messages].mappingType + await save(message) + + await expect(getMessageId(message.rcId)).resolves.toBe(message.matrixId) + await expect(getMessageId('inexistent')).resolves.toBeFalsy() +}) diff --git a/src/helpers/storage.ts b/src/helpers/storage.ts index fcd9ff8..df99642 100644 --- a/src/helpers/storage.ts +++ b/src/helpers/storage.ts @@ -1,6 +1,7 @@ import { DataSource } from 'typeorm' import { IdMapping } from '../entity/IdMapping' import { Membership } from '../entity/Membership' +import { Entity, entities } from '../Entities' const AppDataSource = new DataSource({ type: 'sqlite', @@ -29,7 +30,7 @@ export async function save(entity: IdMapping | Membership): Promise { } export async function getAccessToken(id: string): Promise { - return (await getMapping(id, 0))?.accessToken + return (await getMapping(id, entities[Entity.Users].mappingType))?.accessToken } export async function createMembership( @@ -55,3 +56,16 @@ export async function getMemberships(rcRoomId: string): Promise { }) ).map((entity) => entity.rcUserId) } + +export async function getUserId(rcId: string): Promise { + return (await getMapping(rcId, entities[Entity.Users].mappingType))?.matrixId +} + +export async function getRoomId(rcId: string): Promise { + return (await getMapping(rcId, entities[Entity.Rooms].mappingType))?.matrixId +} + +export async function getMessageId(rcId: string): Promise { + return (await getMapping(rcId, entities[Entity.Messages].mappingType)) + ?.matrixId +}