forked from cadence/out-of-your-element
Emergency sync #11
37 changed files with 739 additions and 615 deletions
|
|
@ -89,7 +89,7 @@ Whether you read those or not, I'm more than happy to help you 1-on-1 with codin
|
||||||
|
|
||||||
# Dependency justification
|
# Dependency justification
|
||||||
|
|
||||||
Total transitive production dependencies: 134
|
Total transitive production dependencies: 121
|
||||||
|
|
||||||
### <font size="+2">🦕</font>
|
### <font size="+2">🦕</font>
|
||||||
|
|
||||||
|
|
@ -108,6 +108,7 @@ Total transitive production dependencies: 134
|
||||||
* (0) @cloudrac3r/in-your-element: This is my Matrix Appservice API library. It depends on h3 and zod, which are already pulled in by OOYE.
|
* (0) @cloudrac3r/in-your-element: This is my Matrix Appservice API library. It depends on h3 and zod, which are already pulled in by OOYE.
|
||||||
* (0) @cloudrac3r/mixin-deep: This is my fork. (It fixes a bug in regular mixin-deep.)
|
* (0) @cloudrac3r/mixin-deep: This is my fork. (It fixes a bug in regular mixin-deep.)
|
||||||
* (0) @cloudrac3r/pngjs: Lottie stickers are converted to bitmaps with the vendored Rlottie WASM build, then the bitmaps are converted to PNG with pngjs.
|
* (0) @cloudrac3r/pngjs: Lottie stickers are converted to bitmaps with the vendored Rlottie WASM build, then the bitmaps are converted to PNG with pngjs.
|
||||||
|
* (0) @cloudrac3r/stream-type: Determine type of Matrix files that don't specify it in info. Switched from stream-mime-type to this.
|
||||||
* (0) @cloudrac3r/turndown: This HTML-to-Markdown converter looked the most suitable. I forked it to change the escaping logic to match the way Discord works.
|
* (0) @cloudrac3r/turndown: This HTML-to-Markdown converter looked the most suitable. I forked it to change the escaping logic to match the way Discord works.
|
||||||
* (3) @stackoverflow/stacks: Stack Overflow design language and icons.
|
* (3) @stackoverflow/stacks: Stack Overflow design language and icons.
|
||||||
* (0) ansi-colors: Helps with interactive prompting for the initial setup, and it's already pulled in by enquirer.
|
* (0) ansi-colors: Helps with interactive prompting for the initial setup, and it's already pulled in by enquirer.
|
||||||
|
|
|
||||||
548
package-lock.json
generated
548
package-lock.json
generated
|
|
@ -10,20 +10,21 @@
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
"@cloudrac3r/discord-markdown": "^2.7.0",
|
||||||
"@cloudrac3r/giframe": "^0.4.3",
|
"@cloudrac3r/giframe": "^0.4.3",
|
||||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||||
"@cloudrac3r/mixin-deep": "^3.0.1",
|
"@cloudrac3r/mixin-deep": "^3.0.1",
|
||||||
"@cloudrac3r/pngjs": "^7.0.3",
|
"@cloudrac3r/pngjs": "^7.0.3",
|
||||||
"@cloudrac3r/pug": "^4.0.4",
|
"@cloudrac3r/pug": "^4.0.4",
|
||||||
|
"@cloudrac3r/stream-type": "^1.0.0",
|
||||||
"@cloudrac3r/turndown": "^7.1.4",
|
"@cloudrac3r/turndown": "^7.1.4",
|
||||||
"@stackoverflow/stacks": "^2.5.4",
|
"@stackoverflow/stacks": "^2.5.4",
|
||||||
"@stackoverflow/stacks-icons": "^6.0.2",
|
"@stackoverflow/stacks-icons": "^6.0.2",
|
||||||
"ansi-colors": "^4.1.3",
|
"ansi-colors": "^4.1.3",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"chunk-text": "^2.0.1",
|
"chunk-text": "^2.0.1",
|
||||||
"cloudstorm": "^0.15.2",
|
"cloudstorm": "^0.17.0",
|
||||||
"discord-api-types": "^0.38.38",
|
"discord-api-types": "^0.38.38",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
|
|
@ -36,9 +37,8 @@
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"prettier-bytes": "^1.0.4",
|
"prettier-bytes": "^1.0.4",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"snowtransfer": "^0.17.1",
|
"snowtransfer": "^0.17.5",
|
||||||
"stream-mime-type": "^1.0.2",
|
"try-to-catch": "^4.0.5",
|
||||||
"try-to-catch": "^3.0.1",
|
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
"xxhash-wasm": "^1.0.2",
|
"xxhash-wasm": "^1.0.2",
|
||||||
"zod": "^4.0.17"
|
"zod": "^4.0.17"
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudrac3r/tap-dot": "^2.0.3",
|
"@cloudrac3r/tap-dot": "^2.0.3",
|
||||||
"@types/node": "^22.17.1",
|
"@types/node": "^22.17.1",
|
||||||
"c8": "^10.1.2",
|
"c8": "^11.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"supertape": "^12.0.12"
|
"supertape": "^12.0.12"
|
||||||
},
|
},
|
||||||
|
|
@ -68,6 +68,23 @@
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"../nodejs-stream-type": {
|
||||||
|
"name": "@cloudrac3r/stream-type",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"extraneous": true,
|
||||||
|
"license": "AGPL-3.0-only",
|
||||||
|
"devDependencies": {
|
||||||
|
"@cloudrac3r/tap-dot": "^2.0.3",
|
||||||
|
"@types/node": "^22.19.15",
|
||||||
|
"c8": "^11.0.0",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
|
"supertape": "^12.10.4",
|
||||||
|
"try-to-catch": "^4.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"../tap-dot": {
|
"../tap-dot": {
|
||||||
"name": "@cloudrac3r/tap-dot",
|
"name": "@cloudrac3r/tap-dot",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
|
@ -137,9 +154,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@chriscdn/promise-semaphore": {
|
"node_modules/@chriscdn/promise-semaphore": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.1.3.tgz",
|
||||||
"integrity": "sha512-rELbH6FSr9wr5J249Ax8dpzQdTaqEgcW+lilDKZxB13Hz0Bz3Iyx4q/7qZxPMnra9FUW4ZOkVf+bx5tbi6Goog==",
|
"integrity": "sha512-EAmwIbH1L2CNsJWloXBG4Kv89H7IUsjYFQnGnmus3OX70LcD5Uu5A7sohPx3O0Ks9UQWEgcr5n2IfxBSuYvOeg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@cloudcmd/stub": {
|
"node_modules/@cloudcmd/stub": {
|
||||||
|
|
@ -156,9 +173,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cloudrac3r/discord-markdown": {
|
"node_modules/@cloudrac3r/discord-markdown": {
|
||||||
"version": "2.6.10",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.10.tgz",
|
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.7.0.tgz",
|
||||||
"integrity": "sha512-E+F9UYDUHP2kHDCciX63SBzgsUnHpu2Pp/h98x9Zo+vKuzXjCQ5PcFNdUlH6M18bvHDZPoIsKVmjnON8UYaAPQ==",
|
"integrity": "sha512-1iR9tKI2WJe8UNB+4VSh7D8m6RP7ugByuf8RNWyJwyhIrSlqQ8ljY1BKXodSvDg7seZkf7B7V2t5FfK7UpTw/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"simple-markdown": "^0.7.3"
|
"simple-markdown": "^0.7.3"
|
||||||
|
|
@ -178,9 +195,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cloudrac3r/in-your-element": {
|
"node_modules/@cloudrac3r/in-your-element": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/in-your-element/-/in-your-element-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cloudrac3r/in-your-element/-/in-your-element-1.1.2.tgz",
|
||||||
"integrity": "sha512-AKp9vnSDA9wzJl4O3C/LA8jgI5m1r0M3MRBQGHcVVL22SrrZMdcy+kWjlZWK343KVLOkuTAISA2D+Jb/zyZS6A==",
|
"integrity": "sha512-adFZel24sGHpTI1vgJdBN5twcdu6QmPFlO8qAJt49KO6N8mwDcbUC2GPqH5pGerXNv1Lpq0eXsNLm+ytKrOTaQ==",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"h3": "^1.12.0",
|
"h3": "^1.12.0",
|
||||||
|
|
@ -246,6 +263,15 @@
|
||||||
"pug-error": "^2.1.0"
|
"pug-error": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@cloudrac3r/stream-type": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cloudrac3r/stream-type/-/stream-type-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-orfdUaeDT00fkELxAab+pJNZWwis+KijJEWw+cUWOD2VqqQWriL04W5DOPN0dlsJvn4VoyBe6cYGrzsJ5YPcOw==",
|
||||||
|
"license": "AGPL-3.0-only",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@cloudrac3r/tap-dot": {
|
"node_modules/@cloudrac3r/tap-dot": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/tap-dot/-/tap-dot-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@cloudrac3r/tap-dot/-/tap-dot-2.0.3.tgz",
|
||||||
|
|
@ -765,21 +791,12 @@
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
|
||||||
"version": "9.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",
|
|
||||||
"integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@istanbuljs/schema": {
|
"node_modules/@istanbuljs/schema": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
|
|
@ -1036,11 +1053,6 @@
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tokenizer/token": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
|
||||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/istanbul-lib-coverage": {
|
"node_modules/@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
||||||
|
|
@ -1048,9 +1060,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.19.7",
|
"version": "22.19.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",
|
||||||
"integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
|
"integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1085,6 +1097,15 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
|
@ -1132,10 +1153,14 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
|
|
@ -1211,34 +1236,25 @@
|
||||||
"ieee754": "^1.1.13"
|
"ieee754": "^1.1.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bl/node_modules/readable-stream": {
|
|
||||||
"version": "3.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
|
||||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
|
||||||
"dependencies": {
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"string_decoder": "^1.1.1",
|
|
||||||
"util-deprecate": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
|
||||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/c8": {
|
"node_modules/c8": {
|
||||||
"version": "10.1.3",
|
"version": "11.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/c8/-/c8-11.0.0.tgz",
|
||||||
"integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==",
|
"integrity": "sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bcoe/v8-coverage": "^1.0.1",
|
"@bcoe/v8-coverage": "^1.0.1",
|
||||||
"@istanbuljs/schema": "^0.1.3",
|
"@istanbuljs/schema": "^0.1.3",
|
||||||
|
|
@ -1247,7 +1263,7 @@
|
||||||
"istanbul-lib-coverage": "^3.2.0",
|
"istanbul-lib-coverage": "^3.2.0",
|
||||||
"istanbul-lib-report": "^3.0.1",
|
"istanbul-lib-report": "^3.0.1",
|
||||||
"istanbul-reports": "^3.1.6",
|
"istanbul-reports": "^3.1.6",
|
||||||
"test-exclude": "^7.0.1",
|
"test-exclude": "^8.0.0",
|
||||||
"v8-to-istanbul": "^9.0.0",
|
"v8-to-istanbul": "^9.0.0",
|
||||||
"yargs": "^17.7.2",
|
"yargs": "^17.7.2",
|
||||||
"yargs-parser": "^21.1.1"
|
"yargs-parser": "^21.1.1"
|
||||||
|
|
@ -1256,7 +1272,7 @@
|
||||||
"c8": "bin/c8.js"
|
"c8": "bin/c8.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": "20 || >=22"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"monocart-coverage-reports": "^2"
|
"monocart-coverage-reports": "^2"
|
||||||
|
|
@ -1348,35 +1364,14 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cliui/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cloudstorm": {
|
"node_modules/cloudstorm": {
|
||||||
"version": "0.15.2",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.17.0.tgz",
|
||||||
"integrity": "sha512-5y7E0uI39R3d7c+AWksqAQAlZlpx+qNjxjQfNIem2hh68s6QRmOFHTKu34I7pBE6JonpZf8AmoMYArY/4lLVmg==",
|
"integrity": "sha512-zsd9y5ljNnbxdvDid9TgWePDqo7il4so5spzx6NDwZ67qWQjR96UUhLxJ+BAOdBBSPF9UXFM61dAzC2g918q+A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord-api-types": "^0.38.37",
|
"discord-api-types": "^0.38.40",
|
||||||
"snowtransfer": "^0.17.0"
|
"snowtransfer": "^0.17.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0"
|
"node": ">=22.0.0"
|
||||||
|
|
@ -1517,9 +1512,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/discord-api-types": {
|
"node_modules/discord-api-types": {
|
||||||
"version": "0.38.38",
|
"version": "0.38.41",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.38.tgz",
|
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz",
|
||||||
"integrity": "sha512-7qcM5IeZrfb+LXW07HvoI5L+j4PQeMZXEkSm1htHAHh4Y9JSMXBWjy/r7zmUCOj4F7zNjMcm7IMWr131MT2h0Q==",
|
"integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"scripts/actions/documentation"
|
"scripts/actions/documentation"
|
||||||
|
|
@ -1561,25 +1556,6 @@
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/enquirer/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/enquirer/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-5.0.0.tgz",
|
||||||
|
|
@ -1617,22 +1593,6 @@
|
||||||
"node": ">= 4.9.1"
|
"node": ">= 4.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/file-type": {
|
|
||||||
"version": "16.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
|
|
||||||
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
|
|
||||||
"dependencies": {
|
|
||||||
"readable-web-to-node-stream": "^3.0.0",
|
|
||||||
"strtok3": "^6.2.4",
|
|
||||||
"token-types": "^4.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/file-uri-to-path": {
|
"node_modules/file-uri-to-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
|
|
@ -1655,10 +1615,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.1",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
|
||||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
"integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
|
|
@ -1731,66 +1692,18 @@
|
||||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "12.0.0",
|
"version": "13.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||||
"integrity": "sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==",
|
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"foreground-child": "^3.3.1",
|
"minimatch": "^10.2.2",
|
||||||
"jackspeak": "^4.1.1",
|
"minipass": "^7.1.3",
|
||||||
"minimatch": "^10.1.1",
|
"path-scurry": "^2.0.2"
|
||||||
"minipass": "^7.1.2",
|
|
||||||
"package-json-from-dist": "^1.0.0",
|
|
||||||
"path-scurry": "^2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"glob": "dist/esm/bin.mjs"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "20 || >=22"
|
"node": "18 || 20 || >=22"
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob/node_modules/balanced-match": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"jackspeak": "^4.2.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob/node_modules/brace-expansion": {
|
|
||||||
"version": "5.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
|
|
||||||
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^4.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob/node_modules/minimatch": {
|
|
||||||
"version": "10.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.0.tgz",
|
|
||||||
"integrity": "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^5.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
|
@ -1973,22 +1886,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jackspeak": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@isaacs/cliui": "^9.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jest-diff": {
|
"node_modules/jest-diff": {
|
||||||
"version": "30.2.0",
|
"version": "30.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz",
|
||||||
|
|
@ -2023,6 +1920,13 @@
|
||||||
"integrity": "sha512-p2BdO7o4BI+pMun3J+dhaOfYan5JsZrw9wjshRjkWY9+p+u+kKSMhNWYnot2yHDR9CSahZ9iT3dcqJ+V72qHMw==",
|
"integrity": "sha512-p2BdO7o4BI+pMun3J+dhaOfYan5JsZrw9wjshRjkWY9+p+u+kKSMhNWYnot2yHDR9CSahZ9iT3dcqJ+V72qHMw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/just-snake-case": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/just-snake-case/-/just-snake-case-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-iugHP9bSE0jOq3BzN0W0rdu/OOkFirPe8FtUw6v9y37UlbUDgJ1x4xiGNfUhI6mV9dc/paaifyiyn+F+mrm8gw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
|
|
@ -2039,9 +1943,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "11.2.4",
|
"version": "11.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
|
||||||
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
|
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "20 || >=22"
|
"node": "20 || >=22"
|
||||||
|
|
@ -2094,16 +1998,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "9.0.5",
|
"version": "10.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^5.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": "18 || 20 || >=22"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
|
@ -2118,10 +2022,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
|
|
@ -2199,12 +2104,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/package-json-from-dist": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
|
@ -2230,9 +2129,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/path-scurry": {
|
"node_modules/path-scurry": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
"integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
|
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2240,24 +2139,12 @@
|
||||||
"minipass": "^7.1.2"
|
"minipass": "^7.1.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "20 || >=22"
|
"node": "18 || 20 || >=22"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/peek-readable": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prebuild-install": {
|
"node_modules/prebuild-install": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
|
||||||
|
|
@ -2425,25 +2312,11 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/readable-web-to-node-stream": {
|
"node_modules/readable-stream": {
|
||||||
"version": "3.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
|
|
||||||
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
|
|
||||||
"dependencies": {
|
|
||||||
"readable-stream": "^3.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/readable-web-to-node-stream/node_modules/readable-stream": {
|
|
||||||
"version": "3.6.2",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"string_decoder": "^1.1.1",
|
"string_decoder": "^1.1.1",
|
||||||
|
|
@ -2648,12 +2521,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/snowtransfer": {
|
"node_modules/snowtransfer": {
|
||||||
"version": "0.17.1",
|
"version": "0.17.5",
|
||||||
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.17.5.tgz",
|
||||||
"integrity": "sha512-WSXj055EJhzzfD7B3oHVyRTxkqFCaxcVhwKY6B3NkBSHRyM6wHxZLq6VbFYhopUg+lMtd7S1ZO8JM+Ut+js2iA==",
|
"integrity": "sha512-nVI1UJNFoX1ndGFZxB3zb3X5SWtD9hIAcw7wCgVKWvCf42Wg2B4UFIrZWI83HxaSBY0CGbPZmZzZb3RSt/v2wQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord-api-types": "^0.38.37"
|
"discord-api-types": "^0.38.40"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22.0.0"
|
"node": ">=22.0.0"
|
||||||
|
|
@ -2688,30 +2561,6 @@
|
||||||
"get-source": "^2.0.12"
|
"get-source": "^2.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stream-head": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/stream-head/-/stream-head-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-aRkUMcmgbDl2Yjd5LqsB1LKB58Ot3JZ4ffuFMkFuvkPQT5X5XFMr4YK2dctApc+d3o52CXU1KUFisYaF/4zjAQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"through2": "4.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/stream-mime-type": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/stream-mime-type/-/stream-mime-type-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-80GzRn7JICPDEPBhSyqJjbztqX66+3DpkuUUcgDHtRBQlZRTkbCz0BsISggUl7AnyinJk9zyHVnd2lftlZXDdg==",
|
|
||||||
"dependencies": {
|
|
||||||
"file-type": "^16.0.1",
|
|
||||||
"mime-types": "^2.1.27",
|
|
||||||
"stream-head": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
|
@ -2734,20 +2583,11 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/string-width/node_modules/ansi-regex": {
|
"node_modules/strip-ansi": {
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
|
|
@ -2763,26 +2603,10 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/strtok3": {
|
|
||||||
"version": "6.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@tokenizer/token": "^0.3.0",
|
|
||||||
"peek-readable": "^4.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supertape": {
|
"node_modules/supertape": {
|
||||||
"version": "12.0.12",
|
"version": "12.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/supertape/-/supertape-12.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/supertape/-/supertape-12.7.0.tgz",
|
||||||
"integrity": "sha512-ugmCQsB7s22fCTJKiMb6+Fd8kP7Hsvlo6/aly0qLGgOepu1PVBydhrBPMWaoY3wf+VqLtMkkvwGxUTCFde5z/g==",
|
"integrity": "sha512-5PXh6HsfEJKkC0SMhPNkH35o8Okj8xlVvoju9R0aCohzsK+GEufeYZ1IPhRBRQ2DBLXdMZHVF6N/4pAefxNuAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2800,9 +2624,10 @@
|
||||||
"cli-progress": "^3.8.2",
|
"cli-progress": "^3.8.2",
|
||||||
"flatted": "^3.3.1",
|
"flatted": "^3.3.1",
|
||||||
"fullstore": "^4.0.0",
|
"fullstore": "^4.0.0",
|
||||||
"glob": "^11.0.1",
|
"glob": "^13.0.0",
|
||||||
"jest-diff": "^30.0.3",
|
"jest-diff": "^30.0.3",
|
||||||
"json-with-bigint": "^3.4.4",
|
"json-with-bigint": "^3.4.4",
|
||||||
|
"just-snake-case": "^3.2.0",
|
||||||
"once": "^1.4.0",
|
"once": "^1.4.0",
|
||||||
"resolve": "^1.17.0",
|
"resolve": "^1.17.0",
|
||||||
"stacktracey": "^2.1.7",
|
"stacktracey": "^2.1.7",
|
||||||
|
|
@ -2818,16 +2643,6 @@
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/supertape/node_modules/try-to-catch": {
|
|
||||||
"version": "4.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-4.0.3.tgz",
|
|
||||||
"integrity": "sha512-mUz1zpe6nkRQW0XZ/Ojfe/Eg7e5h3s+r+h7ONfP3Oo27/Jm8mkNDAnLzZ/A3sEMApROolzuJGBiQhGmmVDAFLw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supertape/node_modules/yargs-parser": {
|
"node_modules/supertape/node_modules/yargs-parser": {
|
||||||
"version": "22.0.0",
|
"version": "22.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
|
||||||
|
|
@ -2889,52 +2704,19 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar-stream/node_modules/readable-stream": {
|
|
||||||
"version": "3.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
|
||||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
|
||||||
"dependencies": {
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"string_decoder": "^1.1.1",
|
|
||||||
"util-deprecate": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/test-exclude": {
|
"node_modules/test-exclude": {
|
||||||
"version": "7.0.1",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz",
|
||||||
"integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
|
"integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@istanbuljs/schema": "^0.1.2",
|
"@istanbuljs/schema": "^0.1.2",
|
||||||
"glob": "^10.4.1",
|
"glob": "^13.0.6",
|
||||||
"minimatch": "^9.0.4"
|
"minimatch": "^10.2.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": "20 || >=22"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/through2": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
|
|
||||||
"dependencies": {
|
|
||||||
"readable-stream": "3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/through2/node_modules/readable-stream": {
|
|
||||||
"version": "3.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
|
||||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
|
||||||
"dependencies": {
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"string_decoder": "^1.1.1",
|
|
||||||
"util-deprecate": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/timer-node": {
|
"node_modules/timer-node": {
|
||||||
|
|
@ -2949,22 +2731,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg=="
|
"integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg=="
|
||||||
},
|
},
|
||||||
"node_modules/token-types": {
|
|
||||||
"version": "4.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
|
|
||||||
"integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@tokenizer/token": "^0.3.0",
|
|
||||||
"ieee754": "^1.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/try-catch": {
|
"node_modules/try-catch": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/try-catch/-/try-catch-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/try-catch/-/try-catch-4.0.7.tgz",
|
||||||
|
|
@ -2976,11 +2742,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/try-to-catch": {
|
"node_modules/try-to-catch": {
|
||||||
"version": "3.0.1",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-4.0.5.tgz",
|
||||||
"integrity": "sha512-hOY83V84Hx/1sCzDSaJA+Xz2IIQOHRvjxzt+F0OjbQGPZ6yLPLArMA0gw/484MlfUkQbCpKYMLX3VDCAjWKfzQ==",
|
"integrity": "sha512-VKBslDQsy4pGj2TMNgDdskWb7AWSi/9dPEmcNv3sdL0+aRMQTPJo6aEqlcuN0vbOwFfsE1oAXmx3bFdf6vrJFg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
|
|
@ -3099,27 +2866,6 @@
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
|
@ -3185,9 +2931,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "4.3.5",
|
"version": "4.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||||
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
|
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -19,20 +19,21 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
"@cloudrac3r/discord-markdown": "^2.7.0",
|
||||||
"@cloudrac3r/giframe": "^0.4.3",
|
"@cloudrac3r/giframe": "^0.4.3",
|
||||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||||
"@cloudrac3r/mixin-deep": "^3.0.1",
|
"@cloudrac3r/mixin-deep": "^3.0.1",
|
||||||
"@cloudrac3r/pngjs": "^7.0.3",
|
"@cloudrac3r/pngjs": "^7.0.3",
|
||||||
"@cloudrac3r/pug": "^4.0.4",
|
"@cloudrac3r/pug": "^4.0.4",
|
||||||
|
"@cloudrac3r/stream-type": "^1.0.0",
|
||||||
"@cloudrac3r/turndown": "^7.1.4",
|
"@cloudrac3r/turndown": "^7.1.4",
|
||||||
"@stackoverflow/stacks": "^2.5.4",
|
"@stackoverflow/stacks": "^2.5.4",
|
||||||
"@stackoverflow/stacks-icons": "^6.0.2",
|
"@stackoverflow/stacks-icons": "^6.0.2",
|
||||||
"ansi-colors": "^4.1.3",
|
"ansi-colors": "^4.1.3",
|
||||||
"better-sqlite3": "^12.2.0",
|
"better-sqlite3": "^12.2.0",
|
||||||
"chunk-text": "^2.0.1",
|
"chunk-text": "^2.0.1",
|
||||||
"cloudstorm": "^0.15.2",
|
"cloudstorm": "^0.17.0",
|
||||||
"discord-api-types": "^0.38.38",
|
"discord-api-types": "^0.38.38",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"enquirer": "^2.4.1",
|
"enquirer": "^2.4.1",
|
||||||
|
|
@ -45,9 +46,8 @@
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"prettier-bytes": "^1.0.4",
|
"prettier-bytes": "^1.0.4",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"snowtransfer": "^0.17.1",
|
"snowtransfer": "^0.17.5",
|
||||||
"stream-mime-type": "^1.0.2",
|
"try-to-catch": "^4.0.5",
|
||||||
"try-to-catch": "^3.0.1",
|
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
"xxhash-wasm": "^1.0.2",
|
"xxhash-wasm": "^1.0.2",
|
||||||
"zod": "^4.0.17"
|
"zod": "^4.0.17"
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudrac3r/tap-dot": "^2.0.3",
|
"@cloudrac3r/tap-dot": "^2.0.3",
|
||||||
"@types/node": "^22.17.1",
|
"@types/node": "^22.17.1",
|
||||||
"c8": "^10.1.2",
|
"c8": "^11.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"supertape": "^12.0.12"
|
"supertape": "^12.0.12"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ if (!channelID) {
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const assert = require("assert/strict")
|
|
||||||
const sqlite = require("better-sqlite3")
|
const sqlite = require("better-sqlite3")
|
||||||
const backfill = new sqlite("scripts/backfill.db")
|
const backfill = new sqlite("scripts/backfill.db")
|
||||||
backfill.prepare("CREATE TABLE IF NOT EXISTS backfill (channel_id TEXT NOT NULL, message_id INTEGER NOT NULL, PRIMARY KEY (channel_id, message_id))").run()
|
backfill.prepare("CREATE TABLE IF NOT EXISTS backfill (channel_id TEXT NOT NULL, message_id INTEGER NOT NULL, PRIMARY KEY (channel_id, message_id))").run()
|
||||||
|
|
@ -38,12 +37,8 @@ passthrough.select = orm.select
|
||||||
|
|
||||||
/** @type {import("../src/d2m/event-dispatcher")}*/
|
/** @type {import("../src/d2m/event-dispatcher")}*/
|
||||||
const eventDispatcher = sync.require("../src/d2m/event-dispatcher")
|
const eventDispatcher = sync.require("../src/d2m/event-dispatcher")
|
||||||
|
/** @type {import("../src/d2m/actions/create-room")} */
|
||||||
const roomID = passthrough.select("channel_room", "room_id", {channel_id: channelID}).pluck().get()
|
const createRoom = sync.require("../src/d2m/actions/create-room")
|
||||||
if (!roomID) {
|
|
||||||
console.error("Please choose a channel that's already bridged.")
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
;(async () => {
|
;(async () => {
|
||||||
await discord.cloud.connect()
|
await discord.cloud.connect()
|
||||||
|
|
@ -60,23 +55,29 @@ async function event(event) {
|
||||||
if (!channel) return
|
if (!channel) return
|
||||||
const guild_id = event.d.id
|
const guild_id = event.d.id
|
||||||
|
|
||||||
let last = backfill.prepare("SELECT cast(max(message_id) as TEXT) FROM backfill WHERE channel_id = ?").pluck().get(channelID) || "0"
|
try {
|
||||||
console.log(`OK, processing messages for #${channel.name}, continuing from ${last}`)
|
await createRoom.syncRoom(channelID)
|
||||||
|
let last = backfill.prepare("SELECT cast(max(message_id) as TEXT) FROM backfill WHERE channel_id = ?").pluck().get(channelID) || "0"
|
||||||
|
console.log(`OK, processing messages for #${channel.name}, continuing from ${last}`)
|
||||||
|
|
||||||
while (last) {
|
while (last) {
|
||||||
const messages = await discord.snow.channel.getChannelMessages(channelID, {limit: 50, after: String(last)})
|
const messages = await discord.snow.channel.getChannelMessages(channelID, {limit: 50, after: String(last)})
|
||||||
messages.reverse() // More recent messages come first -> More recent messages come last
|
messages.reverse() // More recent messages come first -> More recent messages come last
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
const simulatedGatewayDispatchData = {
|
const simulatedGatewayDispatchData = {
|
||||||
guild_id,
|
guild_id,
|
||||||
backfill: true,
|
backfill: true,
|
||||||
...message
|
...message
|
||||||
|
}
|
||||||
|
await eventDispatcher.MESSAGE_CREATE(discord, simulatedGatewayDispatchData)
|
||||||
|
preparedInsert.run(channelID, message.id)
|
||||||
}
|
}
|
||||||
await eventDispatcher.MESSAGE_CREATE(discord, simulatedGatewayDispatchData)
|
last = messages.at(-1)?.id
|
||||||
preparedInsert.run(channelID, message.id)
|
|
||||||
}
|
}
|
||||||
last = messages.at(-1)?.id
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit()
|
process.exit()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1) // won't exit automatically on thrown error due to living discord connection, so manual exit is necessary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ test("edit2changes: bot response", async t => {
|
||||||
newContent: {
|
newContent: {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "* :ae_botrac4r: [@cadence](https://matrix.to/#/@cadence:cadence.moe) asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
|
body: "* :ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: '* <img data-mx-emoticon height="32" src="mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs" title=":ae_botrac4r:" alt=":ae_botrac4r:"> <a href="https://matrix.to/#/@cadence:cadence.moe">@cadence</a> asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img data-mx-emoticon height="32" src="mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT" title=":bn_re:" alt=":bn_re:"> to reroll.',
|
formatted_body: '* <img data-mx-emoticon height="32" src="mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs" title=":ae_botrac4r:" alt=":ae_botrac4r:"> <a href="https://matrix.to/#/@cadence:cadence.moe">@cadence</a> asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img data-mx-emoticon height="32" src="mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT" title=":bn_re:" alt=":bn_re:"> to reroll.',
|
||||||
"m.mentions": {
|
"m.mentions": {
|
||||||
|
|
@ -87,7 +87,7 @@ test("edit2changes: bot response", async t => {
|
||||||
// *** Replaced With: ***
|
// *** Replaced With: ***
|
||||||
"m.new_content": {
|
"m.new_content": {
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: ":ae_botrac4r: [@cadence](https://matrix.to/#/@cadence:cadence.moe) asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
|
body: ":ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: '<img data-mx-emoticon height="32" src="mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs" title=":ae_botrac4r:" alt=":ae_botrac4r:"> <a href="https://matrix.to/#/@cadence:cadence.moe">@cadence</a> asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img data-mx-emoticon height="32" src="mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT" title=":bn_re:" alt=":bn_re:"> to reroll.',
|
formatted_body: '<img data-mx-emoticon height="32" src="mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs" title=":ae_botrac4r:" alt=":ae_botrac4r:"> <a href="https://matrix.to/#/@cadence:cadence.moe">@cadence</a> asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img data-mx-emoticon height="32" src="mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT" title=":bn_re:" alt=":bn_re:"> to reroll.',
|
||||||
"m.mentions": {
|
"m.mentions": {
|
||||||
|
|
|
||||||
|
|
@ -146,10 +146,18 @@ function findMention(pjr, maximumWrittenSection, baseOffset, prefix, content) {
|
||||||
// Highlight the relevant part of the message
|
// Highlight the relevant part of the message
|
||||||
const start = baseOffset + best.scored.matchedInputTokens[0].index
|
const start = baseOffset + best.scored.matchedInputTokens[0].index
|
||||||
const end = baseOffset + prefix.length + best.scored.matchedInputTokens.slice(-1)[0].end
|
const end = baseOffset + prefix.length + best.scored.matchedInputTokens.slice(-1)[0].end
|
||||||
const newContent = content.slice(0, start) + "[" + content.slice(start, end) + "](https://matrix.to/#/" + best.mxid + ")" + content.slice(end)
|
const newNodes = [{
|
||||||
|
type: "text", content: content.slice(0, start)
|
||||||
|
}, {
|
||||||
|
type: "link", target: `https://matrix.to/#/${best.mxid}`, content: [
|
||||||
|
{type: "text", content: content.slice(start, end)}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
type: "text", content: content.slice(end)
|
||||||
|
}]
|
||||||
return {
|
return {
|
||||||
mxid: best.mxid,
|
mxid: best.mxid,
|
||||||
newContent
|
newNodes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,29 @@ function getFormattedInteraction(interaction, isThinkingInteraction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} newEvents merge into events
|
||||||
|
* @param {any} events will be modified
|
||||||
|
* @param {boolean} forceSameMsgtype whether m.text may only be combined with m.text, etc
|
||||||
|
*/
|
||||||
|
function mergeTextEvents(newEvents, events, forceSameMsgtype) {
|
||||||
|
let prev = events.at(-1)
|
||||||
|
for (const ne of newEvents) {
|
||||||
|
const isAllText = prev?.body && prev?.formatted_body && ["m.text", "m.notice"].includes(ne.msgtype) && ["m.text", "m.notice"].includes(prev?.msgtype)
|
||||||
|
const typesPermitted = !forceSameMsgtype || ne?.msgtype === prev?.msgtype
|
||||||
|
if (isAllText && typesPermitted) {
|
||||||
|
const rep = new mxUtils.MatrixStringBuilder()
|
||||||
|
rep.body = prev.body
|
||||||
|
rep.formattedBody = prev.formatted_body
|
||||||
|
rep.addLine(ne.body, ne.formatted_body)
|
||||||
|
prev.body = rep.body
|
||||||
|
prev.formatted_body = rep.formattedBody
|
||||||
|
} else {
|
||||||
|
events.push(ne)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DiscordTypes.APIMessage} message
|
* @param {DiscordTypes.APIMessage} message
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
|
|
@ -519,29 +542,60 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
return emojiToKey.emojiToKey({id, name, animated}, message.id) // Register the custom emoji if needed
|
return emojiToKey.emojiToKey({id, name, animated}, message.id) // Register the custom emoji if needed
|
||||||
}))
|
}))
|
||||||
|
|
||||||
async function transformParsedVia(parsed) {
|
async function transformParsedVia(parsed, scanTextForMentions) {
|
||||||
for (const node of parsed) {
|
for (let n = 0; n < parsed.length; n++) {
|
||||||
|
const node = parsed[n]
|
||||||
if (node.type === "discordChannel" || node.type === "discordChannelLink") {
|
if (node.type === "discordChannel" || node.type === "discordChannelLink") {
|
||||||
node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get()
|
node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get()
|
||||||
if (node.row?.room_id) {
|
if (node.row?.room_id) {
|
||||||
node.via = await getViaServersMemo(node.row.room_id)
|
node.via = await getViaServersMemo(node.row.room_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (node.type === "text" && typeof node.content === "string") {
|
||||||
|
// Merge adjacent text nodes into this one
|
||||||
|
while (parsed[n+1]?.type === "text" && typeof parsed[n+1].content === "string") {
|
||||||
|
node.content += parsed[n+1].content
|
||||||
|
parsed.splice(n+1, 1)
|
||||||
|
}
|
||||||
|
// Mentions scenario 3: scan the message content for written @mentions of matrix users. Allows for up to one space between @ and mention.
|
||||||
|
if (scanTextForMentions) {
|
||||||
|
let content = node.content
|
||||||
|
const matches = [...content.matchAll(/(@ ?)([a-z0-9_.#$][^@\n]+)/gi)]
|
||||||
|
for (let i = matches.length; i--;) {
|
||||||
|
const m = matches[i]
|
||||||
|
const prefix = m[1]
|
||||||
|
const maximumWrittenSection = m[2].toLowerCase()
|
||||||
|
if (m.index > 0 && !content[m.index-1].match(/ |\(|\n/)) continue // must have space before it
|
||||||
|
if (maximumWrittenSection.match(/^everyone\b/) || maximumWrittenSection.match(/^here\b/)) continue // ignore @everyone/@here
|
||||||
|
|
||||||
|
var roomID = roomID ?? select("channel_room", "room_id", {channel_id: message.channel_id}).pluck().get()
|
||||||
|
assert(roomID)
|
||||||
|
var pjr = pjr ?? findMentions.processJoined(Object.entries((await di.api.getJoinedMembers(roomID)).joined).map(([mxid, ev]) => ({mxid, displayname: ev.display_name})))
|
||||||
|
|
||||||
|
const found = findMentions.findMention(pjr, maximumWrittenSection, m.index, prefix, content)
|
||||||
|
if (found) {
|
||||||
|
addMention(found.mxid)
|
||||||
|
parsed.splice(n, 1, ...found.newNodes)
|
||||||
|
content = found.newNodes[0].content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const maybeChildNodesArray of [node, node.content, node.items]) {
|
for (const maybeChildNodesArray of [node, node.content, node.items]) {
|
||||||
if (Array.isArray(maybeChildNodesArray)) {
|
if (Array.isArray(maybeChildNodesArray)) {
|
||||||
await transformParsedVia(maybeChildNodesArray)
|
await transformParsedVia(maybeChildNodesArray, scanTextForMentions && ["blockQuote", "list", "paragraph", "em", "strong", "u", "del", "text"].includes(node.type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
|
let html = await markdown.toHtmlWithPostParser(content, parsed => transformParsedVia(parsed, customOptions.isTheMessageContent && options.scanTextForMentions !== false), {
|
||||||
discordCallback: getDiscordParseCallbacks(message, guild, true, spoilers),
|
discordCallback: getDiscordParseCallbacks(message, guild, true, spoilers),
|
||||||
...customOptions
|
...customOptions
|
||||||
}, customParser, customHtmlOutput)
|
}, customParser, customHtmlOutput)
|
||||||
|
|
||||||
let body = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
|
let body = await markdown.toHtmlWithPostParser(content, parsed => transformParsedVia(parsed, false), { // not scanning plaintext body for mentions as we don't parse whether they're in code
|
||||||
discordCallback: getDiscordParseCallbacks(message, guild, false),
|
discordCallback: getDiscordParseCallbacks(message, guild, false),
|
||||||
discordOnly: true,
|
discordOnly: true,
|
||||||
escapeHTML: false,
|
escapeHTML: false,
|
||||||
|
|
@ -582,7 +636,8 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
// check that condition 1 or 2 is met
|
// check that condition 1 or 2 is met
|
||||||
if (repliedToEventInDifferentRoom || repliedToUnknownEvent) {
|
if (repliedToEventInDifferentRoom || repliedToUnknownEvent) {
|
||||||
let referenced = message.referenced_message
|
let referenced = message.referenced_message
|
||||||
if (!referenced) { // backend couldn't be bothered to dereference the message, have to do it ourselves
|
/* c8 ignore next 4 - backend couldn't be bothered to dereference the message, have to do it ourselves */
|
||||||
|
if (!referenced) {
|
||||||
assert(message.message_reference?.message_id)
|
assert(message.message_reference?.message_id)
|
||||||
referenced = await discord.snow.channel.getChannelMessage(message.message_reference.channel_id, message.message_reference.message_id)
|
referenced = await discord.snow.channel.getChannelMessage(message.message_reference.channel_id, message.message_reference.message_id)
|
||||||
}
|
}
|
||||||
|
|
@ -734,42 +789,30 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
|
|
||||||
// Then text content
|
// Then text content
|
||||||
if (message.content && !isOnlyKlipyGIF && !isThinkingInteraction) {
|
if (message.content && !isOnlyKlipyGIF && !isThinkingInteraction) {
|
||||||
// Mentions scenario 3: scan the message content for written @mentions of matrix users. Allows for up to one space between @ and mention.
|
|
||||||
let content = message.content
|
|
||||||
if (options.scanTextForMentions !== false) {
|
|
||||||
const matches = [...content.matchAll(/(@ ?)([a-z0-9_.#$][^@\n]+)/gi)]
|
|
||||||
for (let i = matches.length; i--;) {
|
|
||||||
const m = matches[i]
|
|
||||||
const prefix = m[1]
|
|
||||||
const maximumWrittenSection = m[2].toLowerCase()
|
|
||||||
if (m.index > 0 && !content[m.index-1].match(/ |\(|\n/)) continue // must have space before it
|
|
||||||
if (maximumWrittenSection.match(/^everyone\b/) || maximumWrittenSection.match(/^here\b/)) continue // ignore @everyone/@here
|
|
||||||
|
|
||||||
var roomID = roomID ?? select("channel_room", "room_id", {channel_id: message.channel_id}).pluck().get()
|
|
||||||
assert(roomID)
|
|
||||||
var pjr = pjr ?? findMentions.processJoined(Object.entries((await di.api.getJoinedMembers(roomID)).joined).map(([mxid, ev]) => ({mxid, displayname: ev.display_name})))
|
|
||||||
|
|
||||||
const found = findMentions.findMention(pjr, maximumWrittenSection, m.index, prefix, content)
|
|
||||||
if (found) {
|
|
||||||
addMention(found.mxid)
|
|
||||||
content = found.newContent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan the content for emojihax and replace them with real emojis
|
// Scan the content for emojihax and replace them with real emojis
|
||||||
content = content.replaceAll(/\[([a-zA-Z0-9_-]{2,32})(?:~[0-9]+)?\]\(https:\/\/cdn\.discordapp\.com\/emojis\/([0-9]+)\.[^ \n)`]+\)/g, (_, name, id) => {
|
let content = message.content.replaceAll(/\[([a-zA-Z0-9_-]{2,32})(?:~[0-9]+)?\]\(https:\/\/cdn\.discordapp\.com\/emojis\/([0-9]+)\.[^ \n)`]+\)/g, (_, name, id) => {
|
||||||
return `<:${name}:${id}>`
|
return `<:${name}:${id}>`
|
||||||
})
|
})
|
||||||
|
|
||||||
const {body, html} = await transformContent(content)
|
const {body, html} = await transformContent(content, {isTheMessageContent: true})
|
||||||
await addTextEvent(body, html, msgtype)
|
await addTextEvent(body, html, msgtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then scheduled events
|
// Then scheduled events
|
||||||
if (message.content && di?.snow) {
|
if (message.content && di?.snow) {
|
||||||
for (const match of [...message.content.matchAll(/discord\.gg\/([A-Za-z0-9]+)\?event=([0-9]{18,})/g)]) { // snowflake has minimum 18 because the events feature is at least that old
|
for (const match of [...message.content.matchAll(/discord\.gg\/([A-Za-z0-9]+)\?event=([0-9]{18,})/g)]) { // snowflake has minimum 18 because the events feature is at least that old
|
||||||
const invite = await di.snow.invite.getInvite(match[1], {guild_scheduled_event_id: match[2]})
|
let invite
|
||||||
|
try {
|
||||||
|
invite = await di.snow.invite.getInvite(match[1], {guild_scheduled_event_id: match[2]})
|
||||||
|
} catch (e) {
|
||||||
|
// Skip expired/invalid invites and events
|
||||||
|
if (e.message === `{"message": "Unknown Invite", "code": 10006}`) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const event = invite.guild_scheduled_event
|
const event = invite.guild_scheduled_event
|
||||||
if (!event) continue // the event ID provided was not valid
|
if (!event) continue // the event ID provided was not valid
|
||||||
|
|
||||||
|
|
@ -815,15 +858,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
|
|
||||||
// Try to merge attachment events with the previous event
|
// Try to merge attachment events with the previous event
|
||||||
// This means that if the attachments ended up as a text link, and especially if there were many of them, the events will be joined together.
|
// This means that if the attachments ended up as a text link, and especially if there were many of them, the events will be joined together.
|
||||||
let prev = events.at(-1)
|
mergeTextEvents(attachmentEvents, events, false)
|
||||||
for (const atch of attachmentEvents) {
|
|
||||||
if (atch.msgtype === "m.text" && prev?.body && prev?.formatted_body && ["m.text", "m.notice"].includes(prev?.msgtype)) {
|
|
||||||
prev.body = prev.body + "\n" + atch.body
|
|
||||||
prev.formatted_body = prev.formatted_body + "<br>" + atch.formatted_body
|
|
||||||
} else {
|
|
||||||
events.push(atch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then components
|
// Then components
|
||||||
|
|
@ -905,11 +940,8 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
else if (component.type === DiscordTypes.ComponentType.Button) {
|
else if (component.type === DiscordTypes.ComponentType.Button) {
|
||||||
// May only be a section accessory or in an action row (up to 5)
|
// May only be a section accessory or in an action row (up to 5)
|
||||||
if (component.style === DiscordTypes.ButtonStyle.Link) {
|
if (component.style === DiscordTypes.ButtonStyle.Link) {
|
||||||
if (component.label) {
|
assert(component.label) // required for Discord to validate link buttons
|
||||||
stack.msb.add(`[${component.label} ${component.url}] `, tag`<a href="${component.url}">${component.label}</a> `)
|
stack.msb.add(`[${component.label} ${component.url}] `, tag`<a href="${component.url}">${component.label}</a> `)
|
||||||
} else {
|
|
||||||
stack.msb.add(component.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -964,6 +996,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
|
|
||||||
// Start building up a replica ("rep") of the embed in Discord-markdown format, which we will convert into both plaintext and formatted body at once
|
// Start building up a replica ("rep") of the embed in Discord-markdown format, which we will convert into both plaintext and formatted body at once
|
||||||
const rep = new mxUtils.MatrixStringBuilder()
|
const rep = new mxUtils.MatrixStringBuilder()
|
||||||
|
let isAdditionalImage = false
|
||||||
|
|
||||||
if (isKlipyGIF) {
|
if (isKlipyGIF) {
|
||||||
assert(embed.video?.url)
|
assert(embed.video?.url)
|
||||||
|
|
@ -1030,7 +1063,11 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
let chosenImage = embed.image?.url
|
let chosenImage = embed.image?.url
|
||||||
// the thumbnail seems to be used for "article" type but displayed big at the bottom by discord
|
// the thumbnail seems to be used for "article" type but displayed big at the bottom by discord
|
||||||
if (embed.type === "article" && embed.thumbnail?.url && !chosenImage) chosenImage = embed.thumbnail.url
|
if (embed.type === "article" && embed.thumbnail?.url && !chosenImage) chosenImage = embed.thumbnail.url
|
||||||
if (chosenImage) rep.addParagraph(`📸 ${dUtils.getPublicUrlForCdn(chosenImage)}`)
|
|
||||||
|
if (chosenImage) {
|
||||||
|
isAdditionalImage = !rep.body && !!events.length
|
||||||
|
rep.addParagraph(`📸 ${dUtils.getPublicUrlForCdn(chosenImage)}`)
|
||||||
|
}
|
||||||
|
|
||||||
if (embed.video?.url) rep.addParagraph(`🎞️ ${dUtils.getPublicUrlForCdn(embed.video.url)}`)
|
if (embed.video?.url) rep.addParagraph(`🎞️ ${dUtils.getPublicUrlForCdn(embed.video.url)}`)
|
||||||
|
|
||||||
|
|
@ -1039,6 +1076,11 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
body = body.split("\n").map(l => "| " + l).join("\n")
|
body = body.split("\n").map(l => "| " + l).join("\n")
|
||||||
html = `<blockquote>${html}</blockquote>`
|
html = `<blockquote>${html}</blockquote>`
|
||||||
|
|
||||||
|
if (isAdditionalImage) {
|
||||||
|
mergeTextEvents([{...rep.get(), body, html, msgtype: "m.notice"}], events, true)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Send as m.notice to apply the usual automated/subtle appearance, showing this wasn't actually typed by the person
|
// Send as m.notice to apply the usual automated/subtle appearance, showing this wasn't actually typed by the person
|
||||||
await addTextEvent(body, html, "m.notice")
|
await addTextEvent(body, html, "m.notice")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,44 @@ test("message2event embeds: author url without name", async t => {
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("message2event embeds: 4 images", async t => {
|
||||||
|
const events = await messageToEvent(data.message_with_embeds.four_images, data.guild.general)
|
||||||
|
t.deepEqual(events, [{
|
||||||
|
$type: "m.room.message",
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "[🔀 Forwarded message]\n» https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "🔀 <em>Forwarded message</em><br><blockquote><a href=\"https://fixupx.com/i/status/2032003668787020046\">https://fixupx.com/i/status/2032003668787020046</a></blockquote>",
|
||||||
|
"m.mentions": {}
|
||||||
|
}, {
|
||||||
|
$type: "m.room.message",
|
||||||
|
msgtype: "m.notice",
|
||||||
|
body: "» | ## ⏺️ AUTOMATON WEST (@AUTOMATON_ENG) https://x.com/AUTOMATON_ENG/status/2032003668787020046"
|
||||||
|
+ "\n» | "
|
||||||
|
+ "\n» | 4chan owner Hiroyuki, Evangelion director Hideaki Anno and GACKT to participate in “humanity’s last non\\-AI made social network”"
|
||||||
|
+ "\n» | ︀︀"
|
||||||
|
+ "\n» | ︀︀[automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/](https://automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/)"
|
||||||
|
+ "\n» | "
|
||||||
|
+ "\n» | **[💬](https://x.com/intent/tweet?in_reply_to=2032003668787020046) 36 [🔁](https://x.com/intent/retweet?tweet_id=2032003668787020046) 212 [❤](https://x.com/intent/like?tweet_id=2032003668787020046) 3\\.0K 👁 131\\.7K **"
|
||||||
|
+ "\n» | "
|
||||||
|
+ "\n» | 📸 https://pbs.twimg.com/media/HDMUyf6bQAM3yts.jpg?name=orig"
|
||||||
|
+ "\n» | — FixupX"
|
||||||
|
+ "\n» | 📸 https://pbs.twimg.com/media/HDMUgxybQAE4FtJ.jpg?name=orig"
|
||||||
|
+ "\n» | 📸 https://pbs.twimg.com/media/HDMUrPobgAAeb90.jpg?name=orig"
|
||||||
|
+ "\n» | 📸 https://pbs.twimg.com/media/HDMUuy5bgAAInj5.jpg?name=orig",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "<blockquote><blockquote><p><strong><a href=\"https://x.com/AUTOMATON_ENG/status/2032003668787020046\">⏺️ AUTOMATON WEST (@AUTOMATON_ENG)</a></strong></p>"
|
||||||
|
+ "<p>4chan owner Hiroyuki, Evangelion director Hideaki Anno and GACKT to participate in “humanity’s last non-AI made social network”"
|
||||||
|
+ "<br>︀︀<br>︀︀<a href=\"https://automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/\">automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/</a>"
|
||||||
|
+ "<br><br><strong><a href=\"https://x.com/intent/tweet?in_reply_to=2032003668787020046\">💬</a> 36 <a href=\"https://x.com/intent/retweet?tweet_id=2032003668787020046\">🔁</a> 212 <a href=\"https://x.com/intent/like?tweet_id=2032003668787020046\">❤</a> 3.0K 👁 131.7K </strong></p>"
|
||||||
|
+ "<p>📸 https://pbs.twimg.com/media/HDMUyf6bQAM3yts.jpg?name=orig</p>— FixupX</blockquote>"
|
||||||
|
+ "<p>📸 https://pbs.twimg.com/media/HDMUgxybQAE4FtJ.jpg?name=orig</p>"
|
||||||
|
+ "<p>📸 https://pbs.twimg.com/media/HDMUrPobgAAeb90.jpg?name=orig</p>"
|
||||||
|
+ "<p>📸 https://pbs.twimg.com/media/HDMUuy5bgAAInj5.jpg?name=orig</p></blockquote>",
|
||||||
|
"m.mentions": {}
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
test("message2event embeds: vx image", async t => {
|
test("message2event embeds: vx image", async t => {
|
||||||
const events = await messageToEvent(data.message_with_embeds.vx_image, data.guild.general)
|
const events = await messageToEvent(data.message_with_embeds.vx_image, data.guild.general)
|
||||||
t.deepEqual(events, [{
|
t.deepEqual(events, [{
|
||||||
|
|
|
||||||
|
|
@ -789,7 +789,7 @@ test("message2event: simple written @mention for matrix user", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "[@ash](https://matrix.to/#/@she_who_brings_destruction:cadence.moe) do you need anything from the store btw as I'm heading there after gym",
|
body: "@ash do you need anything from the store btw as I'm heading there after gym",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `<a href="https://matrix.to/#/@she_who_brings_destruction:cadence.moe">@ash</a> do you need anything from the store btw as I'm heading there after gym`
|
formatted_body: `<a href="https://matrix.to/#/@she_who_brings_destruction:cadence.moe">@ash</a> do you need anything from the store btw as I'm heading there after gym`
|
||||||
}])
|
}])
|
||||||
|
|
@ -838,7 +838,7 @@ test("message2event: many written @mentions for matrix users", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "[@Cadence](https://matrix.to/#/@cadence:cadence.moe), tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and [@huck](https://matrix.to/#/@huckleton:cadence.moe)",
|
body: "@Cadence, tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and @huck",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `<a href="https://matrix.to/#/@cadence:cadence.moe">@Cadence</a>, tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and <a href="https://matrix.to/#/@huckleton:cadence.moe">@huck</a>`
|
formatted_body: `<a href="https://matrix.to/#/@cadence:cadence.moe">@Cadence</a>, tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and <a href="https://matrix.to/#/@huckleton:cadence.moe">@huck</a>`
|
||||||
}])
|
}])
|
||||||
|
|
@ -890,7 +890,7 @@ test("message2event: written @mentions may match part of the name", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "I wonder if [@cadence](https://matrix.to/#/@secret:cadence.moe) saw this?",
|
body: "I wonder if @cadence saw this?",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `I wonder if <a href="https://matrix.to/#/@secret:cadence.moe">@cadence</a> saw this?`
|
formatted_body: `I wonder if <a href="https://matrix.to/#/@secret:cadence.moe">@cadence</a> saw this?`
|
||||||
}])
|
}])
|
||||||
|
|
@ -941,7 +941,7 @@ test("message2event: written @mentions may match part of the mxid", async t => {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "I wonder if [@huck](https://matrix.to/#/@huckleton:cadence.moe) saw this?",
|
body: "I wonder if @huck saw this?",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `I wonder if <a href="https://matrix.to/#/@huckleton:cadence.moe">@huck</a> saw this?`
|
formatted_body: `I wonder if <a href="https://matrix.to/#/@huckleton:cadence.moe">@huck</a> saw this?`
|
||||||
}])
|
}])
|
||||||
|
|
@ -962,6 +962,36 @@ test("message2event: written @mentions do not match in URLs", async t => {
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("message2event: written @mentions do not match in inline code", async t => {
|
||||||
|
const events = await messageToEvent({
|
||||||
|
...data.message.advanced_written_at_mention_for_matrix,
|
||||||
|
content: "`public @Nullable EntityType<?>`"
|
||||||
|
}, data.guild.general, {}, {})
|
||||||
|
t.deepEqual(events, [{
|
||||||
|
$type: "m.room.message",
|
||||||
|
"m.mentions": {},
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "`public @Nullable EntityType<?>`",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: `<code>public @Nullable EntityType<?></code>`
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("message2event: written @mentions do not match in code block", async t => {
|
||||||
|
const events = await messageToEvent({
|
||||||
|
...data.message.advanced_written_at_mention_for_matrix,
|
||||||
|
content: "```java\npublic @Nullable EntityType<?>\n```"
|
||||||
|
}, data.guild.general, {}, {})
|
||||||
|
t.deepEqual(events, [{
|
||||||
|
$type: "m.room.message",
|
||||||
|
"m.mentions": {},
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "```java\npublic @Nullable EntityType<?>\n```",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: `<pre><code class="language-java">public @Nullable EntityType<?></code></pre>`
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
test("message2event: entire message may match elaborate display name", async t => {
|
test("message2event: entire message may match elaborate display name", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const events = await messageToEvent({
|
const events = await messageToEvent({
|
||||||
|
|
@ -1007,7 +1037,7 @@ test("message2event: entire message may match elaborate display name", async t =
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "[@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆](https://matrix.to/#/@wa:cadence.moe)",
|
body: "@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆",
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `<a href="https://matrix.to/#/@wa:cadence.moe">@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆</a>`
|
formatted_body: `<a href="https://matrix.to/#/@wa:cadence.moe">@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆</a>`
|
||||||
}])
|
}])
|
||||||
|
|
@ -1084,7 +1114,7 @@ test("message2event: multiple attachments are combined into the same event where
|
||||||
formatted_body: "hey"
|
formatted_body: "hey"
|
||||||
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
||||||
+ `<br><blockquote>📸 Uploaded SPOILER file: <a href="https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg">https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg</a> (38 KB)</blockquote>`
|
+ `<br><blockquote>📸 Uploaded SPOILER file: <a href="https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg">https://bridge.example.org/download/discordcdn/123/456/SPOILER_secret.jpg</a> (38 KB)</blockquote>`
|
||||||
+ `<br>📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
+ `📄 Uploaded file: <a href="https://bridge.example.org/download/discordcdn/123/456/789.mega">hey.jpg</a> (100 MB)`
|
||||||
}, {
|
}, {
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
"m.mentions": {},
|
"m.mentions": {},
|
||||||
|
|
@ -1538,6 +1568,28 @@ test("message2event: vc invite event renders embed with room link", async t => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("message2event: expired/invalid invites are sent as-is", async t => {
|
||||||
|
const events = await messageToEvent({content: "https://discord.gg/placeholder?event=1381190945646710824"}, {}, {}, {
|
||||||
|
snow: {
|
||||||
|
invite: {
|
||||||
|
async getInvite() {
|
||||||
|
throw new Error(`{"message": "Unknown Invite", "code": 10006}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.deepEqual(events, [
|
||||||
|
{
|
||||||
|
$type: "m.room.message",
|
||||||
|
body: "https://discord.gg/placeholder?event=1381190945646710824",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "<a href=\"https://discord.gg/placeholder?event=1381190945646710824\">https://discord.gg/placeholder?event=1381190945646710824</a>",
|
||||||
|
"m.mentions": {},
|
||||||
|
msgtype: "m.text",
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
test("message2event: channel links are converted even inside lists (parser post-processer descends into list items)", async t => {
|
test("message2event: channel links are converted even inside lists (parser post-processer descends into list items)", async t => {
|
||||||
let called = 0
|
let called = 0
|
||||||
const events = await messageToEvent({
|
const events = await messageToEvent({
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const assert = require("assert")
|
const assert = require("assert")
|
||||||
const data = require("../../../test/data")
|
const data = require("../../../test/data")
|
||||||
const {userToSimName, webhookAuthorToSimName} = require("./user-to-mxid")
|
const {userToSimName, webhookAuthorToSimName} = require("./user-to-mxid")
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,11 @@ class DiscordClient {
|
||||||
/** @type {Map<string, Array<string>>} */
|
/** @type {Map<string, Array<string>>} */
|
||||||
this.guildChannelMap = new Map()
|
this.guildChannelMap = new Map()
|
||||||
if (listen !== "no") {
|
if (listen !== "no") {
|
||||||
this.cloud.on("event", message => discordPackets.onPacket(this, message, listen))
|
this.cloud.on("event", message => {
|
||||||
|
process.nextTick(() => {
|
||||||
|
discordPackets.onPacket(this, message, listen)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const addEventLogger = (eventName, logName) => {
|
const addEventLogger = (eventName, logName) => {
|
||||||
|
|
|
||||||
9
src/db/migrations/0035-role-default.sql
Normal file
9
src/db/migrations/0035-role-default.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
CREATE TABLE "role_default" (
|
||||||
|
"guild_id" TEXT NOT NULL,
|
||||||
|
"role_id" TEXT NOT NULL,
|
||||||
|
PRIMARY KEY ("guild_id", "role_id")
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
5
src/db/orm-defs.d.ts
vendored
5
src/db/orm-defs.d.ts
vendored
|
|
@ -104,6 +104,11 @@ export type Models = {
|
||||||
historical_room_index: number
|
historical_room_index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
role_default: {
|
||||||
|
guild_id: string
|
||||||
|
role_id: string
|
||||||
|
}
|
||||||
|
|
||||||
room_upgrade_pending: {
|
room_upgrade_pending: {
|
||||||
new_room_id: string
|
new_room_id: string
|
||||||
old_room_id: string
|
old_room_id: string
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const sharp = require("sharp")
|
||||||
const api = sync.require("../../matrix/api")
|
const api = sync.require("../../matrix/api")
|
||||||
/** @type {import("../../matrix/mreq")} */
|
/** @type {import("../../matrix/mreq")} */
|
||||||
const mreq = sync.require("../../matrix/mreq")
|
const mreq = sync.require("../../matrix/mreq")
|
||||||
const streamMimeType = require("stream-mime-type")
|
const {streamType} = require("@cloudrac3r/stream-type")
|
||||||
|
|
||||||
const WIDTH = 160
|
const WIDTH = 160
|
||||||
const HEIGHT = 160
|
const HEIGHT = 160
|
||||||
|
|
@ -26,13 +26,13 @@ async function getAndResizeSticker(mxc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const streamIn = Readable.fromWeb(res.body)
|
const streamIn = Readable.fromWeb(res.body)
|
||||||
const { stream, mime } = await streamMimeType.getMimeType(streamIn)
|
const {streamThrough, type} = await streamType(streamIn)
|
||||||
const animated = ["image/gif", "image/webp"].includes(mime)
|
const animated = ["image/gif", "image/webp"].includes(type)
|
||||||
|
|
||||||
const transformer = sharp({animated: animated})
|
const transformer = sharp({animated: animated})
|
||||||
.resize(WIDTH, HEIGHT, {fit: "inside", background: {r: 0, g: 0, b: 0, alpha: 0}})
|
.resize(WIDTH, HEIGHT, {fit: "inside", background: {r: 0, g: 0, b: 0, alpha: 0}})
|
||||||
.webp()
|
.webp()
|
||||||
stream.pipe(transformer)
|
streamThrough.pipe(transformer)
|
||||||
return Readable.toWeb(transformer)
|
return Readable.toWeb(transformer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const {pipeline} = require("stream").promises
|
||||||
const sharp = require("sharp")
|
const sharp = require("sharp")
|
||||||
const {GIFrame} = require("@cloudrac3r/giframe")
|
const {GIFrame} = require("@cloudrac3r/giframe")
|
||||||
const {PNG} = require("@cloudrac3r/pngjs")
|
const {PNG} = require("@cloudrac3r/pngjs")
|
||||||
const streamMimeType = require("stream-mime-type")
|
const {streamType} = require("@cloudrac3r/stream-type")
|
||||||
|
|
||||||
const SIZE = 48
|
const SIZE = 48
|
||||||
const RESULT_WIDTH = 400
|
const RESULT_WIDTH = 400
|
||||||
|
|
@ -54,11 +54,11 @@ async function compositeMatrixEmojis(mxcs, mxcDownloader) {
|
||||||
* @returns {Promise<Buffer | undefined>} Uncompressed PNG image
|
* @returns {Promise<Buffer | undefined>} Uncompressed PNG image
|
||||||
*/
|
*/
|
||||||
async function convertImageStream(streamIn, stopStream) {
|
async function convertImageStream(streamIn, stopStream) {
|
||||||
const {stream, mime} = await streamMimeType.getMimeType(streamIn)
|
const {streamThrough, type} = await streamType(streamIn)
|
||||||
assert(["image/png", "image/jpeg", "image/webp", "image/gif", "image/apng"].includes(mime), `Mime type ${mime} is impossible for emojis`)
|
assert(["image/png", "image/jpeg", "image/webp", "image/gif", "image/apng"].includes(type), `Mime type ${type} is impossible for emojis`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mime === "image/png" || mime === "image/jpeg" || mime === "image/webp") {
|
if (type === "image/png" || type === "image/jpeg" || type === "image/webp") {
|
||||||
/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
|
/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
|
||||||
const result = await new Promise((resolve, reject) => {
|
const result = await new Promise((resolve, reject) => {
|
||||||
const transformer = sharp()
|
const transformer = sharp()
|
||||||
|
|
@ -70,15 +70,15 @@ async function convertImageStream(streamIn, stopStream) {
|
||||||
resolve({info, buffer})
|
resolve({info, buffer})
|
||||||
})
|
})
|
||||||
pipeline(
|
pipeline(
|
||||||
stream,
|
streamThrough,
|
||||||
transformer
|
transformer
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
return result.buffer
|
return result.buffer
|
||||||
|
|
||||||
} else if (mime === "image/gif") {
|
} else if (type === "image/gif") {
|
||||||
const giframe = new GIFrame(0)
|
const giframe = new GIFrame(0)
|
||||||
stream.on("data", chunk => {
|
streamThrough.on("data", chunk => {
|
||||||
giframe.feed(chunk)
|
giframe.feed(chunk)
|
||||||
})
|
})
|
||||||
const frame = await giframe.getFrame()
|
const frame = await giframe.getFrame()
|
||||||
|
|
@ -91,10 +91,10 @@ async function convertImageStream(streamIn, stopStream) {
|
||||||
.toBuffer({resolveWithObject: true})
|
.toBuffer({resolveWithObject: true})
|
||||||
return buffer.data
|
return buffer.data
|
||||||
|
|
||||||
} else if (mime === "image/apng") {
|
} else if (type === "image/apng") {
|
||||||
const png = new PNG({maxFrames: 1})
|
const png = new PNG({maxFrames: 1})
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
stream.pipe(png)
|
streamThrough.pipe(png)
|
||||||
/** @type {Buffer} */ // @ts-ignore
|
/** @type {Buffer} */ // @ts-ignore
|
||||||
const frame = await new Promise(resolve => png.on("parsed", resolve))
|
const frame = await new Promise(resolve => png.on("parsed", resolve))
|
||||||
stopStream()
|
stopStream()
|
||||||
|
|
|
||||||
|
|
@ -4747,17 +4747,17 @@ test("event2message: stickers work", async t => {
|
||||||
messagesToEdit: [],
|
messagesToEdit: [],
|
||||||
messagesToSend: [{
|
messagesToSend: [{
|
||||||
username: "cadence [they]",
|
username: "cadence [they]",
|
||||||
content: "",
|
content: "[get_real2](https://bridge.example.org/download/sticker/cadence.moe/NyMXQFAAdniImbHzsygScbmN/_.webp)",
|
||||||
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU",
|
||||||
attachments: [{id: "0", filename: "get_real2.gif"}],
|
allowed_mentions: {
|
||||||
pendingFiles: [{name: "get_real2.gif", mxc: "mxc://cadence.moe/NyMXQFAAdniImbHzsygScbmN"}]
|
parse: ["users", "roles"]
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("event2message: stickers fetch mimetype from server when mimetype not provided", async t => {
|
test("event2message: stickers fetch mimetype from server when mimetype not provided", async t => {
|
||||||
let called = 0
|
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
await eventToMessage({
|
await eventToMessage({
|
||||||
type: "m.sticker",
|
type: "m.sticker",
|
||||||
|
|
@ -4768,20 +4768,6 @@ test("event2message: stickers fetch mimetype from server when mimetype not provi
|
||||||
},
|
},
|
||||||
event_id: "$mL-eEVWCwOvFtoOiivDP7gepvf-fTYH6_ioK82bWDI0",
|
event_id: "$mL-eEVWCwOvFtoOiivDP7gepvf-fTYH6_ioK82bWDI0",
|
||||||
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
|
||||||
}, {}, {}, {
|
|
||||||
api: {
|
|
||||||
async getMedia(mxc, options) {
|
|
||||||
called++
|
|
||||||
t.equal(mxc, "mxc://cadence.moe/ybOWQCaXysnyUGuUCaQlTGJf")
|
|
||||||
t.equal(options.method, "HEAD")
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
headers: new Map([
|
|
||||||
["content-type", "image/gif"]
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
ensureJoined: [],
|
ensureJoined: [],
|
||||||
|
|
@ -4789,48 +4775,14 @@ test("event2message: stickers fetch mimetype from server when mimetype not provi
|
||||||
messagesToEdit: [],
|
messagesToEdit: [],
|
||||||
messagesToSend: [{
|
messagesToSend: [{
|
||||||
username: "cadence [they]",
|
username: "cadence [they]",
|
||||||
content: "",
|
content: "[YESYESYES](https://bridge.example.org/download/sticker/cadence.moe/ybOWQCaXysnyUGuUCaQlTGJf/_.webp)",
|
||||||
avatar_url: undefined,
|
avatar_url: undefined,
|
||||||
attachments: [{id: "0", filename: "YESYESYES.gif"}],
|
allowed_mentions: {
|
||||||
pendingFiles: [{name: "YESYESYES.gif", mxc: "mxc://cadence.moe/ybOWQCaXysnyUGuUCaQlTGJf"}]
|
parse: ["users", "roles"]
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
t.equal(called, 1, "sticker headers should be fetched")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("event2message: stickers with unknown mimetype are not allowed", async t => {
|
|
||||||
let called = 0
|
|
||||||
try {
|
|
||||||
await eventToMessage({
|
|
||||||
type: "m.sticker",
|
|
||||||
sender: "@cadence:cadence.moe",
|
|
||||||
content: {
|
|
||||||
body: "something",
|
|
||||||
url: "mxc://cadence.moe/ybOWQCaXysnyUGuUCaQlTGJe"
|
|
||||||
},
|
|
||||||
event_id: "$mL-eEVWCwOvFtoOiivDP7gepvf-fTYH6_ioK82bWDI0",
|
|
||||||
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
|
|
||||||
}, {}, {}, {
|
|
||||||
api: {
|
|
||||||
async getMedia(mxc, options) {
|
|
||||||
called++
|
|
||||||
t.equal(mxc, "mxc://cadence.moe/ybOWQCaXysnyUGuUCaQlTGJe")
|
|
||||||
t.equal(options.method, "HEAD")
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
headers: new Map([
|
|
||||||
["content-type", "application/json"]
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
/* c8 ignore next */
|
|
||||||
t.fail("should throw an error")
|
|
||||||
} catch (e) {
|
|
||||||
t.match(e.toString(), "mimetype")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("event2message: static emojis work", async t => {
|
test("event2message: static emojis work", async t => {
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ async function _actuallyUploadDiscordFileToMxc(url) {
|
||||||
writeRegistration(reg)
|
writeRegistration(reg)
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
e.uploadURL = url
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const assert = require("assert").strict
|
const assert = require("assert").strict
|
||||||
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const Ty = require("../types")
|
const Ty = require("../types")
|
||||||
const {pipeline} = require("stream").promises
|
const {pipeline} = require("stream").promises
|
||||||
const sharp = require("sharp")
|
const sharp = require("sharp")
|
||||||
|
|
@ -262,6 +263,46 @@ const commands = [{
|
||||||
await discord.snow.channel.createThreadWithoutMessage(channelID, {type: 11, name: words.slice(1).join(" ")})
|
await discord.snow.channel.createThreadWithoutMessage(channelID, {type: 11, name: words.slice(1).join(" ")})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}, {
|
||||||
|
aliases: ["invite"],
|
||||||
|
execute: replyctx(
|
||||||
|
async (event, realBody, words, ctx) => {
|
||||||
|
// Guard
|
||||||
|
/** @type {string} */ // @ts-ignore
|
||||||
|
const channelID = select("channel_room", "channel_id", {room_id: event.room_id}).pluck().get()
|
||||||
|
const guildID = discord.channels.get(channelID)?.["guild_id"]
|
||||||
|
if (!guildID) {
|
||||||
|
return api.sendEvent(event.room_id, "m.room.message", {
|
||||||
|
...ctx,
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "This room isn't bridged to the other side."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = discord.guilds.get(guildID)
|
||||||
|
assert(guild)
|
||||||
|
const permissions = dUtils.getPermissions(guild.id, [], guild.roles)
|
||||||
|
if (!dUtils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.CreateInstantInvite)) {
|
||||||
|
return api.sendEvent(event.room_id, "m.room.message", {
|
||||||
|
...ctx,
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "This command creates an invite link to the Discord side. But you aren't allowed to do this, because if you were a Discord user, you wouldn't have the Create Invite permission."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const invite = await discord.snow.channel.createChannelInvite(channelID)
|
||||||
|
const validHours = Math.ceil(invite.max_age / (60 * 60))
|
||||||
|
const validUses =
|
||||||
|
( invite.max_uses === 0 ? "unlimited uses"
|
||||||
|
: invite.max_uses === 1 ? "single-use"
|
||||||
|
: `${invite.max_uses} uses`)
|
||||||
|
return api.sendEvent(event.room_id, "m.room.message", {
|
||||||
|
...ctx,
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: `https://discord.gg/${invite.code}\nValid for next ${validHours} hours, ${validUses}.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {reg, checkRegistration, getTemplateRegistration} = require("./read-registration")
|
const {reg, checkRegistration, getTemplateRegistration} = require("./read-registration")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,17 +54,17 @@ async function onBotMembership(event, api, createRoom) {
|
||||||
assert.equal(event.type, "m.room.member")
|
assert.equal(event.type, "m.room.member")
|
||||||
assert.equal(event.state_key, utils.bot)
|
assert.equal(event.state_key, utils.bot)
|
||||||
|
|
||||||
// Check if an upgrade is pending for this room
|
|
||||||
const newRoomID = event.room_id
|
|
||||||
const oldRoomID = select("room_upgrade_pending", "old_room_id", {new_room_id: newRoomID}).pluck().get()
|
|
||||||
if (!oldRoomID) return false
|
|
||||||
const channelRow = from("channel_room").join("guild_space", "guild_id").where({room_id: oldRoomID}).select("space_id", "guild_id", "channel_id").get()
|
|
||||||
assert(channelRow) // this could only fail if the channel was unbridged or something between upgrade and joining
|
|
||||||
|
|
||||||
// Check if is join/invite
|
|
||||||
if (event.content.membership !== "invite" && event.content.membership !== "join") return false
|
|
||||||
|
|
||||||
return await roomUpgradeSema.request(async () => {
|
return await roomUpgradeSema.request(async () => {
|
||||||
|
// Check if an upgrade is pending for this room
|
||||||
|
const newRoomID = event.room_id
|
||||||
|
const oldRoomID = select("room_upgrade_pending", "old_room_id", {new_room_id: newRoomID}).pluck().get()
|
||||||
|
if (!oldRoomID) return false
|
||||||
|
const channelRow = from("channel_room").join("guild_space", "guild_id").where({room_id: oldRoomID}).select("space_id", "guild_id", "channel_id").get()
|
||||||
|
assert(channelRow) // this could only fail if the channel was unbridged or something between upgrade and joining
|
||||||
|
|
||||||
|
// Check if is join/invite
|
||||||
|
if (event.content.membership !== "invite" && event.content.membership !== "join") return false
|
||||||
|
|
||||||
// If invited, join
|
// If invited, join
|
||||||
if (event.content.membership === "invite") {
|
if (event.content.membership === "invite") {
|
||||||
await api.joinRoom(newRoomID)
|
await api.joinRoom(newRoomID)
|
||||||
|
|
|
||||||
|
|
@ -225,19 +225,6 @@ async function getViaServersQuery(roomID, api) {
|
||||||
return qs
|
return qs
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePermittedMediaHash(mxc) {
|
|
||||||
assert(hasher, "xxhash is not ready yet")
|
|
||||||
const mediaParts = mxc?.match(/^mxc:\/\/([^/]+)\/(\w+)$/)
|
|
||||||
if (!mediaParts) return undefined
|
|
||||||
|
|
||||||
const serverAndMediaID = `${mediaParts[1]}/${mediaParts[2]}`
|
|
||||||
const unsignedHash = hasher.h64(serverAndMediaID)
|
|
||||||
const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
|
|
||||||
db.prepare("INSERT OR IGNORE INTO media_proxy (permitted_hash) VALUES (?)").run(signedHash)
|
|
||||||
|
|
||||||
return serverAndMediaID
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since the introduction of authenticated media, this can no longer just be the /_matrix/media/r0/download URL
|
* Since the introduction of authenticated media, this can no longer just be the /_matrix/media/r0/download URL
|
||||||
* because Discord and Discord users cannot use those URLs. Media now has to be proxied through the bridge.
|
* because Discord and Discord users cannot use those URLs. Media now has to be proxied through the bridge.
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ function renderPath(event, path, locals) {
|
||||||
compile()
|
compile()
|
||||||
fs.watch(path, {persistent: false}, compile)
|
fs.watch(path, {persistent: false}, compile)
|
||||||
fs.watch(join(__dirname, "pug", "includes"), {persistent: false}, compile)
|
fs.watch(join(__dirname, "pug", "includes"), {persistent: false}, compile)
|
||||||
|
fs.watch(join(__dirname, "pug", "fragments"), {persistent: false}, compile)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cb = pugCache.get(path)
|
const cb = pugCache.get(path)
|
||||||
|
|
|
||||||
5
src/web/pug/fragments/default-roles-list.pug
Normal file
5
src/web/pug/fragments/default-roles-list.pug
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
//- locals: guild, guild_id
|
||||||
|
|
||||||
|
include ../includes/default-roles-list.pug
|
||||||
|
+default-roles-list(guild, guild_id)
|
||||||
|
+add-roles-menu(guild, guild_id)
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
extends includes/template.pug
|
extends includes/template.pug
|
||||||
|
include includes/default-roles-list.pug
|
||||||
|
|
||||||
mixin badge-readonly
|
mixin badge-readonly
|
||||||
.s-badge.s-badge__xs.s-badge__icon.s-badge__muted
|
.s-badge.s-badge__xs.s-badge__icon.s-badge__muted
|
||||||
|
|
@ -76,7 +77,7 @@ block body
|
||||||
|
|
||||||
if space_id
|
if space_id
|
||||||
h2.mt48.fs-headline1 Server settings
|
h2.mt48.fs-headline1 Server settings
|
||||||
h3.mt32.fs-category Privacy level
|
h3.mt32.fs-category How Matrix users join
|
||||||
span#privacy-level-loading
|
span#privacy-level-loading
|
||||||
.s-card
|
.s-card
|
||||||
form(hx-post=rel("/api/privacy-level") hx-trigger="change" hx-indicator="#privacy-level-loading" hx-disabled-elt="input")
|
form(hx-post=rel("/api/privacy-level") hx-trigger="change" hx-indicator="#privacy-level-loading" hx-disabled-elt="input")
|
||||||
|
|
@ -105,6 +106,24 @@ block body
|
||||||
p.s-description.m0 Shareable invite links, like Discord
|
p.s-description.m0 Shareable invite links, like Discord
|
||||||
p.s-description.m0 Publicly listed in directory, like Discord server discovery
|
p.s-description.m0 Publicly listed in directory, like Discord server discovery
|
||||||
|
|
||||||
|
h3.mt32.fs-category Default roles
|
||||||
|
.s-card
|
||||||
|
form(method="post" action=rel("/api/default-roles") hx-post=rel("/api/default-roles") hx-indicator="#add-role-loading" hx-target="#default-roles-list" hx-select="#default-roles-list" hx-swap="outerHTML")#default-roles
|
||||||
|
input(type="hidden" name="guild_id" value=guild_id)
|
||||||
|
.d-flex.fw-wrap.g4
|
||||||
|
.s-tag.s-tag__md.fs-body1.s-tag__required @everyone
|
||||||
|
|
||||||
|
+default-roles-list(guild, guild_id)
|
||||||
|
|
||||||
|
button(type="button" popovertarget="role-add").s-btn__dropdown.s-tag.s-tag__md.fs-body1.p0
|
||||||
|
.s-tag--dismiss.m1
|
||||||
|
!= icons.Icons.IconPlusSm
|
||||||
|
|
||||||
|
#role-add.s-popover(popover style="display: revert").ws2.px0.py4.bs-lg.overflow-visible
|
||||||
|
.s-popover--arrow.s-popover--arrow__tc
|
||||||
|
+add-roles-menu(guild, guild_id)
|
||||||
|
p.fc-medium.mb0.mt8 Matrix users will start with these roles. If your main channels are gated by a role, use this to let Matrix users skip the gate.
|
||||||
|
|
||||||
h3.mt32.fs-category Features
|
h3.mt32.fs-category Features
|
||||||
.s-card.d-grid.px0.g16
|
.s-card.d-grid.px0.g16
|
||||||
form.d-flex.ai-center.g16
|
form.d-flex.ai-center.g16
|
||||||
|
|
|
||||||
19
src/web/pug/includes/default-roles-list.pug
Normal file
19
src/web/pug/includes/default-roles-list.pug
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
mixin default-roles-list(guild, guild_id)
|
||||||
|
#default-roles-list(style="display: contents")
|
||||||
|
each roleID in select("role_default", "role_id", {guild_id}).pluck().all()
|
||||||
|
- let r = guild.roles.find(r => r.id === roleID)
|
||||||
|
if r
|
||||||
|
.s-tag.s-tag__md.fs-body1= r.name
|
||||||
|
span(id=`role-loading-${roleID}`)
|
||||||
|
button(name="remove_role" value=roleID hx-post="api/default-roles" hx-trigger="click consume" hx-indicator=`#role-loading-${roleID}`).s-tag--dismiss
|
||||||
|
!= icons.Icons.IconClearSm
|
||||||
|
|
||||||
|
mixin add-roles-menu(guild, guild_id)
|
||||||
|
ul.s-menu(role="menu" hx-swap-oob="true").overflow-y-auto.overflow-x-hidden#add-roles-menu
|
||||||
|
li.s-menu--title.d-flex(role="separator") Select role
|
||||||
|
span#add-role-loading
|
||||||
|
each r in guild.roles.sort((a, b) => b.position - a.position)
|
||||||
|
if r.id !== guild_id && !r.managed
|
||||||
|
- let selected = !!select("role_default", "role_id", {guild_id, role_id: r.id}).get()
|
||||||
|
li(role="menuitem")
|
||||||
|
button(name="toggle_role" value=r.id class={"is-selected": selected}).s-block-link.s-block-link__left= r.name
|
||||||
|
|
@ -91,6 +91,19 @@ html(lang="en")
|
||||||
.s-btn__dropdown:has(+ :popover-open) {
|
.s-btn__dropdown:has(+ :popover-open) {
|
||||||
background-color: var(--theme-topbar-item-background-hover, var(--black-200)) !important;
|
background-color: var(--theme-topbar-item-background-hover, var(--black-200)) !important;
|
||||||
}
|
}
|
||||||
|
.s-btn__dropdown.s-tag:has(+ :popover-open) .s-tag--dismiss {
|
||||||
|
background-color: var(--black-500) !important;
|
||||||
|
color: var(--black-150) !important;
|
||||||
|
}
|
||||||
|
.s-tag .is-loading {
|
||||||
|
margin-right: -4px;
|
||||||
|
}
|
||||||
|
.s-tag .is-loading + .s-tag--dismiss {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
a.s-block-link, .s-block-link {
|
||||||
|
--_bl-bs-color: var(--green-400);
|
||||||
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body.theme-system .s-popover {
|
body.theme-system .s-popover {
|
||||||
--_po-bg: var(--black-100);
|
--_po-bg: var(--black-100);
|
||||||
|
|
@ -141,11 +154,15 @@ html(lang="en")
|
||||||
//- Guild list popover
|
//- Guild list popover
|
||||||
script.
|
script.
|
||||||
document.querySelectorAll("[popovertarget]").forEach(e => {
|
document.querySelectorAll("[popovertarget]").forEach(e => {
|
||||||
e.addEventListener("click", () => {
|
const target = document.getElementById(e.getAttribute("popovertarget"))
|
||||||
const rect = e.getBoundingClientRect()
|
e.addEventListener("click", calculate)
|
||||||
const t = `:popover-open { position: absolute; top: ${Math.floor(rect.bottom)}px; left: ${Math.floor(rect.left + rect.width / 2)}px; width: ${Math.floor(rect.width)}px; transform: translateX(-50%); box-sizing: content-box; margin: 0 }`
|
target.addEventListener("toggle", calculate)
|
||||||
|
function calculate() {
|
||||||
|
const buttonRect = e.getBoundingClientRect()
|
||||||
|
const targetRect = target.getBoundingClientRect()
|
||||||
|
const t = `:popover-open { position: absolute; top: ${Math.floor(buttonRect.bottom + window.scrollY)}px; left: ${Math.floor(Math.max(targetRect.width / 2, buttonRect.left + buttonRect.width / 2))}px; width: ${Math.floor(buttonRect.width)}px; transform: translateX(-50%); box-sizing: content-box; margin: 0 }`
|
||||||
document.styleSheets[0].insertRule(t, document.styleSheets[0].cssRules.length)
|
document.styleSheets[0].insertRule(t, document.styleSheets[0].cssRules.length)
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
//- Prevent default
|
//- Prevent default
|
||||||
script.
|
script.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const assert = require("assert").strict
|
const assert = require("assert").strict
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {router} = require("../../../test/web")
|
const {router} = require("../../../test/web")
|
||||||
const {_cache} = require("./download-discord")
|
const {_cache} = require("./download-discord")
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const {convertImageStream} = require("../../m2d/converters/emoji-sheet")
|
const {convertImageStream} = require("../../m2d/converters/emoji-sheet")
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {router} = require("../../../test/web")
|
const {router} = require("../../../test/web")
|
||||||
const streamWeb = require("stream/web")
|
const streamWeb = require("stream/web")
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ const assert = require("assert/strict")
|
||||||
const {z} = require("zod")
|
const {z} = require("zod")
|
||||||
const {defineEventHandler, createError, readValidatedBody, getRequestHeader, setResponseHeader, sendRedirect, H3Event} = require("h3")
|
const {defineEventHandler, createError, readValidatedBody, getRequestHeader, setResponseHeader, sendRedirect, H3Event} = require("h3")
|
||||||
|
|
||||||
const {as, db, sync, select} = require("../../passthrough")
|
const {as, db, sync, select, discord} = require("../../passthrough")
|
||||||
|
|
||||||
/** @type {import("../auth")} */
|
/** @type {import("../auth")} */
|
||||||
const auth = sync.require("../auth")
|
const auth = sync.require("../auth")
|
||||||
|
/** @type {import("../pug-sync")} */
|
||||||
|
const pugSync = sync.require("../pug-sync")
|
||||||
/** @type {import("../../d2m/actions/set-presence")} */
|
/** @type {import("../../d2m/actions/set-presence")} */
|
||||||
const setPresence = sync.require("../../d2m/actions/set-presence")
|
const setPresence = sync.require("../../d2m/actions/set-presence")
|
||||||
|
|
||||||
|
|
@ -20,6 +22,14 @@ function getCreateSpace(event) {
|
||||||
return event.context.createSpace || sync.require("../../d2m/actions/create-space")
|
return event.context.createSpace || sync.require("../../d2m/actions/create-space")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
defaultRoles: z.object({
|
||||||
|
guild_id: z.string(),
|
||||||
|
toggle_role: z.string().optional(),
|
||||||
|
remove_role: z.string().optional()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Options
|
* @typedef Options
|
||||||
* @prop {(value: string?) => number} transform
|
* @prop {(value: string?) => number} transform
|
||||||
|
|
@ -94,3 +104,36 @@ as.router.post("/api/privacy-level", defineToggle("privacy_level", {
|
||||||
await createSpace.syncSpaceFully(guildID) // this is inefficient but OK to call infrequently on user request
|
await createSpace.syncSpaceFully(guildID) // this is inefficient but OK to call infrequently on user request
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
as.router.post("/api/default-roles", defineEventHandler(async event => {
|
||||||
|
const parsedBody = await readValidatedBody(event, schema.defaultRoles.parse)
|
||||||
|
|
||||||
|
const managed = await auth.getManagedGuilds(event)
|
||||||
|
const guildID = parsedBody.guild_id
|
||||||
|
if (!managed.has(guildID)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"})
|
||||||
|
|
||||||
|
const roleID = parsedBody.toggle_role || parsedBody.remove_role
|
||||||
|
assert(roleID)
|
||||||
|
assert.notEqual(guildID, roleID) // the @everyone role is always default
|
||||||
|
|
||||||
|
const guild = discord.guilds.get(guildID)
|
||||||
|
assert(guild)
|
||||||
|
|
||||||
|
let shouldRemove = !!parsedBody.remove_role
|
||||||
|
if (!shouldRemove) {
|
||||||
|
shouldRemove = !!select("role_default", "role_id", {guild_id: guildID, role_id: roleID}).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRemove) {
|
||||||
|
db.prepare("DELETE FROM role_default WHERE guild_id = ? AND role_id = ?").run(guildID, roleID)
|
||||||
|
} else {
|
||||||
|
assert(guild.roles.find(r => r.id === roleID))
|
||||||
|
db.prepare("INSERT OR IGNORE INTO role_default (guild_id, role_id) VALUES (?, ?)").run(guildID, roleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getRequestHeader(event, "HX-Request")) {
|
||||||
|
return pugSync.render(event, "fragments/default-roles-list.pug", {guild, guild_id: guildID})
|
||||||
|
} else {
|
||||||
|
return sendRedirect(event, `/guild?guild_id=${guildID}`, 302)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
const {select} = require("../../passthrough")
|
const {select} = require("../../passthrough")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const DiscordTypes = require("discord-api-types/v10")
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
const {_getPosition} = require("./guild")
|
const {_getPosition} = require("./guild")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
const {select, db} = require("../../passthrough")
|
const {select, db} = require("../../passthrough")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const DiscordTypes = require("discord-api-types/v10")
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const assert = require("assert/strict")
|
const assert = require("assert/strict")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const tryToCatch = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const {router} = require("../../../test/web")
|
const {router} = require("../../../test/web")
|
||||||
|
|
||||||
|
|
|
||||||
135
test/data.js
135
test/data.js
|
|
@ -5067,6 +5067,141 @@ module.exports = {
|
||||||
pinned: false,
|
pinned: false,
|
||||||
mention_everyone: false,
|
mention_everyone: false,
|
||||||
tts: false
|
tts: false
|
||||||
|
},
|
||||||
|
four_images: {
|
||||||
|
type: 0,
|
||||||
|
content: "",
|
||||||
|
mentions: [],
|
||||||
|
mention_roles: [],
|
||||||
|
attachments: [],
|
||||||
|
embeds: [],
|
||||||
|
timestamp: "2026-03-12T18:00:50.737000+00:00",
|
||||||
|
edited_timestamp: null,
|
||||||
|
flags: 16384,
|
||||||
|
components: [],
|
||||||
|
id: "1481713598278533241",
|
||||||
|
channel_id: "687028734322147344",
|
||||||
|
author: {
|
||||||
|
id: "112760500130975744",
|
||||||
|
username: "minimus",
|
||||||
|
avatar: "a_a354b9eaff512485b49c82b13691b941",
|
||||||
|
discriminator: "0",
|
||||||
|
public_flags: 512,
|
||||||
|
flags: 512,
|
||||||
|
banner: null,
|
||||||
|
accent_color: null,
|
||||||
|
global_name: "minimus",
|
||||||
|
avatar_decoration_data: null,
|
||||||
|
collectibles: null,
|
||||||
|
display_name_styles: { font_id: 11, effect_id: 5, colors: [ 6106655 ] },
|
||||||
|
banner_color: null,
|
||||||
|
clan: null,
|
||||||
|
primary_guild: null
|
||||||
|
},
|
||||||
|
pinned: false,
|
||||||
|
mention_everyone: false,
|
||||||
|
tts: false,
|
||||||
|
message_reference: {
|
||||||
|
type: 1,
|
||||||
|
channel_id: "637339857118822430",
|
||||||
|
message_id: "1481696763483258891",
|
||||||
|
guild_id: "408573045540651009"
|
||||||
|
},
|
||||||
|
message_snapshots: [
|
||||||
|
{
|
||||||
|
message: {
|
||||||
|
type: 0,
|
||||||
|
content: "https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
mentions: [],
|
||||||
|
mention_roles: [],
|
||||||
|
attachments: [],
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
type: "rich",
|
||||||
|
url: "https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
description: "4chan owner Hiroyuki, Evangelion director Hideaki Anno and GACKT to participate in “humanity’s last non\\-AI made social network”\n" +
|
||||||
|
"︀︀\n" +
|
||||||
|
"︀︀[automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/](https://automaton-media.com/en/news/4chan-owner-hiroyuki-evangelion-director-hideaki-anno-and-gackt-to-participate-in-humanitys-last-non-ai-made-social-network/)\n" +
|
||||||
|
"\n" +
|
||||||
|
"**[💬](https://x.com/intent/tweet?in_reply_to=2032003668787020046) 36 [🔁](https://x.com/intent/retweet?tweet_id=2032003668787020046) 212 [❤](https://x.com/intent/like?tweet_id=2032003668787020046) 3\\.0K 👁 131\\.7K **",
|
||||||
|
color: 6513919,
|
||||||
|
timestamp: "2026-03-12T08:00:02+00:00",
|
||||||
|
author: {
|
||||||
|
name: "AUTOMATON WEST (@AUTOMATON_ENG)",
|
||||||
|
url: "https://x.com/AUTOMATON_ENG/status/2032003668787020046",
|
||||||
|
icon_url: "https://pbs.twimg.com/profile_images/1353559126693961729/pz-WVnDc_200x200.jpg",
|
||||||
|
proxy_icon_url: "https://images-ext-1.discordapp.net/external/1OzGhjvZTRstTxM38_7pqHXlmdbMddqh1F8R0-WrKqw/https/pbs.twimg.com/profile_images/1353559126693961729/pz-WVnDc_200x200.jpg"
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
url: "https://pbs.twimg.com/media/HDMUyf6bQAM3yts.jpg?name=orig",
|
||||||
|
proxy_url: "https://images-ext-1.discordapp.net/external/NkNgp2SyY1OCH9IdS8hqsUqbnbrp3A9oLNwYusVVCVQ/%3Fname%3Dorig/https/pbs.twimg.com/media/HDMUyf6bQAM3yts.jpg",
|
||||||
|
width: 872,
|
||||||
|
height: 886,
|
||||||
|
content_type: "image/jpeg",
|
||||||
|
placeholder: "6vcFFwL6R3lye2V3l1mIl5l3WPN5FZ8H",
|
||||||
|
placeholder_version: 1,
|
||||||
|
flags: 0
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
text: "FixupX",
|
||||||
|
icon_url: "https://assets.fxembed.com/logos/fixupx64.png",
|
||||||
|
proxy_icon_url: "https://images-ext-1.discordapp.net/external/LwQ70Uiqfu0OCN4ZbA4f482TGCgQa-xGsnUFYfhIgYA/https/assets.fxembed.com/logos/fixupx64.png"
|
||||||
|
},
|
||||||
|
content_scan_version: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "rich",
|
||||||
|
url: "https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
image: {
|
||||||
|
url: "https://pbs.twimg.com/media/HDMUgxybQAE4FtJ.jpg?name=orig",
|
||||||
|
proxy_url: "https://images-ext-1.discordapp.net/external/Rquh1ec-tG9hMqdHqIVSphO7zf5B5Fg_7yTWhCjlsek/%3Fname%3Dorig/https/pbs.twimg.com/media/HDMUgxybQAE4FtJ.jpg",
|
||||||
|
width: 1114,
|
||||||
|
height: 991,
|
||||||
|
content_type: "image/jpeg",
|
||||||
|
placeholder: "JQgKDoL3epZ8ZIdnlmmHZ4d4CIGmUEc=",
|
||||||
|
placeholder_version: 1,
|
||||||
|
flags: 0
|
||||||
|
},
|
||||||
|
content_scan_version: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "rich",
|
||||||
|
url: "https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
image: {
|
||||||
|
url: "https://pbs.twimg.com/media/HDMUrPobgAAeb90.jpg?name=orig",
|
||||||
|
proxy_url: "https://images-ext-1.discordapp.net/external/XrkhHNH3CvlZYvjkdykVnf-_xdz6HWX8uwesoAwwSfY/%3Fname%3Dorig/https/pbs.twimg.com/media/HDMUrPobgAAeb90.jpg",
|
||||||
|
width: 944,
|
||||||
|
height: 954,
|
||||||
|
content_type: "image/jpeg",
|
||||||
|
placeholder: "m/cJDwCbV0mfaoZzlihqeXdqCVN9A6oD",
|
||||||
|
placeholder_version: 1,
|
||||||
|
flags: 0
|
||||||
|
},
|
||||||
|
content_scan_version: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "rich",
|
||||||
|
url: "https://fixupx.com/i/status/2032003668787020046",
|
||||||
|
image: {
|
||||||
|
url: "https://pbs.twimg.com/media/HDMUuy5bgAAInj5.jpg?name=orig",
|
||||||
|
proxy_url: "https://images-ext-1.discordapp.net/external/lO-5hBMU9bGH13Ax9xum2T2Mg0ATdv0b6BEx_VeVi80/%3Fname%3Dorig/https/pbs.twimg.com/media/HDMUuy5bgAAInj5.jpg",
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
content_type: "image/jpeg",
|
||||||
|
placeholder: "tfcJDIK3mIl1eIiPdY23dX9b9w==",
|
||||||
|
placeholder_version: 1,
|
||||||
|
flags: 0
|
||||||
|
},
|
||||||
|
content_scan_version: 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
timestamp: "2026-03-12T16:53:57.009000+00:00",
|
||||||
|
edited_timestamp: null,
|
||||||
|
flags: 0,
|
||||||
|
components: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
message_with_components: {
|
message_with_components: {
|
||||||
|
|
|
||||||
36
test/test.js
36
test/test.js
|
|
@ -6,31 +6,29 @@ const sqlite = require("better-sqlite3")
|
||||||
const {Writable} = require("stream")
|
const {Writable} = require("stream")
|
||||||
const migrate = require("../src/db/migrate")
|
const migrate = require("../src/db/migrate")
|
||||||
const HeatSync = require("heatsync")
|
const HeatSync = require("heatsync")
|
||||||
const {test, extend} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const data = require("./data")
|
const data = require("./data")
|
||||||
const {green} = require("ansi-colors")
|
const {green} = require("ansi-colors")
|
||||||
|
const mixin = require("@cloudrac3r/mixin-deep")
|
||||||
|
|
||||||
const passthrough = require("../src/passthrough")
|
const passthrough = require("../src/passthrough")
|
||||||
const db = new sqlite(":memory:")
|
const db = new sqlite(":memory:")
|
||||||
|
|
||||||
const {reg} = require("../src/matrix/read-registration")
|
const registration = require("../src/matrix/read-registration")
|
||||||
reg.ooye.discord_token = "Njg0MjgwMTkyNTUzODQ0NzQ3.Xl3zlw.baby"
|
registration.reg = mixin(registration.getTemplateRegistration("cadence.moe"), {
|
||||||
reg.ooye.server_origin = "https://matrix.cadence.moe" // so that tests will pass even when hard-coded
|
id: "baby",
|
||||||
reg.ooye.server_name = "cadence.moe"
|
url: "http://localhost:6693",
|
||||||
reg.ooye.namespace_prefix = "_ooye_"
|
as_token: "don't actually take authenticated actions on the server",
|
||||||
reg.sender_localpart = "_ooye_bot"
|
hs_token: "don't actually take authenticated actions on the server",
|
||||||
reg.id = "baby"
|
ooye: {
|
||||||
reg.as_token = "don't actually take authenticated actions on the server"
|
server_origin: "https://matrix.cadence.moe",
|
||||||
reg.hs_token = "don't actually take authenticated actions on the server"
|
bridge_origin: "https://bridge.example.org",
|
||||||
reg.namespaces = {
|
discord_token: "Njg0MjgwMTkyNTUzODQ0NzQ3.Xl3zlw.baby",
|
||||||
users: [{regex: "@_ooye_.*:cadence.moe", exclusive: true}],
|
discord_client_secret: "baby",
|
||||||
aliases: [{regex: "#_ooye_.*:cadence.moe", exclusive: true}]
|
web_password: "password123",
|
||||||
}
|
time_zone: "Pacific/Auckland",
|
||||||
reg.ooye.bridge_origin = "https://bridge.example.org"
|
}
|
||||||
reg.ooye.time_zone = "Pacific/Auckland"
|
})
|
||||||
reg.ooye.max_file_size = 5000000
|
|
||||||
reg.ooye.web_password = "password123"
|
|
||||||
reg.ooye.include_user_id_in_mxid = false
|
|
||||||
|
|
||||||
const sync = new HeatSync({watchFS: false})
|
const sync = new HeatSync({watchFS: false})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue