Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ee943c4284 | ||
|
337b3709d6 | ||
|
eb318c678f | ||
|
081df6beb7 | ||
|
ab911b48b5 | ||
|
8cb3491086 | ||
|
ee794d140f | ||
|
a00542b61b | ||
|
041a13c9d3 | ||
|
24aa90bd9c | ||
|
c574f53417 | ||
|
92b84a9e94 | ||
|
bbf3c74cb2 | ||
|
93cb51a975 | ||
|
0b4ae729a3 | ||
|
b90392576e | ||
|
e143260891 | ||
|
644c5c4faa | ||
|
8d8cedd72c | ||
|
082ac62eda | ||
|
7923a790e6 | ||
|
1368c25824 | ||
|
d0b3678ad6 |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -37,6 +37,9 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build --standalone
|
run: pnpm build --standalone
|
||||||
|
|
||||||
|
- name: Generate plugin list
|
||||||
|
run: pnpm generatePluginJson dist/plugins.json
|
||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/extension* Vencord.user.css
|
rm -rf dist/extension* Vencord.user.css
|
||||||
|
2
.github/workflows/reportBrokenPlugins.yml
vendored
2
.github/workflows/reportBrokenPlugins.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
export PATH="$PWD/node_modules/.bin:$PATH"
|
export PATH="$PWD/node_modules/.bin:$PATH"
|
||||||
export CHROMIUM_BIN=$(which chromium-browser)
|
export CHROMIUM_BIN=$(which chromium-browser)
|
||||||
|
|
||||||
esbuild test/generateReport.ts > dist/report.mjs
|
esbuild scripts/generateReport.ts > dist/report.mjs
|
||||||
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
|
node dist/report.mjs >> $GITHUB_STEP_SUMMARY
|
||||||
env:
|
env:
|
||||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||||
|
20
CODE_OF_CONDUCT.md
Normal file
20
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
Our community is welcoming to everyone, regardless of their characteristics.
|
||||||
|
|
||||||
|
As such, we expect you to treat everyone with respect and contribute to an open and welcoming community.
|
||||||
|
|
||||||
|
DO
|
||||||
|
- have empathy and be nice to others
|
||||||
|
- be respectful of differing opinions, even if you disagree
|
||||||
|
- give and accept constructive criticism
|
||||||
|
|
||||||
|
DON'T
|
||||||
|
- use offensive or derogatory language
|
||||||
|
- troll or spam
|
||||||
|
- personally attack or harass others
|
||||||
|
|
||||||
|
Repetitive violations of these guidelines might get your access to the repository restricted.
|
||||||
|
|
||||||
|
|
||||||
|
If you feel like a user is violating these guidelines or feel treated unfairly, please refrain from publicly challenging them and instead contact a Moderator on our Discord server or send an email to vendicated+conduct@riseup.net!
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"keywords": [ ],
|
"keywords": [ ],
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/build/build.mjs",
|
"build": "node scripts/build/build.mjs",
|
||||||
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
|
"buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs",
|
||||||
|
"generatePluginJson": "tsx scripts/generatePluginList.ts",
|
||||||
"inject": "node scripts/runInstaller.mjs",
|
"inject": "node scripts/runInstaller.mjs",
|
||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||||
"lint-styles": "stylelint \"src/**/*.css\"",
|
"lint-styles": "stylelint \"src/**/*.css\"",
|
||||||
@ -59,6 +60,7 @@
|
|||||||
"standalone-electron-types": "^1.0.0",
|
"standalone-electron-types": "^1.0.0",
|
||||||
"stylelint": "^14.16.1",
|
"stylelint": "^14.16.1",
|
||||||
"stylelint-config-standard": "^29.0.0",
|
"stylelint-config-standard": "^29.0.0",
|
||||||
|
"tsx": "^3.12.6",
|
||||||
"type-fest": "^3.5.3",
|
"type-fest": "^3.5.3",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
|
290
pnpm-lock.yaml
generated
290
pnpm-lock.yaml
generated
@ -35,6 +35,7 @@ specifiers:
|
|||||||
standalone-electron-types: ^1.0.0
|
standalone-electron-types: ^1.0.0
|
||||||
stylelint: ^14.16.1
|
stylelint: ^14.16.1
|
||||||
stylelint-config-standard: ^29.0.0
|
stylelint-config-standard: ^29.0.0
|
||||||
|
tsx: ^3.12.6
|
||||||
type-fest: ^3.5.3
|
type-fest: ^3.5.3
|
||||||
typescript: ^4.9.4
|
typescript: ^4.9.4
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ devDependencies:
|
|||||||
standalone-electron-types: 1.0.0
|
standalone-electron-types: 1.0.0
|
||||||
stylelint: 14.16.1
|
stylelint: 14.16.1
|
||||||
stylelint-config-standard: 29.0.0_stylelint@14.16.1
|
stylelint-config-standard: 29.0.0_stylelint@14.16.1
|
||||||
|
tsx: 3.12.6
|
||||||
type-fest: 3.5.3
|
type-fest: 3.5.3
|
||||||
typescript: 4.9.4
|
typescript: 4.9.4
|
||||||
|
|
||||||
@ -104,6 +106,27 @@ packages:
|
|||||||
postcss-selector-parser: 6.0.11
|
postcss-selector-parser: 6.0.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@esbuild-kit/cjs-loader/2.4.2:
|
||||||
|
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
|
||||||
|
dependencies:
|
||||||
|
'@esbuild-kit/core-utils': 3.1.0
|
||||||
|
get-tsconfig: 4.4.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@esbuild-kit/core-utils/3.1.0:
|
||||||
|
resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==}
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.17.12
|
||||||
|
source-map-support: 0.5.21
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@esbuild-kit/esm-loader/2.5.5:
|
||||||
|
resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==}
|
||||||
|
dependencies:
|
||||||
|
'@esbuild-kit/core-utils': 3.1.0
|
||||||
|
get-tsconfig: 4.4.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@esbuild/android-arm/0.15.18:
|
/@esbuild/android-arm/0.15.18:
|
||||||
resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==}
|
resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -113,6 +136,96 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-arm/0.17.12:
|
||||||
|
resolution: {integrity: sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-arm64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/android-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/darwin-arm64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/darwin-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/freebsd-arm64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/freebsd-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-arm/0.17.12:
|
||||||
|
resolution: {integrity: sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-arm64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-ia32/0.17.12:
|
||||||
|
resolution: {integrity: sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@esbuild/linux-loong64/0.15.18:
|
/@esbuild/linux-loong64/0.15.18:
|
||||||
resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==}
|
resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -122,6 +235,114 @@ packages:
|
|||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-loong64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-mips64el/0.17.12:
|
||||||
|
resolution: {integrity: sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-ppc64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-riscv64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-s390x/0.17.12:
|
||||||
|
resolution: {integrity: sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/linux-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/netbsd-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/openbsd-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/sunos-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-arm64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-ia32/0.17.12:
|
||||||
|
resolution: {integrity: sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@esbuild/win32-x64/0.17.12:
|
||||||
|
resolution: {integrity: sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/@eslint/eslintrc/1.3.3:
|
/@eslint/eslintrc/1.3.3:
|
||||||
resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
|
resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
@ -563,6 +784,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/buffer-from/1.1.2:
|
||||||
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/buffer/5.7.1:
|
/buffer/5.7.1:
|
||||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1047,6 +1272,36 @@ packages:
|
|||||||
esbuild-windows-arm64: 0.15.18
|
esbuild-windows-arm64: 0.15.18
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/esbuild/0.17.12:
|
||||||
|
resolution: {integrity: sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/android-arm': 0.17.12
|
||||||
|
'@esbuild/android-arm64': 0.17.12
|
||||||
|
'@esbuild/android-x64': 0.17.12
|
||||||
|
'@esbuild/darwin-arm64': 0.17.12
|
||||||
|
'@esbuild/darwin-x64': 0.17.12
|
||||||
|
'@esbuild/freebsd-arm64': 0.17.12
|
||||||
|
'@esbuild/freebsd-x64': 0.17.12
|
||||||
|
'@esbuild/linux-arm': 0.17.12
|
||||||
|
'@esbuild/linux-arm64': 0.17.12
|
||||||
|
'@esbuild/linux-ia32': 0.17.12
|
||||||
|
'@esbuild/linux-loong64': 0.17.12
|
||||||
|
'@esbuild/linux-mips64el': 0.17.12
|
||||||
|
'@esbuild/linux-ppc64': 0.17.12
|
||||||
|
'@esbuild/linux-riscv64': 0.17.12
|
||||||
|
'@esbuild/linux-s390x': 0.17.12
|
||||||
|
'@esbuild/linux-x64': 0.17.12
|
||||||
|
'@esbuild/netbsd-x64': 0.17.12
|
||||||
|
'@esbuild/openbsd-x64': 0.17.12
|
||||||
|
'@esbuild/sunos-x64': 0.17.12
|
||||||
|
'@esbuild/win32-arm64': 0.17.12
|
||||||
|
'@esbuild/win32-ia32': 0.17.12
|
||||||
|
'@esbuild/win32-x64': 0.17.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
/escape-string-regexp/1.0.5:
|
/escape-string-regexp/1.0.5:
|
||||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
@ -1400,6 +1655,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fsevents/2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/function-bind/1.1.1:
|
/function-bind/1.1.1:
|
||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -1411,6 +1674,10 @@ packages:
|
|||||||
pump: 3.0.0
|
pump: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/get-tsconfig/4.4.0:
|
||||||
|
resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/get-value/2.0.6:
|
/get-value/2.0.6:
|
||||||
resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
|
resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -2467,6 +2734,13 @@ packages:
|
|||||||
urix: 0.1.0
|
urix: 0.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/source-map-support/0.5.21:
|
||||||
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/source-map-url/0.4.1:
|
/source-map-url/0.4.1:
|
||||||
resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
|
resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
|
||||||
deprecated: See https://github.com/lydell/source-map-url#deprecated
|
deprecated: See https://github.com/lydell/source-map-url#deprecated
|
||||||
@ -2477,6 +2751,11 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/source-map/0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/spdx-correct/3.1.1:
|
/spdx-correct/3.1.1:
|
||||||
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2739,6 +3018,17 @@ packages:
|
|||||||
typescript: 4.9.4
|
typescript: 4.9.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tsx/3.12.6:
|
||||||
|
resolution: {integrity: sha512-q93WgS3lBdHlPgS0h1i+87Pt6n9K/qULIMNYZo07nSeu2z5QE2CellcAZfofVXBo2tQg9av2ZcRMQ2S2i5oadQ==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
'@esbuild-kit/cjs-loader': 2.4.2
|
||||||
|
'@esbuild-kit/core-utils': 3.1.0
|
||||||
|
'@esbuild-kit/esm-loader': 2.5.5
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/type-check/0.4.0:
|
/type-check/0.4.0:
|
||||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
191
scripts/generatePluginList.ts
Normal file
191
scripts/generatePluginList.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
import { access, readFile } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||||
|
|
||||||
|
interface Dev {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PluginData {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
authors: Dev[];
|
||||||
|
dependencies: string[];
|
||||||
|
hasPatches: boolean;
|
||||||
|
hasCommands: boolean;
|
||||||
|
required: boolean;
|
||||||
|
enabledByDefault: boolean;
|
||||||
|
target: "desktop" | "web" | "dev";
|
||||||
|
}
|
||||||
|
|
||||||
|
const devs = {} as Record<string, Dev>;
|
||||||
|
|
||||||
|
function getName(node: NamedDeclaration) {
|
||||||
|
return node.name && isIdentifier(node.name) ? node.name.text : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasName(node: NamedDeclaration, name: string) {
|
||||||
|
return getName(node) === name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObjectProp(node: ObjectLiteralExpression, name: string) {
|
||||||
|
const prop = node.properties.find(p => hasName(p, name));
|
||||||
|
if (prop && isPropertyAssignment(prop)) return prop.initializer;
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDevs() {
|
||||||
|
const file = createSourceFile("constants.ts", readFileSync("src/utils/constants.ts", "utf8"), ScriptTarget.Latest);
|
||||||
|
|
||||||
|
for (const child of file.getChildAt(0).getChildren()) {
|
||||||
|
if (!isVariableStatement(child)) continue;
|
||||||
|
|
||||||
|
const devsDeclaration = child.declarationList.declarations.find(d => hasName(d, "Devs"));
|
||||||
|
if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue;
|
||||||
|
|
||||||
|
const value = devsDeclaration.initializer.arguments[0];
|
||||||
|
|
||||||
|
if (!isObjectLiteralExpression(value)) return;
|
||||||
|
|
||||||
|
for (const prop of value.properties) {
|
||||||
|
const name = (prop.name as Identifier).text;
|
||||||
|
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
|
||||||
|
|
||||||
|
if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse devs: ${name} is not an object literal`);
|
||||||
|
|
||||||
|
devs[name] = {
|
||||||
|
name: (getObjectProp(value, "name") as StringLiteral).text,
|
||||||
|
id: (getObjectProp(value, "id") as BigIntLiteral).text.slice(0, -1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Could not find Devs constant");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseFile(fileName: string) {
|
||||||
|
const file = createSourceFile(fileName, await readFile(fileName, "utf8"), ScriptTarget.Latest);
|
||||||
|
|
||||||
|
const fail = (reason: string) => {
|
||||||
|
return new Error(`Invalid plugin ${fileName}, because ${reason}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const node of file.getChildAt(0).getChildren()) {
|
||||||
|
if (!isExportAssignment(node) || !isCallExpression(node.expression)) continue;
|
||||||
|
|
||||||
|
const call = node.expression;
|
||||||
|
if (!isIdentifier(call.expression) || call.expression.text !== "definePlugin") continue;
|
||||||
|
|
||||||
|
const pluginObj = node.expression.arguments[0];
|
||||||
|
if (!isObjectLiteralExpression(pluginObj)) throw fail("no object literal passed to definePlugin");
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
hasPatches: false,
|
||||||
|
hasCommands: false,
|
||||||
|
enabledByDefault: false,
|
||||||
|
required: false,
|
||||||
|
} as PluginData;
|
||||||
|
|
||||||
|
for (const prop of pluginObj.properties) {
|
||||||
|
const key = getName(prop);
|
||||||
|
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "name":
|
||||||
|
case "description":
|
||||||
|
if (!isStringLiteral(value)) throw fail(`${key} is not a string literal`);
|
||||||
|
data[key] = value.text;
|
||||||
|
break;
|
||||||
|
case "patches":
|
||||||
|
data.hasPatches = true;
|
||||||
|
break;
|
||||||
|
case "commands":
|
||||||
|
data.hasCommands = true;
|
||||||
|
break;
|
||||||
|
case "authors":
|
||||||
|
if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal");
|
||||||
|
data.authors = value.elements.map(e => {
|
||||||
|
if (!isPropertyAccessExpression(e)) throw fail("authors array contains non-property access expressions");
|
||||||
|
return devs[getName(e)!];
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "dependencies":
|
||||||
|
if (!isArrayLiteralExpression(value)) throw fail("dependencies is not an array literal");
|
||||||
|
const { elements } = value;
|
||||||
|
if (elements.some(e => !isStringLiteral(e))) throw fail("dependencies array contains non-string elements");
|
||||||
|
data.dependencies = (elements as NodeArray<StringLiteral>).map(e => e.text);
|
||||||
|
break;
|
||||||
|
case "required":
|
||||||
|
case "enabledByDefault":
|
||||||
|
data[key] = value.kind === SyntaxKind.TrueKeyword;
|
||||||
|
if (!data[key] && value.kind !== SyntaxKind.FalseKeyword) throw fail(`${key} is not a boolean literal`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
|
||||||
|
|
||||||
|
const fileBits = fileName.split(".");
|
||||||
|
if (fileBits.length > 2 && ["ts", "tsx"].includes(fileBits.at(-1)!)) {
|
||||||
|
const mod = fileBits.at(-2)!;
|
||||||
|
if (!["web", "desktop", "dev"].includes(mod)) throw fail(`invalid target ${fileBits.at(-2)}`);
|
||||||
|
data.target = mod as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw fail("no default export called 'definePlugin' found");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEntryPoint(dirent: Dirent) {
|
||||||
|
const base = join("./src/plugins", dirent.name);
|
||||||
|
if (!dirent.isDirectory()) return base;
|
||||||
|
|
||||||
|
for (const name of ["index.ts", "index.tsx"]) {
|
||||||
|
const full = join(base, name);
|
||||||
|
try {
|
||||||
|
await access(full);
|
||||||
|
return full;
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`${dirent.name}: Couldn't find entry point`);
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
parseDevs();
|
||||||
|
const plugins = readdirSync("./src/plugins", { withFileTypes: true }).filter(d => d.name !== "index.ts");
|
||||||
|
|
||||||
|
const promises = plugins.map(async dirent => parseFile(await getEntryPoint(dirent)));
|
||||||
|
|
||||||
|
const data = JSON.stringify(await Promise.all(promises));
|
||||||
|
|
||||||
|
if (process.argv.length > 2) {
|
||||||
|
writeFileSync(process.argv[2], data);
|
||||||
|
} else {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
})();
|
@ -122,6 +122,8 @@ export function _patchContextMenu(props: ContextMenuProps) {
|
|||||||
props.contextMenuApiArguments ??= [];
|
props.contextMenuApiArguments ??= [];
|
||||||
const contextMenuPatches = navPatches.get(props.navId);
|
const contextMenuPatches = navPatches.get(props.navId);
|
||||||
|
|
||||||
|
if (!Array.isArray(props.children)) props.children = [props.children];
|
||||||
|
|
||||||
if (contextMenuPatches) {
|
if (contextMenuPatches) {
|
||||||
for (const patch of contextMenuPatches) {
|
for (const patch of contextMenuPatches) {
|
||||||
try {
|
try {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
import { MessageStore } from "@webpack/common";
|
import { MessageStore } from "@webpack/common";
|
||||||
import type { Channel, Message } from "discord-types/general";
|
import type { Channel, Message } from "discord-types/general";
|
||||||
|
import type { Promisable } from "type-fest";
|
||||||
|
|
||||||
const MessageEventsLogger = new Logger("MessageEvents", "#e5c890");
|
const MessageEventsLogger = new Logger("MessageEvents", "#e5c890");
|
||||||
|
|
||||||
@ -41,16 +42,16 @@ export interface MessageExtra {
|
|||||||
stickerIds?: string[];
|
stickerIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => void | { cancel: boolean; };
|
export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
|
||||||
export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => void;
|
export type EditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void>;
|
||||||
|
|
||||||
const sendListeners = new Set<SendListener>();
|
const sendListeners = new Set<SendListener>();
|
||||||
const editListeners = new Set<EditListener>();
|
const editListeners = new Set<EditListener>();
|
||||||
|
|
||||||
export function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra) {
|
export async function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra) {
|
||||||
for (const listener of sendListeners) {
|
for (const listener of sendListeners) {
|
||||||
try {
|
try {
|
||||||
const result = listener(channelId, messageObj, extra);
|
const result = await listener(channelId, messageObj, extra);
|
||||||
if (result && result.cancel === true) {
|
if (result && result.cancel === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -61,10 +62,10 @@ export function _handlePreSend(channelId: string, messageObj: MessageObject, ext
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _handlePreEdit(channelId: string, messageId: string, messageObj: MessageObject) {
|
export async function _handlePreEdit(channelId: string, messageId: string, messageObj: MessageObject) {
|
||||||
for (const listener of editListeners) {
|
for (const listener of editListeners) {
|
||||||
try {
|
try {
|
||||||
listener(channelId, messageId, messageObj);
|
await listener(channelId, messageId, messageObj);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
MessageEventsLogger.error("MessageEditHandler: Listener encountered an unknown error\n", e);
|
MessageEventsLogger.error("MessageEditHandler: Listener encountered an unknown error\n", e);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ const ErrorBoundary = LazyComponent(() => {
|
|||||||
};
|
};
|
||||||
}) as
|
}) as
|
||||||
React.ComponentType<React.PropsWithChildren<Props>> & {
|
React.ComponentType<React.PropsWithChildren<Props>> & {
|
||||||
wrap<T extends object = any>(Component: React.ComponentType<T>, errorBoundaryProps?: Omit<Props<T>, "wrappedProps">): React.ComponentType<T>;
|
wrap<T extends object = any>(Component: React.ComponentType<T>, errorBoundaryProps?: Omit<Props<T>, "wrappedProps">): React.FunctionComponent<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => (
|
ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => (
|
||||||
|
@ -116,13 +116,9 @@ export default ErrorBoundary.wrap(function () {
|
|||||||
</Card>
|
</Card>
|
||||||
<Forms.FormTitle tag="h5">Themes</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Themes</Forms.FormTitle>
|
||||||
<TextArea
|
<TextArea
|
||||||
style={{
|
|
||||||
padding: ".5em",
|
|
||||||
border: "1px solid var(--background-modifier-accent)"
|
|
||||||
}}
|
|
||||||
value={themeText}
|
value={themeText}
|
||||||
onChange={e => setThemeText(e.currentTarget.value)}
|
onChange={e => setThemeText(e.currentTarget.value)}
|
||||||
className={TextAreaProps.textarea}
|
className={`${TextAreaProps.textarea} vc-settings-theme-links`}
|
||||||
placeholder="Theme Links"
|
placeholder="Theme Links"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
|
@ -59,7 +59,7 @@ function Settings(props: SettingsProps) {
|
|||||||
const CurrentTab = SettingsTabs[tab]?.component;
|
const CurrentTab = SettingsTabs[tab]?.component;
|
||||||
|
|
||||||
return <Forms.FormSection>
|
return <Forms.FormSection>
|
||||||
<Text variant="heading-md/normal" tag="h2">Vencord Settings</Text>
|
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">Vencord Settings</Text>
|
||||||
|
|
||||||
<TabBar
|
<TabBar
|
||||||
type="top"
|
type="top"
|
||||||
|
@ -38,3 +38,11 @@
|
|||||||
color: var(--info-warning-text);
|
color: var(--info-warning-text);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-settings-theme-links {
|
||||||
|
/* Needed to fix bad themes that hide certain textarea elements for whatever eldritch reason */
|
||||||
|
display: inline-block !important;
|
||||||
|
color: var(--text-normal) !important;
|
||||||
|
padding: 0.5em;
|
||||||
|
border: 1px solid var(--background-modifier-accent);
|
||||||
|
}
|
||||||
|
@ -32,10 +32,10 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "\"github.com\":new RegExp(\"\\\\/releases\\\\S*\\\\/download\"),",
|
find: '"7z","ade","adp"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /const o=JSON.parse\('\[.+?'\)/,
|
match: /JSON\.parse\('\[.+?'\)/,
|
||||||
replace: "const o=[]"
|
replace: "[]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -22,22 +22,22 @@ import definePlugin from "@utils/types";
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MessageEventsAPI",
|
name: "MessageEventsAPI",
|
||||||
description: "Api required by anything using message events.",
|
description: "Api required by anything using message events.",
|
||||||
authors: [Devs.Arjix],
|
authors: [Devs.Arjix, Devs.hunt],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "sendMessage:function",
|
find: '"MessageActionCreators"',
|
||||||
replacement: [{
|
replacement: [{
|
||||||
match: /(?<=_sendMessage:function\([^)]+\)){/,
|
match: /_sendMessage:(function\([^)]+\)){/,
|
||||||
replace: "{if(Vencord.Api.MessageEvents._handlePreSend(...arguments)){return;};"
|
replace: "_sendMessage:async $1{if(await Vencord.Api.MessageEvents._handlePreSend(...arguments))return;"
|
||||||
}, {
|
}, {
|
||||||
match: /(?<=\beditMessage:function\([^)]+\)){/,
|
match: /\beditMessage:(function\([^)]+\)){/,
|
||||||
replace: "{Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '("interactionUsernameProfile',
|
find: '("interactionUsernameProfile',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /var \w=(\w)\.id,\w=(\w)\.id;return .{1,2}\.useCallback\(\(?function\((.{1,2})\){/,
|
match: /var \i=(\i)\.id,\i=(\i)\.id;return \i\.useCallback\(\(?function\((\i)\){/,
|
||||||
replace: (m, message, channel, event) =>
|
replace: (m, message, channel, event) =>
|
||||||
// the message param is shadowed by the event param, so need to alias them
|
// the message param is shadowed by the event param, so need to alias them
|
||||||
`var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});`
|
`var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});`
|
||||||
|
@ -27,11 +27,11 @@ export default definePlugin({
|
|||||||
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
// foo && !bar ? createElement(blah,...makeElement(addReactionData))
|
// foo && !bar ? createElement(blah,...makeElement(addReactionData))
|
||||||
match: /(\i&&!\i)\?\(0,\i\.jsxs?\)\(.{0,200}renderPopout:.{0,300}?(\i)\(.{3,20}\{key:"add-reaction".+?\}/,
|
match: /\i&&!\i\?\(0,\i\.jsxs?\)\(.{0,200}renderPopout:.{0,300}?(\i)\(.{3,20}\{key:"add-reaction".+?\}/,
|
||||||
replace: (m, bools, makeElement) => {
|
replace: (m, makeElement) => {
|
||||||
const msg = m.match(/message:(.{1,3}),/)?.[1];
|
const msg = m.match(/message:(.{1,3}),/)?.[1];
|
||||||
if (!msg) throw new Error("Could not find message variable");
|
if (!msg) throw new Error("Could not find message variable");
|
||||||
return `...(${bools}?Vencord.Api.MessagePopover._buildPopoverElements(${msg},${makeElement}):[]),${m}`;
|
return `...Vencord.Api.MessagePopover._buildPopoverElements(${msg},${makeElement}),${m}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
@ -37,11 +37,11 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '"username"===',
|
find: '"dot"===',
|
||||||
all: true,
|
all: true,
|
||||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"(?:username|dot)"===\w(?!\.\w)/g,
|
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||||
replace: "true",
|
replace: "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -215,7 +215,8 @@ async function setRpc(disable?: boolean) {
|
|||||||
|
|
||||||
FluxDispatcher.dispatch({
|
FluxDispatcher.dispatch({
|
||||||
type: "LOCAL_ACTIVITY_UPDATE",
|
type: "LOCAL_ACTIVITY_UPDATE",
|
||||||
activity: !disable ? activity : {}
|
activity: !disable ? activity : null,
|
||||||
|
socketId: "CustomRPC",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,8 @@ function initWs(isManual = false) {
|
|||||||
if (keys.length !== 1)
|
if (keys.length !== 1)
|
||||||
return reply("Expected exactly one 'find' matches, found " + keys.length);
|
return reply("Expected exactly one 'find' matches, found " + keys.length);
|
||||||
|
|
||||||
let src = String(candidates[keys[0]]);
|
const mod = candidates[keys[0]];
|
||||||
|
let src = String(mod.original ?? mod);
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { migratePluginSettings } from "@api/settings";
|
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
@ -176,25 +175,12 @@ function CloneModal({ id, name: emojiName, isAnimated }: { id: string; name: str
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
function buildMenuItem(id: string, name: string, isAnimated: boolean) {
|
||||||
if (!props) return;
|
return (
|
||||||
const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = props;
|
|
||||||
|
|
||||||
if (!emoteClonerDataAlt || favoriteableType !== "emoji") return;
|
|
||||||
|
|
||||||
const name = emoteClonerDataAlt.match(/:(.*)(?:~\d+)?:/)?.[1];
|
|
||||||
if (!name || !favoriteableId) return;
|
|
||||||
|
|
||||||
const src = itemHref ?? itemSrc;
|
|
||||||
const isAnimated = new URL(src).pathname.endsWith(".gif");
|
|
||||||
|
|
||||||
const group = findGroupChildrenByChildId("copy-link", children);
|
|
||||||
if (group && !group.some(child => child?.props?.id === "emote-cloner")) {
|
|
||||||
group.push((
|
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="emote-cloner"
|
id="emote-cloner"
|
||||||
key="emote-cloner"
|
key="emote-cloner"
|
||||||
label="Clone"
|
label="Clone Emote"
|
||||||
action={() =>
|
action={() =>
|
||||||
openModal(modalProps => (
|
openModal(modalProps => (
|
||||||
<ModalRoot {...modalProps}>
|
<ModalRoot {...modalProps}>
|
||||||
@ -202,7 +188,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =
|
|||||||
<img
|
<img
|
||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden
|
aria-hidden
|
||||||
src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${favoriteableId}.${isAnimated ? "gif" : "png"}`}
|
src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${id}.${isAnimated ? "gif" : "png"}`}
|
||||||
alt=""
|
alt=""
|
||||||
height={24}
|
height={24}
|
||||||
width={24}
|
width={24}
|
||||||
@ -211,39 +197,56 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =
|
|||||||
<Forms.FormText>Clone {name}</Forms.FormText>
|
<Forms.FormText>Clone {name}</Forms.FormText>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<CloneModal id={favoriteableId} name={name} isAnimated={isAnimated} />
|
<CloneModal id={id} name={name} isAnimated={isAnimated} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
</Menu.MenuItem>
|
);
|
||||||
));
|
}
|
||||||
}
|
|
||||||
|
function isGifUrl(url: string) {
|
||||||
|
return new URL(url).pathname.endsWith(".gif");
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
||||||
|
const { favoriteableId, itemHref, itemSrc, favoriteableType } = props ?? {};
|
||||||
|
|
||||||
|
if (!favoriteableId || favoriteableType !== "emoji") return;
|
||||||
|
|
||||||
|
const match = props.message.content.match(RegExp(`<a?:(\\w+)(?:~\\d+)?:${favoriteableId}>|https://cdn\\.discordapp\\.com/emojis/${favoriteableId}\\.`));
|
||||||
|
if (!match) return;
|
||||||
|
const name = match[1] ?? "FakeNitroEmoji";
|
||||||
|
|
||||||
|
const group = findGroupChildrenByChildId("copy-link", children);
|
||||||
|
if (group && !group.some(child => child?.props?.id === "emote-cloner"))
|
||||||
|
group.push(buildMenuItem(favoriteableId, name, isGifUrl(itemHref ?? itemSrc)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => {
|
||||||
|
const { id, name, type } = props?.target?.dataset ?? {};
|
||||||
|
if (!id || !name || type !== "emoji") return;
|
||||||
|
|
||||||
|
const firstChild = props.target.firstChild as HTMLImageElement;
|
||||||
|
|
||||||
|
if (!children.some(c => c?.props?.id === "emote-cloner"))
|
||||||
|
children.push(buildMenuItem(id, name, firstChild && isGifUrl(firstChild.src)));
|
||||||
};
|
};
|
||||||
|
|
||||||
migratePluginSettings("EmoteCloner", "EmoteYoink");
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "EmoteCloner",
|
name: "EmoteCloner",
|
||||||
description: "Adds a Clone context menu item to emotes to clone them your own server",
|
description: "Adds a Clone context menu item to emotes to clone them your own server",
|
||||||
authors: [Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.Ven, Devs.Nuckyz],
|
||||||
dependencies: ["MenuItemDeobfuscatorAPI", "ContextMenuAPI"],
|
dependencies: ["MenuItemDeobfuscatorAPI", "ContextMenuAPI"],
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: ".Messages.MESSAGE_ACTIONS_MENU_LABEL",
|
|
||||||
replacement: {
|
|
||||||
match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/,
|
|
||||||
replace: (m, target) => `${m}emoteClonerDataAlt:${target}.alt,`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
addContextMenuPatch("message", messageContextMenuPatch);
|
addContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
addContextMenuPatch("expression-picker", expressionPickerPatch);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
removeContextMenuPatch("message", messageContextMenuPatch);
|
removeContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
removeContextMenuPatch("expression-picker", expressionPickerPatch);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -21,6 +21,7 @@ import { migratePluginSettings, Settings } from "@api/settings";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
|
import { ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
|
||||||
import { getCurrentGuild } from "@utils/discord";
|
import { getCurrentGuild } from "@utils/discord";
|
||||||
|
import { proxyLazy } from "@utils/proxyLazy";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, FluxDispatcher, PermissionStore, UserStore } from "@webpack/common";
|
import { ChannelStore, FluxDispatcher, PermissionStore, UserStore } from "@webpack/common";
|
||||||
@ -28,7 +29,21 @@ import { ChannelStore, FluxDispatcher, PermissionStore, UserStore } from "@webpa
|
|||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
||||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||||
const ProtoPreloadedUserSettings = findLazy(m => m.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
||||||
|
const ReaderFactory = findByPropsLazy("readerFactory");
|
||||||
|
|
||||||
|
function searchProtoClass(localName: string, parentProtoClass: any) {
|
||||||
|
if (!parentProtoClass) return;
|
||||||
|
|
||||||
|
const field = parentProtoClass.fields.find(field => field.localName === localName);
|
||||||
|
if (!field) return;
|
||||||
|
|
||||||
|
const getter: any = Object.values(field).find(value => typeof value === "function");
|
||||||
|
return getter?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppearanceSettingsProto = proxyLazy(() => searchProtoClass("appearance", PreloadedUserSettingsProtoHandler.ProtoClass));
|
||||||
|
const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSettings", AppearanceSettingsProto));
|
||||||
|
|
||||||
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
||||||
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
||||||
@ -167,8 +182,38 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "updateTheme:function",
|
find: "updateTheme:function",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\)/,
|
match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)(\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\))/,
|
||||||
replace: (_, rest, backgroundGradientPresetId, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme});`
|
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: 'jumboable?"jumbo":"default"',
|
||||||
|
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
||||||
|
replacement: {
|
||||||
|
match: /jumboable\?"jumbo":"default",emojiId.+?}}\)},(?<=(\i)=function\(\i\){var \i=\i\.node.+?)/,
|
||||||
|
replace: (m, component) => `${m}fakeNitroEmojiComponentExport=($self.EmojiComponent=${component},void 0),`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||||
|
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
||||||
|
replace: (m, content) => `${m}||${content}[0].target?.startsWith("https://cdn.discordapp.com/emojis/")`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
||||||
|
replace: (_, content) => `${content}=$self.patchFakeNitroEmojis(${content});`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "renderEmbeds=function",
|
||||||
|
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
||||||
|
replacement: {
|
||||||
|
match: /renderEmbeds=function\(\i\){.+?embeds\.map\(\(function\((\i)\){/,
|
||||||
|
replace: (m, embed) => `${m}if(${embed}.url?.startsWith("https://cdn.discordapp.com/emojis/"))return null;`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -186,6 +231,12 @@ export default definePlugin({
|
|||||||
default: 48,
|
default: 48,
|
||||||
markers: [32, 48, 64, 128, 160, 256, 512],
|
markers: [32, 48, 64, 128, 160, 256, 512],
|
||||||
},
|
},
|
||||||
|
transformEmojis: {
|
||||||
|
description: "Whether to transform fake emojis into real ones",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true,
|
||||||
|
},
|
||||||
enableStickerBypass: {
|
enableStickerBypass: {
|
||||||
description: "Allow sending fake stickers",
|
description: "Allow sending fake stickers",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
@ -219,48 +270,56 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleProtoChange(proto: any, user: any) {
|
handleProtoChange(proto: any, user: any) {
|
||||||
const premiumType: number = user?.premium_type ?? UserStore.getCurrentUser()?.premiumType ?? 0;
|
if ((!proto.appearance && !AppearanceSettingsProto) || !UserSettingsProtoStore) return;
|
||||||
|
|
||||||
if (premiumType === 0) {
|
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
const appearanceDummyProto = ProtoPreloadedUserSettings.create({
|
|
||||||
appearance: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
proto.appearance ??= appearanceDummyProto.appearance;
|
if (premiumType !== 2) {
|
||||||
|
proto.appearance ??= AppearanceSettingsProto.create();
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
||||||
proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme;
|
proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) {
|
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null && ClientThemeSettingsProto) {
|
||||||
const clientThemeSettingsDummyProto = ProtoPreloadedUserSettings.create({
|
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
||||||
appearance: {
|
|
||||||
clientThemeSettings: {
|
|
||||||
backgroundGradientPresetId: {
|
backgroundGradientPresetId: {
|
||||||
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto.appearance.clientThemeSettings;
|
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
||||||
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.appearance.clientThemeSettings.backgroundGradientPresetId;
|
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleGradientThemeSelect(backgroundGradientPresetId: number | undefined, theme: number) {
|
handleGradientThemeSelect(backgroundGradientPresetId: number | undefined, theme: number, original: () => void) {
|
||||||
const proto = ProtoPreloadedUserSettings.create({
|
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
appearance: {
|
if (premiumType === 2 || backgroundGradientPresetId == null) return original();
|
||||||
theme,
|
|
||||||
clientThemeSettings: {
|
if (!AppearanceSettingsProto || !ClientThemeSettingsProto || !ReaderFactory) return;
|
||||||
backgroundGradientPresetId: backgroundGradientPresetId != null ? {
|
|
||||||
|
const currentAppearanceProto = PreloadedUserSettingsProtoHandler.getCurrentValue().appearance;
|
||||||
|
|
||||||
|
const newAppearanceProto = currentAppearanceProto != null
|
||||||
|
? AppearanceSettingsProto.fromBinary(AppearanceSettingsProto.toBinary(currentAppearanceProto), ReaderFactory)
|
||||||
|
: AppearanceSettingsProto.create();
|
||||||
|
|
||||||
|
newAppearanceProto.theme = theme;
|
||||||
|
|
||||||
|
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
||||||
|
backgroundGradientPresetId: {
|
||||||
value: backgroundGradientPresetId
|
value: backgroundGradientPresetId
|
||||||
} : void 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
||||||
|
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
||||||
|
|
||||||
|
const proto = PreloadedUserSettingsProtoHandler.ProtoClass.create();
|
||||||
|
proto.appearance = newAppearanceProto;
|
||||||
|
|
||||||
FluxDispatcher.dispatch({
|
FluxDispatcher.dispatch({
|
||||||
type: "USER_SETTINGS_PROTO_UPDATE",
|
type: "USER_SETTINGS_PROTO_UPDATE",
|
||||||
local: true,
|
local: true,
|
||||||
@ -272,6 +331,39 @@ export default definePlugin({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
EmojiComponent: null as any,
|
||||||
|
|
||||||
|
patchFakeNitroEmojis(content: Array<any>) {
|
||||||
|
if (!this.EmojiComponent) return content;
|
||||||
|
|
||||||
|
const newContent: Array<any> = [];
|
||||||
|
|
||||||
|
for (const element of content) {
|
||||||
|
if (element.props?.trusted == null) {
|
||||||
|
newContent.push(element);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeNitroMatch = element.props.href.match(/https:\/\/cdn\.discordapp\.com\/emojis\/(\d+?)\.(png|webp|gif).+?(?=\s|$)/);
|
||||||
|
if (!fakeNitroMatch) {
|
||||||
|
newContent.push(element);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newContent.push((
|
||||||
|
<this.EmojiComponent node={{
|
||||||
|
type: "customEmoji",
|
||||||
|
jumboable: content.length === 1,
|
||||||
|
animated: fakeNitroMatch[2] === "gif",
|
||||||
|
name: ":FakeNitroEmoji:",
|
||||||
|
emojiId: fakeNitroMatch[1]
|
||||||
|
}} />
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newContent;
|
||||||
|
},
|
||||||
|
|
||||||
hasPermissionToUseExternalEmojis(channelId: string) {
|
hasPermissionToUseExternalEmojis(channelId: string) {
|
||||||
const channel = ChannelStore.getChannel(channelId);
|
const channel = ChannelStore.getChannel(channelId);
|
||||||
|
|
@ -43,8 +43,11 @@ export function isPluginEnabled(p: string) {
|
|||||||
|
|
||||||
const pluginsValues = Object.values(Plugins);
|
const pluginsValues = Object.values(Plugins);
|
||||||
|
|
||||||
// First roundtrip to mark and force enable dependencies
|
// First roundtrip to mark and force enable dependencies (only for enabled plugins)
|
||||||
for (const p of pluginsValues) {
|
//
|
||||||
|
// FIXME: might need to revisit this if there's ever nested (dependencies of dependencies) dependencies since this only
|
||||||
|
// goes for the top level and their children, but for now this works okay with the current API plugins
|
||||||
|
for (const p of pluginsValues) if (settings[p.name]?.enabled) {
|
||||||
p.dependencies?.forEach(d => {
|
p.dependencies?.forEach(d => {
|
||||||
const dep = Plugins[d];
|
const dep = Plugins[d];
|
||||||
if (dep) {
|
if (dep) {
|
||||||
|
@ -16,9 +16,10 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/settings";
|
import { definePluginSettings } from "@api/settings";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import Logger from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms } from "@webpack/common";
|
import { FluxDispatcher, Forms } from "@webpack/common";
|
||||||
@ -30,6 +31,12 @@ interface ActivityAssets {
|
|||||||
small_text?: string;
|
small_text?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ActivityButton {
|
||||||
|
label: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface Activity {
|
interface Activity {
|
||||||
state: string;
|
state: string;
|
||||||
details?: string;
|
details?: string;
|
||||||
@ -66,6 +73,9 @@ enum ActivityFlag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const applicationId = "1043533871037284423";
|
const applicationId = "1043533871037284423";
|
||||||
|
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
||||||
|
|
||||||
|
const logger = new Logger("LastFMRichPresence");
|
||||||
|
|
||||||
const presenceStore = findByPropsLazy("getLocalPresence");
|
const presenceStore = findByPropsLazy("getLocalPresence");
|
||||||
const assetManager = mapMangledModuleLazy(
|
const assetManager = mapMangledModuleLazy(
|
||||||
@ -79,14 +89,64 @@ async function getApplicationAsset(key: string): Promise<string> {
|
|||||||
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActivity(activity?: Activity) {
|
function setActivity(activity: Activity | null) {
|
||||||
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: activity });
|
FluxDispatcher.dispatch({
|
||||||
|
type: "LOCAL_ACTIVITY_UPDATE",
|
||||||
|
activity,
|
||||||
|
socketId: "LastFM",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
username: {
|
||||||
|
description: "last.fm username",
|
||||||
|
type: OptionType.STRING,
|
||||||
|
},
|
||||||
|
apiKey: {
|
||||||
|
description: "last.fm api key",
|
||||||
|
type: OptionType.STRING,
|
||||||
|
},
|
||||||
|
shareUsername: {
|
||||||
|
description: "show link to last.fm profile",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
hideWithSpotify: {
|
||||||
|
description: "hide last.fm presence if spotify is running",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
statusName: {
|
||||||
|
description: "text shown in status",
|
||||||
|
type: OptionType.STRING,
|
||||||
|
default: "some music",
|
||||||
|
},
|
||||||
|
useListeningStatus: {
|
||||||
|
description: 'show "Listening to" status instead of "Playing"',
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
missingArt: {
|
||||||
|
description: "When album or album art is missing",
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Use large Last.fm logo",
|
||||||
|
value: "lastfmLogo",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Use generic placeholder",
|
||||||
|
value: "placeholder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "LastFMRichPresence",
|
name: "LastFMRichPresence",
|
||||||
description: "Little plugin for Last.fm rich presence",
|
description: "Little plugin for Last.fm rich presence",
|
||||||
authors: [Devs.dzshn],
|
authors: [Devs.dzshn, Devs.RuiNtD],
|
||||||
|
|
||||||
settingsAboutComponent: () => (
|
settingsAboutComponent: () => (
|
||||||
<>
|
<>
|
||||||
@ -104,30 +164,9 @@ export default definePlugin({
|
|||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
|
||||||
options: {
|
settings,
|
||||||
username: {
|
|
||||||
description: "last.fm username",
|
|
||||||
type: OptionType.STRING,
|
|
||||||
},
|
|
||||||
apiKey: {
|
|
||||||
description: "last.fm api key",
|
|
||||||
type: OptionType.STRING,
|
|
||||||
},
|
|
||||||
hideWithSpotify: {
|
|
||||||
description: "hide last.fm presence if spotify is running",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
useListeningStatus: {
|
|
||||||
description: 'show "Listening to" status instead of "Playing"',
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.settings = Settings.plugins.LastFMRichPresence;
|
|
||||||
|
|
||||||
this.updateInterval = setInterval(() => { this.updatePresence(); }, 16000);
|
this.updateInterval = setInterval(() => { this.updatePresence(); }, 16000);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -136,12 +175,31 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async fetchTrackData(): Promise<TrackData | null> {
|
async fetchTrackData(): Promise<TrackData | null> {
|
||||||
if (!this.settings.username || !this.settings.apiKey) return null;
|
if (!settings.store.username || !settings.store.apiKey)
|
||||||
|
return null;
|
||||||
|
|
||||||
const response = await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&api_key=${this.settings.apiKey}&user=${this.settings.username}&limit=1&format=json`);
|
try {
|
||||||
const trackData = (await response.json()).recenttracks.track[0];
|
const params = new URLSearchParams({
|
||||||
|
method: "user.getrecenttracks",
|
||||||
|
api_key: settings.store.apiKey,
|
||||||
|
user: settings.store.username,
|
||||||
|
limit: "1",
|
||||||
|
format: "json"
|
||||||
|
});
|
||||||
|
|
||||||
if (!trackData["@attr"]?.nowplaying) return null;
|
const res = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`);
|
||||||
|
if (!res.ok) throw `${res.status} ${res.statusText}`;
|
||||||
|
|
||||||
|
const json = await res.json();
|
||||||
|
if (json.error) {
|
||||||
|
logger.error("Error from Last.fm API", `${json.error}: ${json.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trackData = json.recenttracks?.track[0];
|
||||||
|
|
||||||
|
if (!trackData || !trackData["@attr"]?.nowplaying)
|
||||||
|
return null;
|
||||||
|
|
||||||
// why does the json api have xml structure
|
// why does the json api have xml structure
|
||||||
return {
|
return {
|
||||||
@ -149,60 +207,80 @@ export default definePlugin({
|
|||||||
album: trackData.album["#text"],
|
album: trackData.album["#text"],
|
||||||
artist: trackData.artist["#text"] || "Unknown",
|
artist: trackData.artist["#text"] || "Unknown",
|
||||||
url: trackData.url,
|
url: trackData.url,
|
||||||
imageUrl: (trackData.image || []).filter(x => x.size === "large")[0]?.["#text"]
|
imageUrl: trackData.image?.find((x: any) => x.size === "large")?.["#text"]
|
||||||
};
|
};
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Failed to query Last.fm API", e);
|
||||||
|
// will clear the rich presence if API fails
|
||||||
|
return null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updatePresence() {
|
async updatePresence() {
|
||||||
if (this.settings.hideWithSpotify) {
|
setActivity(await this.getActivity());
|
||||||
|
},
|
||||||
|
|
||||||
|
getLargeImage(track: TrackData): string | undefined {
|
||||||
|
if (track.imageUrl && !track.imageUrl.includes(placeholderId))
|
||||||
|
return track.imageUrl;
|
||||||
|
|
||||||
|
if (settings.store.missingArt === "placeholder")
|
||||||
|
return "placeholder";
|
||||||
|
},
|
||||||
|
|
||||||
|
async getActivity(): Promise<Activity | null> {
|
||||||
|
if (settings.store.hideWithSpotify) {
|
||||||
for (const activity of presenceStore.getActivities()) {
|
for (const activity of presenceStore.getActivities()) {
|
||||||
if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) {
|
if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) {
|
||||||
// there is already music status (probably only spotify can do this currently)
|
// there is already music status because of Spotify or richerCider (probably more)
|
||||||
setActivity();
|
return null;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackData = await this.fetchTrackData();
|
const trackData = await this.fetchTrackData();
|
||||||
|
if (!trackData) return null;
|
||||||
|
|
||||||
if (!trackData) {
|
const largeImage = this.getLargeImage(trackData);
|
||||||
setActivity();
|
const assets: ActivityAssets = largeImage ?
|
||||||
return;
|
{
|
||||||
}
|
large_image: await getApplicationAsset(largeImage),
|
||||||
|
large_text: trackData.album || undefined,
|
||||||
const hideAlbumName = !trackData.album || trackData.album === trackData.name;
|
|
||||||
|
|
||||||
let assets: ActivityAssets;
|
|
||||||
if (trackData.imageUrl) {
|
|
||||||
assets = {
|
|
||||||
large_image: await getApplicationAsset(trackData.imageUrl),
|
|
||||||
large_text: trackData.name,
|
|
||||||
small_image: await getApplicationAsset("lastfm-small"),
|
small_image: await getApplicationAsset("lastfm-small"),
|
||||||
small_text: "Last.fm",
|
small_text: "Last.fm",
|
||||||
};
|
} : {
|
||||||
} else {
|
|
||||||
assets = {
|
|
||||||
large_image: await getApplicationAsset("lastfm-large"),
|
large_image: await getApplicationAsset("lastfm-large"),
|
||||||
large_text: "Last.fm",
|
large_text: trackData.album || undefined,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
setActivity({
|
const buttons: ActivityButton[] = [
|
||||||
|
{
|
||||||
|
label: "View Song",
|
||||||
|
url: trackData.url,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (settings.store.shareUsername)
|
||||||
|
buttons.push({
|
||||||
|
label: "Last.fm Profile",
|
||||||
|
url: `https://www.last.fm/user/${settings.store.username}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
application_id: applicationId,
|
application_id: applicationId,
|
||||||
name: "some music",
|
name: settings.store.statusName,
|
||||||
|
|
||||||
details: trackData.name,
|
details: trackData.name,
|
||||||
state: hideAlbumName ? trackData.artist : `${trackData.artist} - ${trackData.album}`,
|
state: trackData.artist,
|
||||||
assets,
|
assets,
|
||||||
|
|
||||||
buttons: ["Open in Last.fm"],
|
buttons: buttons.map(v => v.label),
|
||||||
metadata: {
|
metadata: {
|
||||||
button_urls: [trackData.url]
|
button_urls: buttons.map(v => v.url),
|
||||||
},
|
},
|
||||||
|
|
||||||
type: this.settings.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
|
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
|
||||||
flags: ActivityFlag.INSTANCE,
|
flags: ActivityFlag.INSTANCE,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -264,7 +264,7 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe
|
|||||||
color: "var(--background-secondary)",
|
color: "var(--background-secondary)",
|
||||||
author: {
|
author: {
|
||||||
name: <Text variant="text-xs/medium" tag="span">
|
name: <Text variant="text-xs/medium" tag="span">
|
||||||
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>,
|
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>
|
||||||
{isDM
|
{isDM
|
||||||
? Parser.parse(`<@${dmReceiver.id}>`)
|
? Parser.parse(`<@${dmReceiver.id}>`)
|
||||||
: Parser.parse(`<#${channel.id}>`)
|
: Parser.parse(`<#${channel.id}>`)
|
||||||
@ -302,7 +302,7 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
|||||||
{isDM
|
{isDM
|
||||||
? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
|
? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
|
||||||
: parse(`<#${channel.id}>`)
|
: parse(`<#${channel.id}>`)
|
||||||
},
|
}
|
||||||
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
.messagelogger-deleted div {
|
.messagelogger-deleted div {
|
||||||
color: #f04747;
|
color: #f04747;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.messagelogger-deleted a {
|
||||||
|
color: #be3535;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
@ -18,19 +18,19 @@
|
|||||||
|
|
||||||
import "./messageLogger.css";
|
import "./messageLogger.css";
|
||||||
|
|
||||||
|
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
import { FluxDispatcher, i18n, Menu, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import overlayStyle from "./deleteStyleOverlay.css?managed";
|
import overlayStyle from "./deleteStyleOverlay.css?managed";
|
||||||
import textStyle from "./deleteStyleText.css?managed";
|
import textStyle from "./deleteStyleText.css?managed";
|
||||||
|
|
||||||
const i18n = findLazy(m => m.Messages?.["en-US"]);
|
|
||||||
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
|
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
|
||||||
|
|
||||||
function addDeleteStyle() {
|
function addDeleteStyle() {
|
||||||
@ -43,20 +43,48 @@ function addDeleteStyle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MENU_ITEM_ID = "message-logger-remove-history";
|
||||||
|
const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) => {
|
||||||
|
const { message } = props;
|
||||||
|
const { deleted, editHistory, id, channel_id } = message;
|
||||||
|
|
||||||
|
if (!deleted && !editHistory?.length) return;
|
||||||
|
if (children.some(c => c?.props?.id === MENU_ITEM_ID)) return;
|
||||||
|
|
||||||
|
children.push((
|
||||||
|
<Menu.MenuItem
|
||||||
|
id={MENU_ITEM_ID}
|
||||||
|
key={MENU_ITEM_ID}
|
||||||
|
label="Remove Message History"
|
||||||
|
action={() => {
|
||||||
|
if (message.deleted) {
|
||||||
|
FluxDispatcher.dispatch({
|
||||||
|
type: "MESSAGE_DELETE",
|
||||||
|
channelId: channel_id,
|
||||||
|
id,
|
||||||
|
mlDeleted: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.editHistory = [];
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MessageLogger",
|
name: "MessageLogger",
|
||||||
description: "Temporarily logs deleted and edited messages.",
|
description: "Temporarily logs deleted and edited messages.",
|
||||||
authors: [Devs.rushii, Devs.Ven],
|
authors: [Devs.rushii, Devs.Ven],
|
||||||
|
dependencies: ["ContextMenuAPI", "MenuItemDeobfuscatorAPI"],
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
addDeleteStyle();
|
addDeleteStyle();
|
||||||
|
addContextMenuPatch("message", patchMessageContextMenu);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
document.querySelectorAll(".messagelogger-deleted").forEach(e => e.remove());
|
removeContextMenuPatch("message", patchMessageContextMenu);
|
||||||
document.querySelectorAll(".messagelogger-edited").forEach(e => e.remove());
|
|
||||||
document.body.classList.remove("messagelogger-red-overlay");
|
|
||||||
document.body.classList.remove("messagelogger-red-text");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderEdit(edit: { timestamp: any, content: string; }) {
|
renderEdit(edit: { timestamp: any, content: string; }) {
|
||||||
@ -106,7 +134,7 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDelete(cache: any, data: { ids: string[], id: string; }, isBulk: boolean) {
|
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
|
||||||
try {
|
try {
|
||||||
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
||||||
|
|
||||||
@ -118,7 +146,8 @@ export default definePlugin({
|
|||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
|
|
||||||
const EPHEMERAL = 64;
|
const EPHEMERAL = 64;
|
||||||
const shouldIgnore = (msg.flags & EPHEMERAL) === EPHEMERAL ||
|
const shouldIgnore = data.mlDeleted ||
|
||||||
|
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
||||||
ignoreBots && msg.author?.bot ||
|
ignoreBots && msg.author?.bot ||
|
||||||
ignoreSelf && msg.author?.id === myId;
|
ignoreSelf && msg.author?.id === myId;
|
||||||
|
|
||||||
@ -271,15 +300,10 @@ export default definePlugin({
|
|||||||
// Module 748241
|
// Module 748241
|
||||||
find: "Message must not be a thread starter message",
|
find: "Message must not be a thread starter message",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
|
||||||
// Write message.deleted to deleted var
|
|
||||||
match: /var (\w)=(\w).id,(?=\w=\w.message)/,
|
|
||||||
replace: "var $1=$2.id,deleted=$2.message.deleted,"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// Append messagelogger-deleted to classNames if deleted
|
// Append messagelogger-deleted to classNames if deleted
|
||||||
match: /\)\("li",\{(.+?),className:/,
|
match: /\)\("li",\{(.+?),className:/,
|
||||||
replace: ")(\"li\",{$1,className:(deleted ? \"messagelogger-deleted \" : \"\")+"
|
replace: ")(\"li\",{$1,className:(arguments[0].message.deleted ? \"messagelogger-deleted \" : \"\")+"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messagelogger-deleted-attachment {
|
.messagelogger-deleted-attachment,
|
||||||
|
.messagelogger-deleted div iframe {
|
||||||
filter: grayscale(1);
|
filter: grayscale(1);
|
||||||
transition: 150ms filter ease-in-out;
|
transition: 150ms filter ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messagelogger-deleted-attachment:hover {
|
.messagelogger-deleted-attachment:hover,
|
||||||
|
.messagelogger-deleted div iframe:hover {
|
||||||
filter: grayscale(0);
|
filter: grayscale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,39 +17,63 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import { classes, useAwaiter } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
import { fetchPronouns, formatPronouns } from "../pronoundbUtils";
|
import { awaitAndFormatPronouns } from "../pronoundbUtils";
|
||||||
import { PronounMapping } from "../types";
|
|
||||||
|
|
||||||
const styles: Record<string, string> = findByPropsLazy("timestampInline");
|
const styles: Record<string, string> = findByPropsLazy("timestampInline");
|
||||||
|
|
||||||
export default function PronounsChatComponentWrapper({ message }: { message: Message; }) {
|
function shouldShow(message: Message): boolean {
|
||||||
|
// Respect showInMessages
|
||||||
|
if (!Settings.plugins.PronounDB.showInMessages)
|
||||||
|
return false;
|
||||||
// Don't bother fetching bot or system users
|
// Don't bother fetching bot or system users
|
||||||
if (message.author.bot || message.author.system)
|
if (message.author.bot || message.author.system)
|
||||||
return null;
|
return false;
|
||||||
// Respect showSelf options
|
// Respect showSelf options
|
||||||
if (!Settings.plugins.PronounDB.showSelf && message.author.id === UserStore.getCurrentUser().id)
|
if (!Settings.plugins.PronounDB.showSelf && message.author.id === UserStore.getCurrentUser().id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PronounsChatComponentWrapper({ message }: { message: Message; }) {
|
||||||
|
if (!shouldShow(message))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return <PronounsChatComponent message={message} />;
|
return <PronounsChatComponent message={message} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PronounsChatComponent({ message }: { message: Message; }) {
|
export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) {
|
||||||
const [result, , isPending] = useAwaiter(() => fetchPronouns(message.author.id), {
|
if (!shouldShow(message))
|
||||||
fallbackValue: null,
|
return null;
|
||||||
onError: e => console.error("Fetching pronouns failed: ", e)
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the promise completed, the result was not "unspecified", and there is a mapping for the code, then return a span with the pronouns
|
return <CompactPronounsChatComponent message={message} />;
|
||||||
if (!isPending && result && result !== "unspecified" && PronounMapping[result]) {
|
}
|
||||||
|
|
||||||
|
function PronounsChatComponent({ message }: { message: Message; }) {
|
||||||
|
const result = awaitAndFormatPronouns(message.author.id);
|
||||||
|
if (result != null) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={classes(styles.timestampInline, styles.timestamp)}
|
className={classes(styles.timestampInline, styles.timestamp)}
|
||||||
>• {formatPronouns(result)}</span>
|
>• {result}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CompactPronounsChatComponent({ message }: { message: Message; }) {
|
||||||
|
const result = awaitAndFormatPronouns(message.author.id);
|
||||||
|
if (result != null) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classes(styles.timestampInline, styles.timestamp, "vc-pronoundb-compact")}
|
||||||
|
>• {result}</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,16 +17,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import { useAwaiter } from "@utils/misc";
|
|
||||||
import { UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { fetchPronouns, formatPronouns } from "../pronoundbUtils";
|
import { awaitAndFormatPronouns } from "../pronoundbUtils";
|
||||||
import { PronounMapping, UserProfilePronounsProps, UserProfileProps } from "../types";
|
import { UserProfilePronounsProps, UserProfileProps } from "../types";
|
||||||
|
|
||||||
export default function PronounsProfileWrapper(PronounsComponent: React.ElementType<UserProfilePronounsProps>, props: UserProfilePronounsProps, profileProps: UserProfileProps) {
|
export default function PronounsProfileWrapper(PronounsComponent: React.ElementType<UserProfilePronounsProps>, props: UserProfilePronounsProps, profileProps: UserProfileProps) {
|
||||||
const user = UserStore.getUser(profileProps.userId) ?? {};
|
const user = UserStore.getUser(profileProps.userId) ?? {};
|
||||||
|
// Respect showInProfile
|
||||||
|
if (!Settings.plugins.PronounDB.showInProfile)
|
||||||
|
return null;
|
||||||
// Don't bother fetching bot or system users
|
// Don't bother fetching bot or system users
|
||||||
if (user.bot || user.system) return null;
|
if (user.bot || user.system)
|
||||||
|
return null;
|
||||||
// Respect showSelf options
|
// Respect showSelf options
|
||||||
if (!Settings.plugins.PronounDB.showSelf && user.id === UserStore.getCurrentUser().id)
|
if (!Settings.plugins.PronounDB.showSelf && user.id === UserStore.getCurrentUser().id)
|
||||||
return null;
|
return null;
|
||||||
@ -45,15 +48,12 @@ function ProfilePronouns(
|
|||||||
leProps: UserProfilePronounsProps;
|
leProps: UserProfilePronounsProps;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const [result, , isPending] = useAwaiter(() => fetchPronouns(userId), {
|
const result = awaitAndFormatPronouns(userId);
|
||||||
fallbackValue: null,
|
|
||||||
onError: e => console.error("Fetching pronouns failed: ", e),
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the promise completed, the result was not "unspecified", and there is a mapping for the code, then render
|
// If the promise completed, the result was not "unspecified", and there is a mapping for the code, then render
|
||||||
if (!isPending && result && result !== "unspecified" && PronounMapping[result]) {
|
if (result != null) {
|
||||||
// First child is the header, second is a div with the actual text
|
// First child is the header, second is a div with the actual text
|
||||||
leProps.currentPronouns ||= formatPronouns(result);
|
leProps.currentPronouns ||= result;
|
||||||
return <Component {...leProps} />;
|
return <Component {...leProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
import PronounsAboutComponent from "./components/PronounsAboutComponent";
|
import PronounsAboutComponent from "./components/PronounsAboutComponent";
|
||||||
import PronounsChatComponent from "./components/PronounsChatComponent";
|
import { CompactPronounsChatComponentWrapper, PronounsChatComponentWrapper } from "./components/PronounsChatComponent";
|
||||||
import PronounsProfileWrapper from "./components/PronounsProfileWrapper";
|
import PronounsProfileWrapper from "./components/PronounsProfileWrapper";
|
||||||
|
|
||||||
export enum PronounsFormat {
|
export enum PronounsFormat {
|
||||||
@ -30,22 +32,30 @@ export enum PronounsFormat {
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "PronounDB",
|
name: "PronounDB",
|
||||||
authors: [Devs.Tyman],
|
authors: [Devs.Tyman, Devs.TheKodeToad],
|
||||||
description: "Adds pronouns to user messages using pronoundb",
|
description: "Adds pronouns to user messages using pronoundb",
|
||||||
patches: [
|
patches: [
|
||||||
// Patch the chat timestamp element
|
// Add next to username (compact mode)
|
||||||
{
|
{
|
||||||
find: "showCommunicationDisabledStyles",
|
find: "showCommunicationDisabledStyles",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=return\s*\(0,\w{1,3}\.jsxs?\)\(.+!\w{1,3}&&)(\(0,\w{1,3}.jsxs?\)\(.+?\{.+?\}\))/,
|
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
||||||
replace: "[$1, $self.PronounsChatComponent(e)]"
|
replace: "$1, $self.CompactPronounsChatComponentWrapper(e)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Patch the chat timestamp element (normal mode)
|
||||||
|
{
|
||||||
|
find: "showCommunicationDisabledStyles",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
||||||
|
replace: "[$1, $self.PronounsChatComponentWrapper(e)]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Hijack the discord pronouns section and add a wrapper around the text section
|
// Hijack the discord pronouns section and add a wrapper around the text section
|
||||||
{
|
{
|
||||||
find: ".Messages.BOT_PROFILE_SLASH_COMMANDS",
|
find: ".Messages.BOT_PROFILE_SLASH_COMMANDS",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\(0,.\.jsx\)\((?<PronounComponent>.{1,2}\..),(?<pronounProps>{currentPronouns.+?:(?<fullProps>.{1,2})\.pronouns.+?})\)/,
|
match: /\(0,.\.jsx\)\((?<PronounComponent>\i\..),(?<pronounProps>{currentPronouns.+?:(?<fullProps>\i)\.pronouns.+?})\)/,
|
||||||
replace: "$<fullProps>&&$self.PronounsProfileWrapper($<PronounComponent>,$<pronounProps>,$<fullProps>)"
|
replace: "$<fullProps>&&$self.PronounsProfileWrapper($<PronounComponent>,$<pronounProps>,$<fullProps>)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -79,10 +89,21 @@ export default definePlugin({
|
|||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Enable or disable showing pronouns for the current user",
|
description: "Enable or disable showing pronouns for the current user",
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
showInMessages: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show in messages",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showInProfile: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show in profile",
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
settingsAboutComponent: PronounsAboutComponent,
|
settingsAboutComponent: PronounsAboutComponent,
|
||||||
// Re-export the components on the plugin object so it is easily accessible in patches
|
// Re-export the components on the plugin object so it is easily accessible in patches
|
||||||
PronounsChatComponent,
|
PronounsChatComponentWrapper,
|
||||||
|
CompactPronounsChatComponentWrapper,
|
||||||
PronounsProfileWrapper
|
PronounsProfileWrapper
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
import { Settings } from "@api/settings";
|
import { Settings } from "@api/settings";
|
||||||
import { VENCORD_USER_AGENT } from "@utils/constants";
|
import { VENCORD_USER_AGENT } from "@utils/constants";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
|
import { useAwaiter } from "@utils/misc";
|
||||||
|
|
||||||
import { PronounsFormat } from ".";
|
import { PronounsFormat } from ".";
|
||||||
import { PronounCode, PronounMapping, PronounsResponse } from "./types";
|
import { PronounCode, PronounMapping, PronounsResponse } from "./types";
|
||||||
@ -39,6 +40,19 @@ const bulkFetch = debounce(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function awaitAndFormatPronouns(id: string): string | null {
|
||||||
|
const [result, , isPending] = useAwaiter(() => fetchPronouns(id), {
|
||||||
|
fallbackValue: null,
|
||||||
|
onError: e => console.error("Fetching pronouns failed: ", e)
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the promise completed, the result was not "unspecified", and there is a mapping for the code, then return the mappings
|
||||||
|
if (!isPending && result && result !== "unspecified" && PronounMapping[result])
|
||||||
|
return formatPronouns(result);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Fetches the pronouns for one id, returning a promise that resolves if it was cached, or once the request is completed
|
// Fetches the pronouns for one id, returning a promise that resolves if it was cached, or once the request is completed
|
||||||
export function fetchPronouns(id: string): Promise<PronounCode> {
|
export function fetchPronouns(id: string): Promise<PronounCode> {
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
|
9
src/plugins/pronoundb/styles.css
Normal file
9
src/plugins/pronoundb/styles.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.vc-pronoundb-compact {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="compact"] .vc-pronoundb-compact {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -2px;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 OpenAsar
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Link } from "@components/Link";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { Forms } from "@webpack/common";
|
|
||||||
const appIds = [
|
|
||||||
"911790844204437504",
|
|
||||||
"886578863147192350",
|
|
||||||
"1020414178047041627",
|
|
||||||
"1032800329332445255"
|
|
||||||
];
|
|
||||||
export default definePlugin({
|
|
||||||
name: "richerCider",
|
|
||||||
description: "Enhances Cider (More details in info button) by adding the \"Listening to\" type prefix to the user's rich presence when an applicable ID is found.",
|
|
||||||
authors: [{
|
|
||||||
id: 191621342473224192n,
|
|
||||||
name: "cryptofyre",
|
|
||||||
}],
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: '.displayName="LocalActivityStore"',
|
|
||||||
replacement: {
|
|
||||||
match: /LOCAL_ACTIVITY_UPDATE:function\((\i)\)\{/,
|
|
||||||
replace: "$&$self.patchActivity($1.activity);",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
settingsAboutComponent: () => (
|
|
||||||
<>
|
|
||||||
<Forms.FormTitle tag="h3">Install Cider to use this Plugin</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
<Link href="https://cider.sh">Follow the link to our website</Link> to get Cider up and running, and then enable the plugin.
|
|
||||||
</Forms.FormText>
|
|
||||||
<br></br>
|
|
||||||
<Forms.FormTitle tag="h3">What is Cider?</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
Cider is an open-source and community oriented Apple Music client for Windows, macOS, and Linux.
|
|
||||||
</Forms.FormText>
|
|
||||||
<br></br>
|
|
||||||
<Forms.FormTitle tag="h3">Recommended Optional Plugins</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
I'd recommend using TimeBarAllActivities alongside this plugin to give off a much better visual to the eye (Keep in mind this only affects your client and will not show for other users)
|
|
||||||
</Forms.FormText>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
patchActivity(activity: any) {
|
|
||||||
if (appIds.includes(activity.application_id)) {
|
|
||||||
activity.type = 2; /* LISTENING type */
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
80
src/plugins/searchReply.tsx
Normal file
80
src/plugins/searchReply.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { LazyComponent } from "@utils/misc";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { findByCode, findByCodeLazy } from "@webpack";
|
||||||
|
import { ChannelStore, i18n, Menu, SelectedChannelStore } from "@webpack/common";
|
||||||
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
|
const ReplyIcon = LazyComponent(() => findByCode("M10 8.26667V4L3 11.4667L10 18.9333V14.56C15 14.56 18.5 16.2667 21 20C20 14.6667 17 9.33333 10 8.26667Z"));
|
||||||
|
|
||||||
|
const replyFn = findByCodeLazy("showMentionToggle", "TEXTAREA_FOCUS", "shiftKey");
|
||||||
|
|
||||||
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => {
|
||||||
|
// make sure the message is in the selected channel
|
||||||
|
if (SelectedChannelStore.getChannelId() !== message.channel_id) return;
|
||||||
|
|
||||||
|
const channel = ChannelStore.getChannel(message?.channel_id);
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
|
// dms and group chats
|
||||||
|
const dmGroup = findGroupChildrenByChildId("pin", children);
|
||||||
|
if (dmGroup && !dmGroup.some(child => child?.props?.id === "reply")) {
|
||||||
|
const pinIndex = dmGroup.findIndex(c => c.props.id === "pin");
|
||||||
|
return dmGroup.splice(pinIndex + 1, 0, (
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="reply"
|
||||||
|
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
||||||
|
icon={ReplyIcon}
|
||||||
|
action={(e: React.MouseEvent) => replyFn(channel, message, e)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// servers
|
||||||
|
const serverGroup = findGroupChildrenByChildId("mark-unread", children);
|
||||||
|
if (serverGroup && !serverGroup.some(child => child?.props?.id === "reply")) {
|
||||||
|
return serverGroup.unshift((
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="reply"
|
||||||
|
label={i18n.Messages.MESSAGE_ACTION_REPLY}
|
||||||
|
icon={ReplyIcon}
|
||||||
|
action={(e: React.MouseEvent) => replyFn(channel, message, e)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "SearchReply",
|
||||||
|
description: "Adds a reply button to search results",
|
||||||
|
authors: [Devs.Aria],
|
||||||
|
|
||||||
|
start() {
|
||||||
|
addContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
removeContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
}
|
||||||
|
});
|
@ -22,7 +22,11 @@ import { Devs } from "@utils/constants";
|
|||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Button, ButtonLooks, ButtonWrapperClasses, React, Tooltip } from "@webpack/common";
|
import { Button, ButtonLooks, ButtonWrapperClasses, React, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
function SilentMessageToggle() {
|
function SilentMessageToggle(chatBoxProps: {
|
||||||
|
type: {
|
||||||
|
analyticsName: string;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
const [enabled, setEnabled] = React.useState(false);
|
const [enabled, setEnabled] = React.useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -37,6 +41,8 @@ function SilentMessageToggle() {
|
|||||||
return () => void removePreSendListener(listener);
|
return () => void removePreSendListener(listener);
|
||||||
}, [enabled]);
|
}, [enabled]);
|
||||||
|
|
||||||
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip text="Toggle Silent Message">
|
<Tooltip text="Toggle Silent Message">
|
||||||
{tooltipProps => (
|
{tooltipProps => (
|
||||||
@ -78,7 +84,7 @@ export default definePlugin({
|
|||||||
find: ".activeCommandOption",
|
find: ".activeCommandOption",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"gift"\)\);(?<=(\i)\.push.+?disabled:(\i),.+?)/,
|
match: /"gift"\)\);(?<=(\i)\.push.+?disabled:(\i),.+?)/,
|
||||||
replace: (m, array, disabled) => `${m}${disabled}||${array}.push($self.SilentMessageToggle());`
|
replace: (m, array, disabled) => `${m};try{${disabled}||${array}.push($self.SilentMessageToggle(arguments[0]));}catch{}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -37,10 +37,16 @@ const settings = definePluginSettings({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function SilentTypingToggle() {
|
function SilentTypingToggle(chatBoxProps: {
|
||||||
|
type: {
|
||||||
|
analyticsName: string;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
const { isEnabled } = settings.use(["isEnabled"]);
|
const { isEnabled } = settings.use(["isEnabled"]);
|
||||||
const toggle = () => settings.store.isEnabled = !settings.store.isEnabled;
|
const toggle = () => settings.store.isEnabled = !settings.store.isEnabled;
|
||||||
|
|
||||||
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip text={isEnabled ? "Disable silent typing" : "Enable silent typing"}>
|
<Tooltip text={isEnabled ? "Disable silent typing" : "Enable silent typing"}>
|
||||||
{(tooltipProps: any) => (
|
{(tooltipProps: any) => (
|
||||||
@ -83,7 +89,7 @@ export default definePlugin({
|
|||||||
predicate: () => settings.store.showIcon,
|
predicate: () => settings.store.showIcon,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||||
replace: "$&;try{$2||$1.push($self.chatBarIcon())}catch{}",
|
replace: "$&;try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -21,10 +21,11 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy } from "@webpack";
|
||||||
import { GuildMemberStore, React, RelationshipStore } from "@webpack/common";
|
import { GuildMemberStore, React, RelationshipStore, SelectedChannelStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
const Avatar = findByCodeLazy('"top",spacing:');
|
const Avatar = findByCodeLazy('"top",spacing:');
|
||||||
|
const openProfile = findByCodeLazy("friendToken", "USER_PROFILE_MODAL_OPEN");
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
showAvatars: {
|
showAvatars: {
|
||||||
@ -53,6 +54,46 @@ export function buildSeveralUsers({ a, b, c }: { a: string, b: string, c: number
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
user: User;
|
||||||
|
guildId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
|
||||||
|
return (
|
||||||
|
<strong
|
||||||
|
role="button"
|
||||||
|
onClick={() => {
|
||||||
|
openProfile({
|
||||||
|
userId: user.id,
|
||||||
|
guildId,
|
||||||
|
channelId: SelectedChannelStore.getChannelId(),
|
||||||
|
analyticsLocation: {
|
||||||
|
page: guildId ? "Guild Channel" : "DM Channel",
|
||||||
|
section: "Profile Popout"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridAutoFlow: "column",
|
||||||
|
gap: "4px",
|
||||||
|
color: settings.store.showRoleColors ? GuildMemberStore.getMember(guildId, user.id)?.colorString : undefined,
|
||||||
|
cursor: "pointer"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{settings.store.showAvatars && (
|
||||||
|
<div style={{ marginTop: "4px" }}>
|
||||||
|
<Avatar
|
||||||
|
size="SIZE_16"
|
||||||
|
src={user.getAvatarURL(guildId, 128)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{GuildMemberStore.getNick(guildId!, user.id) || !guildId && RelationshipStore.getNickname(user.id) || user.username}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
}, { noop: true });
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "TypingTweaks",
|
name: "TypingTweaks",
|
||||||
description: "Show avatars and role colours in the typing indicator",
|
description: "Show avatars and role colours in the typing indicator",
|
||||||
@ -93,22 +134,10 @@ export default definePlugin({
|
|||||||
|
|
||||||
let element = 0;
|
let element = 0;
|
||||||
|
|
||||||
return children.map(c => c.type === "strong" ? <this.TypingUser {...props} user={users[element++]} /> : c);
|
return children.map(c =>
|
||||||
|
c.type === "strong"
|
||||||
|
? <TypingUser {...props} user={users[element++]} />
|
||||||
|
: c
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
TypingUser: ErrorBoundary.wrap(({ user, guildId }: { user: User, guildId: string; }) => {
|
|
||||||
return <strong style={{
|
|
||||||
display: "grid",
|
|
||||||
gridAutoFlow: "column",
|
|
||||||
gap: "4px",
|
|
||||||
color: settings.store.showRoleColors ? GuildMemberStore.getMember(guildId, user.id)?.colorString : undefined
|
|
||||||
}}>
|
|
||||||
{settings.store.showAvatars && <div style={{ marginTop: "4px" }}>
|
|
||||||
<Avatar
|
|
||||||
size="SIZE_16"
|
|
||||||
src={user.getAvatarURL(guildId, 128)} />
|
|
||||||
</div>}
|
|
||||||
{GuildMemberStore.getNick(guildId!, user.id) || !guildId && RelationshipStore.getNickname(user.id) || user.username}
|
|
||||||
</strong>;
|
|
||||||
}, { noop: true })
|
|
||||||
});
|
});
|
||||||
|
@ -21,87 +21,71 @@ import { Devs } from "@utils/constants";
|
|||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
const endings = [
|
const endings = [
|
||||||
"owo", "UwU", ">w<", "^w^", "●w●", "☆w☆", "𝗨𝘄𝗨", "(ᗒᗨᗕ)", "(▰˘v˘▰)",
|
"rawr x3",
|
||||||
"( ´ ▽ ` ).。o♡", "*unbuttons shirt*", ">3<", ">:3", ":3", "murr~",
|
"OwO",
|
||||||
"♥(。U ω U。)", "(˘ε˘)", "*screams*", "*twerks*", "*sweats*",
|
"UwU",
|
||||||
|
"o.O",
|
||||||
|
"-.-",
|
||||||
|
">w<",
|
||||||
|
"(⑅˘꒳˘)",
|
||||||
|
"(ꈍᴗꈍ)",
|
||||||
|
"(˘ω˘)",
|
||||||
|
"(U ᵕ U❁)",
|
||||||
|
"σωσ",
|
||||||
|
"òωó",
|
||||||
|
"(///ˬ///✿)",
|
||||||
|
"(U ﹏ U)",
|
||||||
|
"( ͡o ω ͡o )",
|
||||||
|
"ʘwʘ",
|
||||||
|
":3",
|
||||||
|
":3", // important enough to have twice
|
||||||
|
"XD",
|
||||||
|
"nyaa~~",
|
||||||
|
"mya",
|
||||||
|
">_<",
|
||||||
|
"😳",
|
||||||
|
"🥺",
|
||||||
|
"😳😳😳",
|
||||||
|
"rawr",
|
||||||
|
"^^",
|
||||||
|
"^^;;",
|
||||||
|
"(ˆ ﻌ ˆ)♡",
|
||||||
|
"^•ﻌ•^",
|
||||||
|
"/(^•ω•^)",
|
||||||
|
"(✿oωo)"
|
||||||
];
|
];
|
||||||
|
|
||||||
const replacements = [
|
const replacements = [
|
||||||
["love", "wuv"],
|
["small", "smol"],
|
||||||
["mr", "mistuh"],
|
["cute", "kawaii~"],
|
||||||
["dog", "doggo"],
|
["fluff", "floof"],
|
||||||
["cat", "kitteh"],
|
["love", "luv"],
|
||||||
["hello", "henwo"],
|
["stupid", "baka"],
|
||||||
["hell", "heck"],
|
["what", "nani"],
|
||||||
["fuck", "fwick"],
|
["meow", "nya~"],
|
||||||
["fuk", "fwick"],
|
|
||||||
["shit", "shoot"],
|
|
||||||
["friend", "fwend"],
|
|
||||||
["stop", "stawp"],
|
|
||||||
["god", "gosh"],
|
|
||||||
["dick", "peepee"],
|
|
||||||
["penis", "bulge"],
|
|
||||||
["damn", "darn"],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function selectRandomElement(arr) {
|
||||||
|
// generate a random index based on the length of the array
|
||||||
|
const randomIndex = Math.floor(Math.random() * arr.length);
|
||||||
|
|
||||||
|
// return the element at the randomly generated index
|
||||||
|
return arr[randomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function uwuify(message: string): string {
|
function uwuify(message: string): string {
|
||||||
return message
|
message = message.toLowerCase();
|
||||||
.split(" ")
|
// words
|
||||||
.map(w => {
|
for (const pair of replacements) {
|
||||||
let owofied = false;
|
message = message.replaceAll(pair[0], pair[1]);
|
||||||
const lowerCase = w.toLowerCase();
|
|
||||||
// return if the word is too short - uwuifying short words makes them unreadable
|
|
||||||
if (w.length < 4) {
|
|
||||||
return w;
|
|
||||||
}
|
}
|
||||||
|
message = message
|
||||||
// replacing the words based on the array on line 29
|
.replaceAll(/([ \t\n])n/g, "$1ny") // nyaify
|
||||||
for (const [find, replace] of replacements) {
|
.replaceAll(/[lr]/g, "w") // [lr] > w
|
||||||
if (w.includes(find)) {
|
.replaceAll(/([ \t\n])([a-z])/g, (_, p1, p2) => Math.random() < .5 ? `${p1}${p2}-${p2}` : `${p1}${p2}`) // stutter
|
||||||
w = w.replace(find, replace);
|
.replaceAll(/([^.,!][.,!])([ \t\n])/g, (_, p1, p2) => `${p1} ${selectRandomElement(endings)}${p2}`); // endings
|
||||||
owofied = true;
|
return message;
|
||||||
}
|
|
||||||
}
|
|
||||||
// these are the biggest word changes. if any of these are done we dont do the
|
|
||||||
// ones after the isowo check. to keep the words somewhat readable
|
|
||||||
if (lowerCase.includes("u") && !lowerCase.includes("uwu")) {
|
|
||||||
w = w.replace("u", "UwU");
|
|
||||||
owofied = true;
|
|
||||||
}
|
|
||||||
if (lowerCase.includes("o") && !lowerCase.includes("owo")) {
|
|
||||||
w = w.replace("o", "OwO");
|
|
||||||
owofied = true;
|
|
||||||
}
|
|
||||||
if (lowerCase.endsWith("y") && w.length < 7) {
|
|
||||||
w = w + " " + "w" + w.slice(1);
|
|
||||||
owofied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returning if word has been already uwuified - to prevent over-uwuifying
|
|
||||||
if (owofied) {
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
// more tiny changes - to keep the words that passed through the latter changes uwuified
|
|
||||||
if (!lowerCase.endsWith("n")) {
|
|
||||||
w = w.replace("n", "ny");
|
|
||||||
}
|
|
||||||
if (Math.floor(Math.random() * 2) === 1) {
|
|
||||||
w.replace("s", "sh");
|
|
||||||
}
|
|
||||||
if (Math.floor(Math.random() * 5) === 3 && !owofied) {
|
|
||||||
w = w[0] + "-" + w[0] + "-" + w;
|
|
||||||
}
|
|
||||||
if (Math.floor(Math.random() * 5) === 3) {
|
|
||||||
w =
|
|
||||||
w +
|
|
||||||
" " +
|
|
||||||
endings[Math.floor(Math.random() * endings.length)];
|
|
||||||
}
|
|
||||||
w = w.replaceAll("r", "w").replaceAll("l", "w");
|
|
||||||
return w;
|
|
||||||
}).join(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -110,7 +94,7 @@ function uwuify(message: string): string {
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "UwUifier",
|
name: "UwUifier",
|
||||||
description: "Simply uwuify commands",
|
description: "Simply uwuify commands",
|
||||||
authors: [Devs.echo],
|
authors: [Devs.echo, Devs.skyevg],
|
||||||
dependencies: ["CommandsAPI"],
|
dependencies: ["CommandsAPI"],
|
||||||
|
|
||||||
commands: [
|
commands: [
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { LazyComponent } from "@utils/misc";
|
import { LazyComponent } from "@utils/misc";
|
||||||
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { PluginDef } from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { find, findByCode, findByPropsLazy } from "@webpack";
|
import { find, findByCode, findByPropsLazy } from "@webpack";
|
||||||
import { Menu } from "@webpack/common";
|
import { Menu } from "@webpack/common";
|
||||||
import type { Guild } from "discord-types/general";
|
import type { Guild } from "discord-types/general";
|
||||||
@ -30,12 +30,12 @@ const MaskedLink = LazyComponent(() => find(m => m.type?.toString().includes("MA
|
|||||||
const GuildBannerStore = findByPropsLazy("getGuildBannerURL");
|
const GuildBannerStore = findByPropsLazy("getGuildBannerURL");
|
||||||
|
|
||||||
const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage(";
|
const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage(";
|
||||||
export default new class ViewIcons implements PluginDef {
|
export default definePlugin({
|
||||||
name = "ViewIcons";
|
name: "ViewIcons",
|
||||||
authors = [Devs.Ven];
|
authors: [Devs.Ven],
|
||||||
description = "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.";
|
description: "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.",
|
||||||
|
|
||||||
dependencies = ["MenuItemDeobfuscatorAPI"];
|
dependencies: ["MenuItemDeobfuscatorAPI"],
|
||||||
|
|
||||||
openImage(url: string) {
|
openImage(url: string) {
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
@ -52,9 +52,9 @@ export default new class ViewIcons implements PluginDef {
|
|||||||
/>
|
/>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
|
|
||||||
patches = [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "onAddFriend:",
|
find: "onAddFriend:",
|
||||||
replacement: {
|
replacement: {
|
||||||
@ -83,7 +83,7 @@ export default new class ViewIcons implements PluginDef {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
],
|
||||||
|
|
||||||
buildGuildContextMenuEntries(guild: Guild) {
|
buildGuildContextMenuEntries(guild: Guild) {
|
||||||
return (
|
return (
|
||||||
@ -107,4 +107,4 @@ export default new class ViewIcons implements PluginDef {
|
|||||||
</Menu.MenuGroup>
|
</Menu.MenuGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
@ -29,15 +29,9 @@ import { Message } from "discord-types/general";
|
|||||||
|
|
||||||
|
|
||||||
const CopyIcon = () => {
|
const CopyIcon = () => {
|
||||||
return <svg viewBox="0 0 512.002 512.002" fill="currentColor" aria-hidden="true" width="22" height="22">
|
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="22" height="22">
|
||||||
<path d="M462.002,92.002h-42.001V50c0-27.57-22.43-50-50-50h-320c-27.57,0-50,22.43-50,50v320.002c0,27.57,22.43,50,50,50h42.001 v42c0,27.57,22.43,50,50,50h320c27.57,0,50-22.43,50-50v-320C512.001,114.432,489.573,92.002,462.002,92.002z M50.001,400.002 c-16.542,0-30-13.458-30-30V50c0-16.542,13.458-30,30-30h320c16.542,0,30,13.458,30,30v320.002c0,16.542-13.458,30-30,30H50.001z M492.002,462.002c0,16.542-13.458,30-30,30h-320c-16.542,0-30-13.458-30-30v-42h257.999c27.57,0,50-22.43,50-50v-258h42.001 c16.542,0,30,13.458,30,30V462.002z" />
|
<path d="M12.9297 3.25007C12.7343 3.05261 12.4154 3.05226 12.2196 3.24928L11.5746 3.89824C11.3811 4.09297 11.3808 4.40733 11.5739 4.60245L16.5685 9.64824C16.7614 9.84309 16.7614 10.1569 16.5685 10.3517L11.5739 15.3975C11.3808 15.5927 11.3811 15.907 11.5746 16.1017L12.2196 16.7507C12.4154 16.9477 12.7343 16.9474 12.9297 16.7499L19.2604 10.3517C19.4532 10.1568 19.4532 9.84314 19.2604 9.64832L12.9297 3.25007Z" />
|
||||||
<path d="M462.024,457.002H170.98c-5.522,0-10,4.478-10,10c0,5.523,4.478,10,10,10h291.043c5.522,0,10-4.477,10-10 S467.546,457.002,462.024,457.002z" />
|
<path d="M8.42616 4.60245C8.6193 4.40733 8.61898 4.09297 8.42545 3.89824L7.78047 3.24928C7.58466 3.05226 7.26578 3.05261 7.07041 3.25007L0.739669 9.64832C0.5469 9.84314 0.546901 10.1568 0.739669 10.3517L7.07041 16.7499C7.26578 16.9474 7.58465 16.9477 7.78047 16.7507L8.42545 16.1017C8.61898 15.907 8.6193 15.5927 8.42616 15.3975L3.43155 10.3517C3.23869 10.1569 3.23869 9.84309 3.43155 9.64824L8.42616 4.60245Z" />
|
||||||
<path d="M142.25,457.002h-0.27c-5.522,0-10,4.478-10,10c0,5.523,4.478,10,10,10h0.27c5.523,0,10-4.477,10-10 S147.773,457.002,142.25,457.002z" />
|
|
||||||
<path d="M110.035,35h-0.27c-5.522,0-10,4.478-10,10s4.478,10,10,10h0.27c5.522,0,10-4.478,10-10S115.558,35,110.035,35z" />
|
|
||||||
<path d="M81.036,35H50.001c-5.522,0-10,4.478-10,10s4.478,10,10,10h31.034c5.523,0,10.001-4.478,10.001-10S86.558,35,81.036,35z" />
|
|
||||||
<path d="M122.084,246.829l-0.008-0.008l-14.8-30.002c5.407-2.305,10.07-5.975,13.466-10.537c3.725-5.006,5.931-11.08,5.931-17.604 c0-8.588-3.248-15.902-9.743-21.965c-3.196-2.992-6.739-5.242-10.627-6.744c-3.891-1.506-8.097-2.258-12.612-2.258H64.936 c-2.793,0-5.327,1.145-7.163,2.996c-1.832,1.85-2.968,4.396-2.968,7.205v82.777c0,2.896,1.137,5.338,2.893,7.105 c0.962,0.969,2.107,1.728,3.349,2.246c1.249,0.52,2.597,0.797,3.953,0.797c2.49,0,5.028-0.92,7.096-2.967 c1.004-0.99,1.761-2.09,2.271-3.297c0.516-1.221,0.771-2.516,0.771-3.885v-31.139h10.844l17.862,36.184l-0.004,0.005 c1.315,2.699,3.416,4.4,5.763,5.201c1.275,0.434,2.616,0.596,3.938,0.502c1.317-0.094,2.618-0.44,3.812-1.022 c2.307-1.123,4.237-3.123,5.179-5.881h0.002c0.45-1.307,0.639-2.609,0.564-3.904C123.023,249.327,122.684,248.058,122.084,246.829 z M106.124,190.85c-0.438,1.795-1.473,3.546-3.189,5.07c-1.205,1.039-2.538,1.838-3.996,2.395c-1.45,0.553-3.02,0.865-4.704,0.934 v-0.006H75.138v-21.221H93.69c3.19,0,5.9,0.893,7.984,2.328c1.83,1.26,3.187,2.939,3.96,4.791 C106.393,186.957,106.591,188.942,106.124,190.85z" />
|
|
||||||
<path d="M364.199,163.706c-1.12-2.354-3.136-4.285-5.777-5.254l-0.055-0.016c-2.721-0.916-5.49-0.688-7.823,0.439 c-2.349,1.135-4.243,3.172-5.193,5.861v0.008l-19.568,55.744l-19.571-55.816h-0.001c-0.705-2.352-2.14-4.141-3.935-5.322 c-1.589-1.047-3.458-1.609-5.345-1.656c-1.887-0.047-3.801,0.42-5.479,1.43c-1.938,1.164-3.553,3.031-4.445,5.637h0.002 l-19.564,55.73l-19.569-55.746l-0.008-0.016c-0.997-2.779-2.938-4.752-5.235-5.846c-1.222-0.584-2.552-0.918-3.899-0.99 c-1.34-0.074-2.701,0.115-3.991,0.578c-2.347,0.842-4.429,2.57-5.701,5.24c-1.206,2.529-1.354,5.119-0.441,7.766l0.005,0.009 l29.181,83.133l0.016,0.047c1.186,3.139,3.318,5.213,5.775,6.234c1.141,0.473,2.355,0.717,3.581,0.73 c1.219,0.016,2.438-0.197,3.595-0.635c2.602-0.982,4.914-3.084,6.238-6.268l0.039-0.109l19.595-55.779l20.043,56.877l0.047,0.125 c0.967,2.25,2.404,3.912,4.071,4.99c1.666,1.078,3.544,1.563,5.408,1.465c1.854-0.096,3.686-0.764,5.274-1.992 c1.666-1.289,3.059-3.191,3.899-5.686l29.251-83.133C365.546,168.825,365.317,166.053,364.199,163.706z" />
|
|
||||||
<path d="M226.7,247.37l-0.008-0.031l-8.818-21.24l-0.003,0.002l-25.528-61.617h0.001c-1.024-2.709-3.167-4.654-5.674-5.645 c-1.165-0.461-2.429-0.715-3.708-0.742c-1.28-0.027-2.565,0.178-3.773,0.635c-2.393,0.906-4.479,2.764-5.647,5.719l0.005,0.002 l-25.54,61.719h-0.003l-8.818,21.168v0.008c-1.076,2.607-1,5.383-0.008,7.783c0.994,2.408,2.91,4.433,5.51,5.527l0.038,0.023 c2.619,1.035,5.435,1.064,7.886,0.041c2.295-0.955,4.242-2.813,5.384-5.6l6.147-14.877h37.566l6.148,14.877 c1.175,2.844,3.171,4.674,5.459,5.609c1.261,0.518,2.601,0.752,3.932,0.73c1.324-0.021,2.642-0.295,3.865-0.801 c2.418-0.998,4.496-2.9,5.579-5.547l0.008-0.016C227.723,252.523,227.723,249.945,226.7,247.37z M172.552,219.938l10.344-25.01 l10.416,25.01H172.552z" />
|
|
||||||
</svg>;
|
</svg>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -202,8 +202,28 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||||||
name: "lewisakura",
|
name: "lewisakura",
|
||||||
id: 96269247411400704n
|
id: 96269247411400704n
|
||||||
},
|
},
|
||||||
|
RuiNtD: {
|
||||||
|
name: "RuiNtD",
|
||||||
|
id: 157917665162297344n
|
||||||
|
},
|
||||||
|
hunt: {
|
||||||
|
name: "hunt-g",
|
||||||
|
id: 222800179697287168n
|
||||||
|
},
|
||||||
cloudburst: {
|
cloudburst: {
|
||||||
name: "cloudburst",
|
name: "cloudburst",
|
||||||
id: 892128204150685769n
|
id: 892128204150685769n
|
||||||
|
},
|
||||||
|
Aria: {
|
||||||
|
name: "Syncxv",
|
||||||
|
id: 549244932213309442n,
|
||||||
|
},
|
||||||
|
TheKodeToad: {
|
||||||
|
name: "TheKodeToad",
|
||||||
|
id: 706152404072267788n
|
||||||
|
},
|
||||||
|
skyevg: {
|
||||||
|
name: "skyevg",
|
||||||
|
id: 1090310844283363348n
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
24
src/webpack/common/types/i18nMessages.ts
Normal file
24
src/webpack/common/types/i18nMessages.ts
Normal file
File diff suppressed because one or more lines are too long
28
src/webpack/common/types/utils.d.ts
vendored
28
src/webpack/common/types/utils.d.ts
vendored
@ -19,6 +19,7 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import type { FluxEvents } from "./fluxEvents";
|
import type { FluxEvents } from "./fluxEvents";
|
||||||
|
import { i18nMessages } from "./i18nMessages";
|
||||||
|
|
||||||
export { FluxEvents };
|
export { FluxEvents };
|
||||||
|
|
||||||
@ -82,3 +83,30 @@ export type RestAPI = Record<"delete" | "get" | "patch" | "post" | "put", (data:
|
|||||||
V8APIError: Error;
|
V8APIError: Error;
|
||||||
getAPIBaseURL(withVersion?: boolean): string;
|
getAPIBaseURL(withVersion?: boolean): string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface Locale {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
localizedName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocaleInfo {
|
||||||
|
code: string;
|
||||||
|
enabled: boolean;
|
||||||
|
name: string;
|
||||||
|
englishName: string;
|
||||||
|
postgresLang: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface i18n {
|
||||||
|
getAvailableLocales(): Locale[];
|
||||||
|
getLanguages(): LocaleInfo[];
|
||||||
|
getDefaultLocale(): string;
|
||||||
|
getLocale(): string;
|
||||||
|
getLocaleInfo(): LocaleInfo;
|
||||||
|
setLocale(locale: string): void;
|
||||||
|
|
||||||
|
loadPromise: Promise<void>;
|
||||||
|
|
||||||
|
Messages: Record<i18nMessages, string>;
|
||||||
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import type { User } from "discord-types/general";
|
import type { User } from "discord-types/general";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// eslint-disable-next-line path-alias/no-relative
|
||||||
import { _resolveReady,filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
||||||
import type * as t from "./types/utils";
|
import type * as t from "./types/utils";
|
||||||
|
|
||||||
export let FluxDispatcher: t.FluxDispatcher;
|
export let FluxDispatcher: t.FluxDispatcher;
|
||||||
@ -29,6 +29,8 @@ export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYea
|
|||||||
|
|
||||||
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight");
|
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight");
|
||||||
|
|
||||||
|
export const i18n: t.i18n = findLazy(m => m.Messages?.["en-US"]);
|
||||||
|
|
||||||
export let SnowflakeUtils: t.SnowflakeUtils;
|
export let SnowflakeUtils: t.SnowflakeUtils;
|
||||||
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user