Merge branch 'dev' into bun
This commit is contained in:
commit
3a6f4713e3
977 changed files with 46541 additions and 16881 deletions
4
.github/ISSUE_TEMPLATE/blank.yml
vendored
4
.github/ISSUE_TEMPLATE/blank.yml
vendored
|
|
@ -5,9 +5,9 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||

|
||||

|
||||
|
||||
GitHub Issues are for development, not support! Please use our [support server](https://vencord.dev/discord) unless you are a Vencord Developer.
|
||||
GitHub Issues are for development, not support! Please use our [support server](https://equicord.org/discord) unless you are a Vencord Developer.
|
||||
|
||||
- type: textarea
|
||||
id: content
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -9,7 +9,7 @@ body:
|
|||
value: |
|
||||

|
||||
|
||||
GitHub Issues are for development, not support! Please use our [support server](https://discord.gg/5Xh2W87egW) unless you are a Equicord/Vencord Developer.
|
||||
GitHub Issues are for development, not support! Please use our [support server](https://equicord.org/discord) unless you are a Equicord/Vencord Developer.
|
||||
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
|
|
|
|||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,8 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Equicord Support Server
|
||||
url: https://discord.gg/5Xh2W87egW
|
||||
url: https://equicord.org/discord
|
||||
about: If you need help regarding Equicord, please join our support server!
|
||||
- name: Equicord Installer
|
||||
url: https://github.com/Equicord/Installer
|
||||
url: https://github.com/Equicord/Equilotl
|
||||
about: You can find the Equicord Installer here
|
||||
|
|
|
|||
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
|
|
@ -1,16 +1,18 @@
|
|||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
GITHUB_TOKEN: ${{ secrets.ETOKEN }}
|
||||
REPO: Equicord/Equibored
|
||||
USERNAME: GitHub-Actions
|
||||
permissions: write-all
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: Build Equicord
|
||||
|
|
@ -26,10 +28,14 @@ jobs:
|
|||
run: bun run buildWebStandalone
|
||||
- name: Build
|
||||
run: bun run buildStandalone
|
||||
- name: Generate plugin list
|
||||
run: bun run generatePluginJson dist/plugins.json
|
||||
- name: Generate Equicord plugin list
|
||||
run: bun run generateEquicordPluginJson dist/equicordplugins.json
|
||||
|
||||
- name: Generate plugin lists
|
||||
run: |
|
||||
pnpm generatePluginJson dist/plugins.json
|
||||
pnpm generateEquicordPluginJson dist/equicordplugins.json
|
||||
pnpm generateVencordPluginJson dist/vencordplugins.json
|
||||
pnpm generateDevsList dist/devs.json
|
||||
|
||||
- name: Collect files to be released
|
||||
run: |
|
||||
cd dist
|
||||
|
|
@ -48,10 +54,10 @@ jobs:
|
|||
rm release/package.json
|
||||
rm release/*.map
|
||||
- name: Upload Equicord Stable
|
||||
if: ${{ github.ref_name == 'main' }}
|
||||
run: |
|
||||
gh release upload latest --clobber dist/release/*
|
||||
- name: Upload Plugins JSON to Equibored repo
|
||||
|
||||
- name: Upload Plugin JSONs to Equibored repo
|
||||
run: |
|
||||
git config --global user.name "GitHub-Actions"
|
||||
git config --global user.email actions@github.com
|
||||
|
|
|
|||
17
.github/workflows/nixosBuild.yml
vendored
17
.github/workflows/nixosBuild.yml
vendored
|
|
@ -5,9 +5,10 @@ on:
|
|||
- cron: 0 0 * * *
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
GITHUB_TOKEN: ${{ secrets.ETOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
permissions: write-all
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
|
|
@ -25,10 +26,14 @@ jobs:
|
|||
run: bun run buildWebStandalone
|
||||
- name: Build
|
||||
run: bun run buildStandalone
|
||||
- name: Generate plugin list
|
||||
run: bun run generatePluginJson dist/vencordplugins.json
|
||||
- name: Generate Equicord plugin list
|
||||
run: bun run generateEquicordPluginJson dist/equicordplugins.json
|
||||
|
||||
- name: Generate plugin lists
|
||||
run: |
|
||||
pnpm generatePluginJson dist/plugins.json
|
||||
pnpm generateEquicordPluginJson dist/equicordplugins.json
|
||||
pnpm generateVencordPluginJson dist/vencordplugins.json
|
||||
pnpm generateDevsList dist/devs.json
|
||||
|
||||
- name: Collect files to be released
|
||||
run: |
|
||||
cd dist
|
||||
|
|
|
|||
89
.github/workflows/publish.yml
vendored
Normal file
89
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
name: Release Browser Extension
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get package.json version
|
||||
id: pkg
|
||||
run: echo "version=v$(jq -r .version < package.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get latest Git tag
|
||||
id: tag
|
||||
run: |
|
||||
latest_tag=$(git tag --list 'v*' --sort=-v:refname | head -n 1 || echo "v0.0.0")
|
||||
echo "latest=$latest_tag" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if package.json version is newer
|
||||
id: check
|
||||
run: |
|
||||
pkg="${{ steps.pkg.outputs.version }}"
|
||||
latest="${{ steps.tag.outputs.latest }}"
|
||||
|
||||
echo "Latest tag: $latest"
|
||||
echo "Package version: $pkg"
|
||||
|
||||
if [ "$pkg" != "$latest" ] && [ "$(sort -V <<<"$latest"$'\n'"$pkg" | tail -n1)" = "$pkg" ]; then
|
||||
echo "release=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "release=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Stop if not newer
|
||||
if: steps.check.outputs.release == 'false'
|
||||
run: |
|
||||
echo "No new version to release (package.json ${{ steps.pkg.outputs.version }} <= latest tag ${{ steps.tag.outputs.latest }})"
|
||||
exit 0
|
||||
|
||||
- name: Create Tag
|
||||
if: steps.check.outputs.release == 'true'
|
||||
uses: butlerlogic/action-autotag@1.1.2
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
tag_prefix: "v"
|
||||
|
||||
- name: Setup PNPM
|
||||
if: steps.check.outputs.release == 'true'
|
||||
uses: pnpm/action-setup@v3
|
||||
|
||||
- name: Use Node.js 20
|
||||
if: steps.check.outputs.release == 'true'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check.outputs.release == 'true'
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build web
|
||||
if: steps.check.outputs.release == 'true'
|
||||
run: pnpm buildWebStandalone
|
||||
|
||||
- name: Publish extension
|
||||
if: steps.check.outputs.release == 'true'
|
||||
run: |
|
||||
cd dist/browser/chromium-unpacked
|
||||
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish
|
||||
env:
|
||||
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
|
||||
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
|
||||
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
|
||||
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
|
||||
3
.github/workflows/reportBrokenPlugins.yml
vendored
3
.github/workflows/reportBrokenPlugins.yml
vendored
|
|
@ -18,6 +18,9 @@ on:
|
|||
# # Every day at midnight
|
||||
- cron: 0 0 * * *
|
||||
|
||||
env:
|
||||
FORCE_COLOR: true
|
||||
|
||||
jobs:
|
||||
TestPlugins:
|
||||
name: Test Patches
|
||||
|
|
|
|||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -20,12 +20,14 @@ yarn-error.log*
|
|||
lerna-debug.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
.modules/
|
||||
src/userplugins
|
||||
|
||||
ExtensionCache/
|
||||
settings/
|
||||
|
||||
src/equicordplugins/usrpe
|
||||
/settings
|
||||
|
||||
# vencord companion module cache
|
||||
.modules
|
||||
|
||||
# wakatime related stuff
|
||||
.wakatime-project
|
||||
|
|
|
|||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
|
|
@ -4,6 +4,6 @@
|
|||
"EditorConfig.EditorConfig",
|
||||
"GregorBiswanger.json2ts",
|
||||
"stylelint.vscode-stylelint",
|
||||
"thororen.equicord-companion"
|
||||
"sadan.vencord-user-companion"
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -20,4 +20,8 @@
|
|||
}
|
||||
],
|
||||
"equicord-companion.showSidebar": true,
|
||||
"css.format.spaceAroundSelectorSeparator": true,
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "vscode.css-language-features"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,13 @@ 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
|
||||
|
|
@ -17,5 +19,5 @@ DON'T
|
|||
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 vigilantism
|
||||
and instead report the issue to a moderator! The best way is joining our [official Discord community](https://discord.gg/5Xh2W87egW)
|
||||
and instead report the issue to a moderator! The best way is joining our [official Discord community](https://equicord.org/discord)
|
||||
and opening a modmail ticket.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Contributing to Vencord
|
||||
# Contributing to Equicord
|
||||
|
||||
Vencord is a community project and welcomes any kind of contribution from anyone!
|
||||
Equicord is a community project and welcomes any kind of contribution from anyone!
|
||||
|
||||
We have development documentation for new contributors, which can be found at <https://docs.equicord.org>.
|
||||
|
||||
|
|
@ -17,10 +17,11 @@ Pull requests can be made either to the `main` or the `dev` branch. However, unl
|
|||
Writing a plugin is the primary way to contribute.
|
||||
|
||||
Before starting your plugin:
|
||||
|
||||
- Be in our discord
|
||||
- Check existing pull requests to see if someone is already working on a similar plugin
|
||||
- Check our [plugin requests tracker](https://discord.com/channels/1173279886065029291/1173334591302553631) to see if there is an existing request, or if the same idea has been rejected
|
||||
- If there isn't an existing request, [open one](https://discord.com/channels/1173279886065029291/1173334591302553631) yourself.
|
||||
- Check our [plugin requests tracker](https://discord.com/channels/1173279886065029291/1419347113745059961) to see if there is an existing request, or if the same idea has been rejected
|
||||
- If there isn't an existing request, [open one](https://discord.com/channels/1173279886065029291/1419347113745059961) yourself.
|
||||
and include that you'd like to work on this yourself. Then wait for feedback to see if the idea even has any chance of being accepted. Or maybe others have some ideas to improve it!
|
||||
- Familarise yourself with our plugin rules below to ensure your plugin is not banned
|
||||
|
||||
|
|
@ -46,5 +47,5 @@ Or if you notice any bugs or typos, feel free to fix them!
|
|||
|
||||
## Help out users in our Discord community
|
||||
|
||||
We have an open support channel in our [Discord community](https://discord.gg/5Xh2W87egW).
|
||||
We have an open support channel in our [Discord community](https://equicord.org/discord).
|
||||
Helping out users there is always appreciated! The more, the merrier.
|
||||
|
|
|
|||
223
README.md
223
README.md
|
|
@ -2,221 +2,15 @@
|
|||
|
||||
[](https://github.com/Equicord/Equibop)
|
||||
[](https://github.com/Equicord/Equicord/actions/workflows/test.yml)
|
||||
[](https://discord.gg/5Xh2W87egW)
|
||||
[](https://equicord.org/discord)
|
||||
|
||||
Equicord is a fork of [Vencord](https://github.com/Vendicated/Vencord), with over 300+ plugins.
|
||||
|
||||
You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, changes, chat or even support.
|
||||
You can join our [Discord server](https://equicord.org/discord) for commits, changes, chatting, or even support.
|
||||
|
||||
### Extra included plugins
|
||||
### Included Plugins
|
||||
|
||||
<details>
|
||||
<summary>186 additional plugins</summary>
|
||||
|
||||
### All Platforms
|
||||
|
||||
- AllCallTimers by MaxHerbold & D3SOX
|
||||
- AltKrispSwitch by newwares
|
||||
- AlwaysExpandProfile by thororen
|
||||
- AmITyping by MrDiamond
|
||||
- Anammox by Kyuuhachi
|
||||
- AudiobookShelfRPC by vMohammad
|
||||
- AtSomeone by Joona
|
||||
- BannersEverywhere by ImLvna & AutumnVN
|
||||
- BetterActivities by D3SOX, Arjix, AutumnVN
|
||||
- BetterAudioPlayer by Creations
|
||||
- BetterBanReasons by Inbestigator
|
||||
- BetterBlockedUsers by TheArmagan
|
||||
- BetterInvites by iamme
|
||||
- BetterPlusReacts by Joona
|
||||
- BetterQuickReact by Ven & Sqaaakoi
|
||||
- BlockKeywords by catcraft
|
||||
- BlockKrisp by D3SOX
|
||||
- BypassPinPrompt by thororen
|
||||
- BypassStatus by Inbestigator & thororen
|
||||
- ChannelBadges by Creations
|
||||
- ChannelTabs by TheSun, TheKodeToad, keifufu, Nickyux
|
||||
- CharacterCounter by Creations & Panniku
|
||||
- CleanChannelName by AutumnVN
|
||||
- ClientSideBlock by Samwich
|
||||
- CommandPalette by Ethan
|
||||
- CopyStickerLinks by Byeoon
|
||||
- CopyUserMention by Cortex & castdrian
|
||||
- CustomFolderIcons by sadan
|
||||
- CustomSounds by TheKodeToad & SpikeHD
|
||||
- CustomTimestamps by Rini & nvhrr
|
||||
- CustomUserColors by mochienya
|
||||
- CuteAnimeBoys by ShadyGoat
|
||||
- CuteNekos by echo
|
||||
- CutePats by thororen
|
||||
- DecodeBase64 by ThePirateStoner
|
||||
- Demonstration by Samwich
|
||||
- DisableAnimations by S€th
|
||||
- DisableCameras by Joona
|
||||
- DontFilterMe by Samwich
|
||||
- Encryptcord by Inbestigator
|
||||
- EquicordHelper by thororen & nyx
|
||||
- Equissant by SomeAspy & thororen
|
||||
- ExportContacts by dat_insanity
|
||||
- FakeProfileThemesAndEffects by ryan
|
||||
- CopyProfileColors by Crxa
|
||||
- FastDeleteChannels by thororen
|
||||
- FindReply by newwares
|
||||
- FixFileExtensions by thororen
|
||||
- FollowVoiceUser by TheArmagan
|
||||
- FontLoader by vmohammad
|
||||
- ForwardAnywhere by thororen
|
||||
- Freaky by nyx
|
||||
- FrequentQuickSwitcher by Samwich
|
||||
- FriendCodes by HypedDomi
|
||||
- FriendshipRanks by Samwich
|
||||
- FriendTags by Samwich
|
||||
- FullVcPfp by mochie
|
||||
- GensokyoRadioRPC by RyanCaoDev & Prince527
|
||||
- GifCollections by Aria & Creations
|
||||
- GifRoulette by Samwich
|
||||
- GitHubRepos by talhakf
|
||||
- Glide by Samwich
|
||||
- GlobalBadges by HypedDomi & Hosted by Wolfie
|
||||
- GoogleThat by Samwich
|
||||
- GuildPickerDumper by Cortex, Samwich, Synth, thororen
|
||||
- HideChatButtons by iamme
|
||||
- HideServers by bepvte
|
||||
- HolyNotes by Wolfie
|
||||
- HomeTyping by Samwich
|
||||
- HopOn by ImLvna
|
||||
- Husk by nin0dev
|
||||
- IconViewer by iamme
|
||||
- Identity by Samwich
|
||||
- IgnoreCalls by TheArmagan
|
||||
- IgnoreTerms by D3SOX
|
||||
- ImagePreview by Creations
|
||||
- ImgToGif by zyqunix
|
||||
- Ingtoninator by zyqunix
|
||||
- InRole by nin0dev
|
||||
- InstantScreenshare by HAHALOSAH & thororen
|
||||
- IRememberYou by zoodogood
|
||||
- JellyfinRichPresence by vMohammad
|
||||
- Jumpscare by Surgedevs
|
||||
- JumpToStart by Samwich
|
||||
- KeyboardSounds by HypedDomi
|
||||
- KeywordNotify by camila314 & x3rt
|
||||
- LastActive by Crxa
|
||||
- LimitMiddleClickPaste by no dev listed
|
||||
- LoginWithQR by nexpid
|
||||
- MediaPlaybackSpeed by D3SOX
|
||||
- Meow by Samwich
|
||||
- MessageBurst by port
|
||||
- MessageColors by Hen
|
||||
- MessageFetchTimer by GroupXyz
|
||||
- MessageLinkTooltip by Kyuuhachi
|
||||
- MessageLoggerEnhanced by Aria
|
||||
- MessageTranslate by Samwich
|
||||
- ModalFade by Kyuuhachi
|
||||
- MoreCommands by Arjix, echo, Samu
|
||||
- MoreKaomoji by JacobTm & voidbbg
|
||||
- MoreStickers by Leko & Arjix
|
||||
- MoreUserTags by Cyn, TheSun, RyanCaoDev, LordElias, AutumnVN, hen
|
||||
- Morse by zyqunix
|
||||
- Moyai by Megu & Nuckyz
|
||||
- NeverPausePreviews by vappstar
|
||||
- NewPluginsManager by Sqaaakoi
|
||||
- NoAppsAllowed by kvba
|
||||
- NoBulletPoints by Samwich
|
||||
- NoDeleteSafety by Samwich
|
||||
- NoMirroredCamera by Nyx
|
||||
- NoModalAnimation by AutumnVN
|
||||
- NoNitroUpsell by thororen
|
||||
- NoOnboarding by omaw & Glitch
|
||||
- NoRoleHeaders by Samwich
|
||||
- NotificationTitle by Kyuuhachi
|
||||
- PartyMode by UwUDev
|
||||
- PingNotifications by smuki
|
||||
- PinIcon by iamme
|
||||
- PlatformSpoofer by Drag
|
||||
- PolishWording by Samwich
|
||||
- PurgeMessages by bhop & nyx
|
||||
- QuestCompleter by Amia
|
||||
- QuestionMarkReplacement by nyx
|
||||
- Quoter by Samwich
|
||||
- RandomVoice by xijexo, omaw, thororen
|
||||
- Remix by MrDiamond
|
||||
- RemixMe by kvba
|
||||
- RepeatMessage by Tolgchu
|
||||
- ReplyPingControl by ant0n & MrDiamond
|
||||
- RPCEditor by Nyako & nin0dev
|
||||
- RPCStats by Samwich
|
||||
- SearchFix by Jaxx
|
||||
- SekaiStickers by MaiKokain
|
||||
- ServerSearch by camila314
|
||||
- ShowBadgesInChat by Inbestigator & KrystalSkull
|
||||
- SidebarChat by Joona
|
||||
- Signature by Ven, Rini, ImBanana, KrystalSkull
|
||||
- Slap by Korbo
|
||||
- Soggy by sliwka
|
||||
- SoundBoardLogger by Moxxie, fres, echo, maintained by thororen
|
||||
- SplitLargeMessages by Reycko
|
||||
- SpotifyActivityToggle by thororen
|
||||
- SpotifyLyrics by Joona
|
||||
- StatsfmPresence by Crxa
|
||||
- StatusPresets by iamme
|
||||
- SteamStatusSync by niko
|
||||
- StickerBlocker by Samwich
|
||||
- StreamingCodecDisabler by davidkra230
|
||||
- TalkInReverse by Tolgchu
|
||||
- TeX by Kyuuhachi
|
||||
- TextToSpeech by Samwich
|
||||
- ThemeLibrary by Fafa
|
||||
- Timezones by Aria
|
||||
- Title by Kyuuhachi
|
||||
- ToastNotifications by Skully, Ethan, Buzzy
|
||||
- ToggleVideoBind by mochie
|
||||
- TosuRPC by AutumnVN
|
||||
- Translate+ by Prince527 & Ven
|
||||
- UnitConverter by sadan
|
||||
- UnlimitedAccounts by thororen
|
||||
- UnreadCountBadge by Joona
|
||||
- UserPFP by nexpid & thororen
|
||||
- UwUifier by echo
|
||||
- VCNarratorCustom by Loukios, ported by example-git
|
||||
- VCPanelSettings by nin0dev
|
||||
- VCSupport by thororen
|
||||
- VencordRPC by AutumnVN
|
||||
- VideoSpeed by Samwich
|
||||
- ViewRawVariant by Kyuuhachi
|
||||
- VoiceChatUtilities by D3SOX
|
||||
- VoiceJoinMessages by Sqaaakoi & maintained by thororen
|
||||
- WallpaperFree by Joona
|
||||
- WebpackTarball by Kyuuhachi
|
||||
- WhitelistedEmojis by Creations
|
||||
- WhosWatching by fres
|
||||
- WigglyText by nexpid
|
||||
- Woof by Samwich
|
||||
- WriteUpperCase by Samwich & KrystalSkull
|
||||
- YoutubeDescription by arHSM
|
||||
|
||||
### Web Only
|
||||
|
||||
- None At This Time
|
||||
|
||||
### Vesktop & Equibop Only
|
||||
|
||||
- ScreenRecorder by AutumnVN
|
||||
|
||||
### Discord Desktop Only
|
||||
|
||||
- ClipsEnhancements by niko
|
||||
- MediaDownloader by Colorman
|
||||
- NoRPC by Cyn
|
||||
- StatusWhilePlaying by thororen
|
||||
|
||||
### Equicord Devbuilds Only
|
||||
|
||||
- FurudoSpeak by example-git
|
||||
- Shakespearean by vmohammad
|
||||
- VoiceChannelLog by Sqaaakoi & maintained by thororen
|
||||
|
||||
</details>
|
||||
Our included plugins can be found [here](https://equicord.org/plugins).
|
||||
|
||||
## Installing / Uninstalling
|
||||
|
||||
|
|
@ -258,16 +52,19 @@ cd Equicord
|
|||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```shell
|
||||
bun install
|
||||
```
|
||||
|
||||
Build Equicord:
|
||||
|
||||
```shell
|
||||
bun run build
|
||||
```
|
||||
|
||||
Inject Equicord into your client:
|
||||
|
||||
```shell
|
||||
bun run inject
|
||||
```
|
||||
|
|
@ -288,7 +85,7 @@ Thank you to [Vendicated](https://github.com/Vendicated) for creating [Vencord](
|
|||
|
||||
## Disclaimer
|
||||
|
||||
Discord is trademark of Discord Inc. and solely mentioned for the sake of descriptivity.
|
||||
Discord is trademark of Discord Inc., and solely mentioned for the sake of descriptivity.
|
||||
Mentioning it does not imply any affiliation with or endorsement by Discord Inc.
|
||||
Vencord is not connected to Equicord and as such, all donation links go to Vendicated's donation link.
|
||||
|
||||
|
|
@ -299,8 +96,8 @@ Client modifications are against Discord’s Terms of Service.
|
|||
|
||||
However, Discord is pretty indifferent about them and there are no known cases of users getting banned for using client mods! So you should generally be fine if you don’t use plugins that implement abusive behaviour. But no worries, all inbuilt plugins are safe to use!
|
||||
|
||||
Regardless, if your account is essential to you and getting disabled would be a disaster for you, you should probably not use any client mods (not exclusive to Equicord), just to be safe
|
||||
Regardless, if your account is essential to you and getting disabled would be a disaster for you, you should probably not use any client mods (not exclusive to Equicord), just to be safe.
|
||||
|
||||
Additionally, make sure not to post screenshots with Equicord in a server where you might get banned for it
|
||||
Additionally, make sure not to post screenshots with Equicord in a server where you might get banned for it.
|
||||
|
||||
</details>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ window.VencordNative = {
|
|||
),
|
||||
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore),
|
||||
getSystemValues: async () => ({}),
|
||||
|
||||
openFolder: async () => Promise.reject("themes:openFolder is not supported on web"),
|
||||
},
|
||||
|
||||
native: {
|
||||
|
|
@ -110,8 +112,10 @@ window.VencordNative = {
|
|||
}
|
||||
},
|
||||
set: async (s: Settings) => localStorage.setItem("EquicordSettings", JSON.stringify(s)),
|
||||
getSettingsDir: async () => "LocalStorage"
|
||||
getSettingsDir: async () => "LocalStorage",
|
||||
openFolder: async () => Promise.reject("settings:openFolder is not supported on web"),
|
||||
},
|
||||
|
||||
pluginHelpers: {} as any,
|
||||
csp: {} as any,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ style.href = browser.runtime.getURL("dist/Vencord.css");
|
|||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
document.documentElement.append(style);
|
||||
document.body.insertAdjacentElement("afterend", style);
|
||||
window.postMessage({
|
||||
type: "vencord:meta",
|
||||
meta: {
|
||||
|
|
|
|||
29
package.json
29
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "equicord",
|
||||
"private": "true",
|
||||
"version": "1.12.4",
|
||||
"version": "1.13.4",
|
||||
"description": "The other cutest Discord client mod",
|
||||
"homepage": "https://github.com/Equicord/Equicord#readme",
|
||||
"bugs": {
|
||||
|
|
@ -23,11 +23,14 @@
|
|||
"watch": "bun run build --watch",
|
||||
"dev": "bun run watch",
|
||||
"watchWeb": "bun run buildWeb --watch",
|
||||
"generateDevsList": "bun run scripts/generateDevsList.ts",
|
||||
"generatePluginJson": "bun run scripts/generatePluginList.ts",
|
||||
"generateEquicordPluginJson": "bun run scripts/generateEquicordPluginList.ts",
|
||||
"generateVencordPluginJson": "bun run scripts/generateVencordPluginList.ts",
|
||||
"generateTypes": "tspc --emitDeclarationOnly --declaration --outDir packages/vencord-types --allowJs false",
|
||||
"inject": "bun run scripts/runInstaller.mjs --install",
|
||||
"uninject": "bun run scripts/runInstaller.mjs --uninstall",
|
||||
"repair": "bun run scripts/runInstaller.mjs --repair",
|
||||
"lint": "eslint",
|
||||
"lint-styles": "stylelint \"src/**/*.css\" --ignore-pattern src/userplugins",
|
||||
"lint:fix": "bun run lint --fix",
|
||||
|
|
@ -39,26 +42,24 @@
|
|||
"@ffmpeg/ffmpeg": "^0.12.10",
|
||||
"@ffmpeg/util": "^0.12.1",
|
||||
"@intrnl/xxhash64": "^0.1.2",
|
||||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||
"@types/less": "^3.0.6",
|
||||
"@types/less": "^3.0.8",
|
||||
"@types/stylus": "^0.48.42",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
"fflate": "^0.8.2",
|
||||
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||
"idb": "8.0.3",
|
||||
"jsqr": "1.4.0",
|
||||
"idb": "8.0.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"nanoid": "^5.1.5",
|
||||
"socket.io": "^4.8.1",
|
||||
"usercss-meta": "^0.12.0",
|
||||
"openai": "^4.30.0",
|
||||
"virtual-merge": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/asar": "^3.2.10",
|
||||
"@electron/asar": "^4.0.1",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/chrome": "^0.0.312",
|
||||
"@types/diff": "^7.0.2",
|
||||
"@types/lodash": "^4.17.14",
|
||||
|
|
@ -66,8 +67,9 @@
|
|||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/yazl": "^2.4.5",
|
||||
"@vencord/discord-types": "link:packages/discord-types",
|
||||
"adm-zip": "^0.5.16",
|
||||
"diff": "^7.0.0",
|
||||
"discord-types": "^1.3.26",
|
||||
"esbuild": "^0.25.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
|
|
@ -78,23 +80,24 @@
|
|||
"highlight.js": "11.11.1",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"moment": "^2.22.2",
|
||||
"puppeteer-core": "^24.4.0",
|
||||
"puppeteer-core": "^24.19.0",
|
||||
"standalone-electron-types": "^34.2.0",
|
||||
"stylelint": "^16.17.0",
|
||||
"stylelint-config-standard": "^37.0.0",
|
||||
"ts-patch": "^3.3.0",
|
||||
"ts-pattern": "^5.6.0",
|
||||
"type-fest": "^4.41.0",
|
||||
"type-fest": "^4.38.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.28.0",
|
||||
"typescript-transform-paths": "^3.5.5",
|
||||
"zip-local": "^0.3.5",
|
||||
"zustand": "^3.7.2"
|
||||
"typescript-transform-paths": "^3.5.5"
|
||||
},
|
||||
"packageManager": "bun@1.1.0",
|
||||
"trustedDependencies": [
|
||||
"esbuild"
|
||||
],
|
||||
"overrides": {
|
||||
"@eslint/plugin-kit": ">=0.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"bun": ">=1.0.0"
|
||||
|
|
|
|||
1
packages/discord-types/.npmignore
Normal file
1
packages/discord-types/.npmignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
||||
1
packages/discord-types/CONTRIBUTING.md
Normal file
1
packages/discord-types/CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Hint: https://docs.discord.food is an incredible resource and allows you to copy paste complete enums and interfaces
|
||||
165
packages/discord-types/LICENSE
Normal file
165
packages/discord-types/LICENSE
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
42
packages/discord-types/README.md
Normal file
42
packages/discord-types/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Discord Types
|
||||
|
||||
This package provides TypeScript types for the Webpack modules of Discord's web app.
|
||||
|
||||
While it was primarily created for Vencord, other client mods could also benefit from this, so it is published as a standalone package!
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install -D @vencord/discord-types
|
||||
yarn add -D @vencord/discord-types
|
||||
pnpm add -D @vencord/discord-types
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
```ts
|
||||
import type { UserStore } from "@vencord/discord-types";
|
||||
|
||||
const userStore: UserStore = findStore("UserStore"); // findStore is up to you to implement, this library only provides types and no runtime code
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
This library also exports some const enums that you can use from Typescript code:
|
||||
```ts
|
||||
import { ApplicationCommandType } from "@vencord/discord-types/enums";
|
||||
|
||||
console.log(ApplicationCommandType.CHAT_INPUT); // 1
|
||||
```
|
||||
|
||||
### License
|
||||
|
||||
This package is licensed under the [LGPL-3.0](./LICENSE) (or later) license.
|
||||
|
||||
A very short summary of the license is that you can use this package as a library in both open source and closed source projects,
|
||||
similar to an MIT-licensed project.
|
||||
However, if you modify the code of this package, you must release source code of your modified version under the same license.
|
||||
|
||||
### Credit
|
||||
|
||||
This package was inspired by Swishilicous' [discord-types](https://www.npmjs.com/package/discord-types) package.
|
||||
30
packages/discord-types/enums/activity.ts
Normal file
30
packages/discord-types/enums/activity.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
CUSTOM_STATUS = 4,
|
||||
COMPETING = 5,
|
||||
HANG_STATUS = 6
|
||||
}
|
||||
|
||||
export const enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
/** @deprecated */
|
||||
SPECTATE = 1 << 2,
|
||||
/** @deprecated */
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5,
|
||||
PARTY_PRIVACY_FRIENDS = 1 << 6,
|
||||
PARTY_PRIVACY_VOICE_CHANNEL = 1 << 7,
|
||||
EMBEDDED = 1 << 8,
|
||||
CONTEXTLESS = 1 << 9
|
||||
}
|
||||
|
||||
export const enum ActivityStatusDisplayType {
|
||||
NAME = 0,
|
||||
STATE = 1,
|
||||
DETAILS = 2
|
||||
}
|
||||
15
packages/discord-types/enums/channel.ts
Normal file
15
packages/discord-types/enums/channel.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export const enum ChannelType {
|
||||
GUILD_TEXT = 0,
|
||||
DM = 1,
|
||||
GUILD_VOICE = 2,
|
||||
GROUP_DM = 3,
|
||||
GUILD_CATEGORY = 4,
|
||||
GUILD_ANNOUNCEMENT = 5,
|
||||
ANNOUNCEMENT_THREAD = 10,
|
||||
PUBLIC_THREAD = 11,
|
||||
PRIVATE_THREAD = 12,
|
||||
GUILD_STAGE_VOICE = 13,
|
||||
GUILD_DIRECTORY = 14,
|
||||
GUILD_FORUM = 15,
|
||||
GUILD_MEDIA = 16
|
||||
}
|
||||
32
packages/discord-types/enums/commands.ts
Normal file
32
packages/discord-types/enums/commands.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
export const enum ApplicationCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
MENTIONABLE = 9,
|
||||
NUMBER = 10,
|
||||
ATTACHMENT = 11,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandInputType {
|
||||
BUILT_IN = 0,
|
||||
BUILT_IN_TEXT = 1,
|
||||
BUILT_IN_INTEGRATION = 2,
|
||||
BOT = 3,
|
||||
PLACEHOLDER = 4,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandType {
|
||||
CHAT_INPUT = 1,
|
||||
USER = 2,
|
||||
MESSAGE = 3,
|
||||
}
|
||||
|
||||
export const enum ApplicationIntegrationType {
|
||||
GUILD_INSTALL = 0,
|
||||
USER_INSTALL = 1
|
||||
}
|
||||
5
packages/discord-types/enums/index.ts
Normal file
5
packages/discord-types/enums/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./activity";
|
||||
export * from "./channel";
|
||||
export * from "./commands";
|
||||
export * from "./messages";
|
||||
export * from "./misc";
|
||||
596
packages/discord-types/enums/messages.ts
Normal file
596
packages/discord-types/enums/messages.ts
Normal file
|
|
@ -0,0 +1,596 @@
|
|||
export const enum StickerType {
|
||||
/** an official sticker in a pack */
|
||||
STANDARD = 1,
|
||||
/** a sticker uploaded to a guild for the guild's members */
|
||||
GUILD = 2
|
||||
}
|
||||
|
||||
export const enum StickerFormatType {
|
||||
PNG = 1,
|
||||
APNG = 2,
|
||||
LOTTIE = 3,
|
||||
GIF = 4
|
||||
}
|
||||
|
||||
export const enum MessageType {
|
||||
/**
|
||||
* A default message (see below)
|
||||
*
|
||||
* Value: 0
|
||||
* Name: DEFAULT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
DEFAULT = 0,
|
||||
/**
|
||||
* A message sent when a user is added to a group DM or thread
|
||||
*
|
||||
* Value: 1
|
||||
* Name: RECIPIENT_ADD
|
||||
* Rendered Content: "{author} added {mentions [0] } to the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_ADD = 1,
|
||||
/**
|
||||
* A message sent when a user is removed from a group DM or thread
|
||||
*
|
||||
* Value: 2
|
||||
* Name: RECIPIENT_REMOVE
|
||||
* Rendered Content: "{author} removed {mentions [0] } from the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_REMOVE = 2,
|
||||
/**
|
||||
* A message sent when a user creates a call in a private channel
|
||||
*
|
||||
* Value: 3
|
||||
* Name: CALL
|
||||
* Rendered Content: participated ? "{author} started a call{ended ? " that lasted {duration}" : " — Join the call"}." : "You missed a call from {author} that lasted {duration}."
|
||||
* Deletable: false
|
||||
*/
|
||||
CALL = 3,
|
||||
/**
|
||||
* A message sent when a group DM or thread's name is changed
|
||||
*
|
||||
* Value: 4
|
||||
* Name: CHANNEL_NAME_CHANGE
|
||||
* Rendered Content: "{author} changed the {is_forum ? "post title" : "channel name"}: {content} "
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_NAME_CHANGE = 4,
|
||||
/**
|
||||
* A message sent when a group DM's icon is changed
|
||||
*
|
||||
* Value: 5
|
||||
* Name: CHANNEL_ICON_CHANGE
|
||||
* Rendered Content: "{author} changed the channel icon."
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_ICON_CHANGE = 5,
|
||||
/**
|
||||
* A message sent when a message is pinned in a channel
|
||||
*
|
||||
* Value: 6
|
||||
* Name: CHANNEL_PINNED_MESSAGE
|
||||
* Rendered Content: "{author} pinned a message to this channel."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_PINNED_MESSAGE = 6,
|
||||
/**
|
||||
* A message sent when a user joins a guild
|
||||
*
|
||||
* Value: 7
|
||||
* Name: USER_JOIN
|
||||
* Rendered Content: See user join message type , obtained via the formula timestamp_ms % 13
|
||||
* Deletable: true
|
||||
*/
|
||||
USER_JOIN = 7,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild
|
||||
*
|
||||
* Value: 8
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION = 8,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 1
|
||||
*
|
||||
* Value: 9
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_1
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 1! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 2
|
||||
*
|
||||
* Value: 10
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_2
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 2! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 3
|
||||
*
|
||||
* Value: 11
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_3
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 3! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11,
|
||||
/**
|
||||
* A message sent when a news channel is followed
|
||||
*
|
||||
* Value: 12
|
||||
* Name: CHANNEL_FOLLOW_ADD
|
||||
* Rendered Content: "{author} has added {content} to this channel. Its most important updates will show up here."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_FOLLOW_ADD = 12,
|
||||
/**
|
||||
* A message sent when a guild is disqualified from discovery
|
||||
*
|
||||
* Value: 14
|
||||
* Name: GUILD_DISCOVERY_DISQUALIFIED
|
||||
* Rendered Content: "This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_DISQUALIFIED = 14,
|
||||
/**
|
||||
* A message sent when a guild requalifies for discovery
|
||||
*
|
||||
* Value: 15
|
||||
* Name: GUILD_DISCOVERY_REQUALIFIED
|
||||
* Rendered Content: "This server is eligible for Server Discovery again and has been automatically relisted!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_REQUALIFIED = 15,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for a week
|
||||
*
|
||||
* Value: 16
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for 3 weeks
|
||||
*
|
||||
* Value: 17
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17,
|
||||
/**
|
||||
* A message sent when a thread is created
|
||||
*
|
||||
* Value: 18
|
||||
* Name: THREAD_CREATED
|
||||
* Rendered Content: "{author} started a thread: {content} . See all threads."
|
||||
* Deletable: true
|
||||
*/
|
||||
THREAD_CREATED = 18,
|
||||
/**
|
||||
* A message sent when a user replies to a message
|
||||
*
|
||||
* Value: 19
|
||||
* Name: REPLY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPLY = 19,
|
||||
/**
|
||||
* A message sent when a user uses a slash command
|
||||
*
|
||||
* Value: 20
|
||||
* Name: CHAT_INPUT_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHAT_INPUT_COMMAND = 20,
|
||||
/**
|
||||
* A message sent when a thread starter message is added to a thread
|
||||
*
|
||||
* Value: 21
|
||||
* Name: THREAD_STARTER_MESSAGE
|
||||
* Rendered Content: "{referenced_message?.content}" ?? "Sorry, we couldn't load the first message in this thread"
|
||||
* Deletable: false
|
||||
*/
|
||||
THREAD_STARTER_MESSAGE = 21,
|
||||
/**
|
||||
* A message sent to remind users to invite friends to a guild
|
||||
*
|
||||
* Value: 22
|
||||
* Name: GUILD_INVITE_REMINDER
|
||||
* Rendered Content: "Wondering who to invite?\nStart by inviting anyone who can help you build the server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INVITE_REMINDER = 22,
|
||||
/**
|
||||
* A message sent when a user uses a context menu command
|
||||
*
|
||||
* Value: 23
|
||||
* Name: CONTEXT_MENU_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CONTEXT_MENU_COMMAND = 23,
|
||||
/**
|
||||
* A message sent when auto moderation takes an action
|
||||
*
|
||||
* Value: 24
|
||||
* Name: AUTO_MODERATION_ACTION
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true 1
|
||||
*/
|
||||
AUTO_MODERATION_ACTION = 24,
|
||||
/**
|
||||
* A message sent when a user purchases or renews a role subscription
|
||||
*
|
||||
* Value: 25
|
||||
* Name: ROLE_SUBSCRIPTION_PURCHASE
|
||||
* Rendered Content: "{author} {is_renewal ? "renewed" : "joined"} {role_subscription.tier_name} and has been a subscriber of {guild} for {role_subscription.total_months_subscribed} month(?s)!"
|
||||
* Deletable: true
|
||||
*/
|
||||
ROLE_SUBSCRIPTION_PURCHASE = 25,
|
||||
/**
|
||||
* A message sent when a user is upsold to a premium interaction
|
||||
*
|
||||
* Value: 26
|
||||
* Name: INTERACTION_PREMIUM_UPSELL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
INTERACTION_PREMIUM_UPSELL = 26,
|
||||
/**
|
||||
* A message sent when a stage channel starts
|
||||
*
|
||||
* Value: 27
|
||||
* Name: STAGE_START
|
||||
* Rendered Content: "{author} started {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_START = 27,
|
||||
/**
|
||||
* A message sent when a stage channel ends
|
||||
*
|
||||
* Value: 28
|
||||
* Name: STAGE_END
|
||||
* Rendered Content: "{author} ended {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_END = 28,
|
||||
/**
|
||||
* A message sent when a user starts speaking in a stage channel
|
||||
*
|
||||
* Value: 29
|
||||
* Name: STAGE_SPEAKER
|
||||
* Rendered Content: "{author} is now a speaker."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_SPEAKER = 29,
|
||||
/**
|
||||
* A message sent when a user raises their hand in a stage channel
|
||||
*
|
||||
* Value: 30
|
||||
* Name: STAGE_RAISE_HAND
|
||||
* Rendered Content: "{author} requested to speak."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_RAISE_HAND = 30,
|
||||
/**
|
||||
* A message sent when a stage channel's topic is changed
|
||||
*
|
||||
* Value: 31
|
||||
* Name: STAGE_TOPIC
|
||||
* Rendered Content: "{author} changed the Stage topic: {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_TOPIC = 31,
|
||||
/**
|
||||
* A message sent when a user purchases an application premium subscription
|
||||
*
|
||||
* Value: 32
|
||||
* Name: GUILD_APPLICATION_PREMIUM_SUBSCRIPTION
|
||||
* Rendered Content: "{author} upgraded {application ?? "a deleted application"} to premium for this server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_APPLICATION_PREMIUM_SUBSCRIPTION = 32,
|
||||
/**
|
||||
* A message sent when a user gifts a premium (Nitro) referral
|
||||
*
|
||||
* Value: 35
|
||||
* Name: PREMIUM_REFERRAL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_REFERRAL = 35,
|
||||
/**
|
||||
* A message sent when a user enabled lockdown for the guild
|
||||
*
|
||||
* Value: 36
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_ENABLED
|
||||
* Rendered Content: "{author} enabled security actions until {content}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_ENABLED = 36,
|
||||
/**
|
||||
* A message sent when a user disables lockdown for the guild
|
||||
*
|
||||
* Value: 37
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_DISABLED
|
||||
* Rendered Content: "{author} disabled security actions."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_DISABLED = 37,
|
||||
/**
|
||||
* A message sent when a user reports a raid for the guild
|
||||
*
|
||||
* Value: 38
|
||||
* Name: GUILD_INCIDENT_REPORT_RAID
|
||||
* Rendered Content: "{author} reported a raid in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_RAID = 38,
|
||||
/**
|
||||
* A message sent when a user reports a false alarm for the guild
|
||||
*
|
||||
* Value: 39
|
||||
* Name: GUILD_INCIDENT_REPORT_FALSE_ALARM
|
||||
* Rendered Content: "{author} reported a false alarm in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_FALSE_ALARM = 39,
|
||||
/**
|
||||
* A message sent when no one sends a message in the current channel for 1 hour
|
||||
*
|
||||
* Value: 40
|
||||
* Name: GUILD_DEADCHAT_REVIVE_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DEADCHAT_REVIVE_PROMPT = 40,
|
||||
/**
|
||||
* A message sent when a user buys another user a gift
|
||||
*
|
||||
* Value: 41
|
||||
* Name: CUSTOM_GIFT
|
||||
* Rendered Content: Special embed rendered from embeds[0].url and gift_info
|
||||
* Deletable: true
|
||||
*/
|
||||
CUSTOM_GIFT = 41,
|
||||
/**
|
||||
* Value: 42
|
||||
* Name: GUILD_GAMING_STATS_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_GAMING_STATS_PROMPT = 42,
|
||||
/**
|
||||
* A message sent when a user purchases a guild product
|
||||
*
|
||||
* Value: 44
|
||||
* Name: PURCHASE_NOTIFICATION
|
||||
* Rendered Content: "{author} has purchased {purchase_notification.guild_product_purchase.product_name}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PURCHASE_NOTIFICATION = 44,
|
||||
/**
|
||||
* A message sent when a poll is finalized
|
||||
*
|
||||
* Value: 46
|
||||
* Name: POLL_RESULT
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true
|
||||
*/
|
||||
POLL_RESULT = 46,
|
||||
/**
|
||||
* A message sent by the Discord Updates account when a new changelog is posted
|
||||
*
|
||||
* Value: 47
|
||||
* Name: CHANGELOG
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANGELOG = 47,
|
||||
/**
|
||||
* A message sent when a Nitro promotion is triggered
|
||||
*
|
||||
* Value: 48
|
||||
* Name: NITRO_NOTIFICATION
|
||||
* Rendered Content: Special embed rendered from content
|
||||
* Deletable: true
|
||||
*/
|
||||
NITRO_NOTIFICATION = 48,
|
||||
/**
|
||||
* A message sent when a voice channel is linked to a lobby
|
||||
*
|
||||
* Value: 49
|
||||
* Name: CHANNEL_LINKED_TO_LOBBY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_LINKED_TO_LOBBY = 49,
|
||||
/**
|
||||
* A local-only ephemeral message sent when a user is prompted to gift Nitro to a friend on their friendship anniversary
|
||||
*
|
||||
* Value: 50
|
||||
* Name: GIFTING_PROMPT
|
||||
* Rendered Content: Special embed
|
||||
* Deletable: true
|
||||
*/
|
||||
GIFTING_PROMPT = 50,
|
||||
/**
|
||||
* A local-only message sent when a user receives an in-game message NUX
|
||||
*
|
||||
* Value: 51
|
||||
* Name: IN_GAME_MESSAGE_NUX
|
||||
* Rendered Content: "{author} messaged you from {application.name}. In-game chat may not include rich messaging features such as images, polls, or apps. Learn More "
|
||||
* Deletable: true
|
||||
*/
|
||||
IN_GAME_MESSAGE_NUX = 51,
|
||||
/**
|
||||
* A message sent when a user accepts a guild join request
|
||||
*
|
||||
* Value: 52
|
||||
* Name: GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was approved! Welcome!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION = 52,
|
||||
/**
|
||||
* A message sent when a user rejects a guild join request
|
||||
*
|
||||
* Value: 53
|
||||
* Name: GUILD_JOIN_REQUEST_REJECT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was rejected."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_REJECT_NOTIFICATION = 53,
|
||||
/**
|
||||
* A message sent when a user withdraws a guild join request
|
||||
*
|
||||
* Value: 54
|
||||
* Name: GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} has been withdrawn."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION = 54,
|
||||
/**
|
||||
* A message sent when a user upgrades to HD streaming
|
||||
*
|
||||
* Value: 55
|
||||
* Name: HD_STREAMING_UPGRADED
|
||||
* Rendered Content: "{author} activated HD Splash Potion "
|
||||
* Deletable: true
|
||||
*/
|
||||
HD_STREAMING_UPGRADED = 55,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by deleting the offending message
|
||||
*
|
||||
* Value: 58
|
||||
* Name: REPORT_TO_MOD_DELETED_MESSAGE
|
||||
* Rendered Content: "{author} deleted the message"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_DELETED_MESSAGE = 58,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by timing out the offending user
|
||||
*
|
||||
* Value: 59
|
||||
* Name: REPORT_TO_MOD_TIMEOUT_USER
|
||||
* Rendered Content: "{author} timed out {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_TIMEOUT_USER = 59,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by kicking the offending user
|
||||
*
|
||||
* Value: 60
|
||||
* Name: REPORT_TO_MOD_KICK_USER
|
||||
* Rendered Content: "{author} kicked {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_KICK_USER = 60,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by banning the offending user
|
||||
*
|
||||
* Value: 61
|
||||
* Name: REPORT_TO_MOD_BAN_USER
|
||||
* Rendered Content: "{author} banned {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_BAN_USER = 61,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report
|
||||
*
|
||||
* Value: 62
|
||||
* Name: REPORT_TO_MOD_CLOSED_REPORT
|
||||
* Rendered Content: "{author} resolved this flag"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_CLOSED_REPORT = 62,
|
||||
/**
|
||||
* A message sent when a user adds a new emoji to a guild
|
||||
*
|
||||
* Value: 63
|
||||
* Name: EMOJI_ADDED
|
||||
* Rendered Content: "{author} added a new emoji, {content} :{emoji.name}: "
|
||||
* Deletable: true
|
||||
*/
|
||||
EMOJI_ADDED = 63,
|
||||
}
|
||||
|
||||
export const enum MessageFlags {
|
||||
/**
|
||||
* Message has been published to subscribed channels (via Channel Following)
|
||||
*
|
||||
* Value: 1 << 0
|
||||
*/
|
||||
CROSSPOSTED = 1 << 0,
|
||||
/**
|
||||
* Message originated from a message in another channel (via Channel Following)
|
||||
*/
|
||||
IS_CROSSPOST = 1 << 1,
|
||||
/**
|
||||
* Embeds will not be included when serializing this message
|
||||
*/
|
||||
SUPPRESS_EMBEDS = 1 << 2,
|
||||
/**
|
||||
* Source message for this crosspost has been deleted (via Channel Following)
|
||||
*/
|
||||
SOURCE_MESSAGE_DELETED = 1 << 3,
|
||||
/**
|
||||
* Message came from the urgent message system
|
||||
*/
|
||||
URGENT = 1 << 4,
|
||||
/**
|
||||
* Message has an associated thread, with the same ID as the message
|
||||
*/
|
||||
HAS_THREAD = 1 << 5,
|
||||
/**
|
||||
* Message is only visible to the user who invoked the interaction
|
||||
*/
|
||||
EPHEMERAL = 1 << 6,
|
||||
/**
|
||||
* Message is an interaction response and the bot is "thinking"
|
||||
*/
|
||||
LOADING = 1 << 7,
|
||||
/**
|
||||
* Some roles were not mentioned and added to the thread
|
||||
*/
|
||||
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8,
|
||||
/**
|
||||
* Message is hidden from the guild's feed
|
||||
*/
|
||||
GUILD_FEED_HIDDEN = 1 << 9,
|
||||
/**
|
||||
* Message contains a link that impersonates Discord
|
||||
*/
|
||||
SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10,
|
||||
/**
|
||||
* Message will not trigger push and desktop notifications
|
||||
*/
|
||||
SUPPRESS_NOTIFICATIONS = 1 << 12,
|
||||
/**
|
||||
* Message's audio attachment is rendered as a voice message
|
||||
*/
|
||||
IS_VOICE_MESSAGE = 1 << 13,
|
||||
/**
|
||||
* Message has a forwarded message snapshot attached
|
||||
*/
|
||||
HAS_SNAPSHOT = 1 << 14,
|
||||
/**
|
||||
* Message contains components from version 2 of the UI kit
|
||||
*/
|
||||
IS_COMPONENTS_V2 = 1 << 15,
|
||||
/**
|
||||
* Message was triggered by the social layer integration
|
||||
*/
|
||||
SENT_BY_SOCIAL_LAYER_INTEGRATION = 1 << 16,
|
||||
}
|
||||
4
packages/discord-types/enums/misc.ts
Normal file
4
packages/discord-types/enums/misc.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const enum CloudUploadPlatform {
|
||||
REACT_NATIVE = 0,
|
||||
WEB = 1,
|
||||
}
|
||||
22
packages/discord-types/package.json
Normal file
22
packages/discord-types/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@vencord/discord-types",
|
||||
"author": "Vencord Contributors",
|
||||
"private": false,
|
||||
"description": "Typescript definitions for the webpack modules of the Discord Web app",
|
||||
"version": "1.0.0",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"types": "src/index.d.ts",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Vendicated/Vencord.git",
|
||||
"directory": "packages/discord-types"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.22.2",
|
||||
"type-fest": "^4.41.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.10"
|
||||
}
|
||||
}
|
||||
16
packages/discord-types/src/classes.d.ts
vendored
Normal file
16
packages/discord-types/src/classes.d.ts
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export interface ButtonWrapperClasses {
|
||||
hoverScale: string;
|
||||
buttonWrapper: string;
|
||||
button: string;
|
||||
iconMask: string;
|
||||
buttonContent: string;
|
||||
icon: string;
|
||||
pulseIcon: string;
|
||||
pulseButton: string;
|
||||
notificationDot: string;
|
||||
sparkleContainer: string;
|
||||
sparkleStar: string;
|
||||
sparklePlus: string;
|
||||
sparkle: string;
|
||||
active: string;
|
||||
}
|
||||
47
packages/discord-types/src/common/Activity.d.ts
vendored
Normal file
47
packages/discord-types/src/common/Activity.d.ts
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "../../enums";
|
||||
|
||||
export interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
export interface ActivityButton {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
created_at?: number;
|
||||
id?: string;
|
||||
name: string;
|
||||
application_id: string;
|
||||
type: ActivityType;
|
||||
emoji?: {
|
||||
animated: boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
state?: string;
|
||||
state_url?: string;
|
||||
details?: string;
|
||||
details_url?: string;
|
||||
url?: string;
|
||||
flags: ActivityFlags;
|
||||
status_display_type?: ActivityStatusDisplayType;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: string[];
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
platform?: string;
|
||||
party?: {
|
||||
id?: string;
|
||||
size?: [number, number];
|
||||
};
|
||||
}
|
||||
23
packages/discord-types/src/common/Application.d.ts
vendored
Normal file
23
packages/discord-types/src/common/Application.d.ts
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { User } from "./User";
|
||||
|
||||
export interface Application {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
type: number | null;
|
||||
icon: string | null | undefined;
|
||||
is_discoverable: boolean;
|
||||
is_monetized: boolean;
|
||||
is_verified: boolean;
|
||||
bot?: User;
|
||||
deeplink_uri?: string;
|
||||
flags?: number;
|
||||
privacy_policy_url?: string;
|
||||
terms_of_service_url?: string;
|
||||
install_params?: ApplicationInstallParams;
|
||||
}
|
||||
|
||||
export interface ApplicationInstallParams {
|
||||
permissions: string | null;
|
||||
scopes: string[];
|
||||
}
|
||||
84
packages/discord-types/src/common/Channel.d.ts
vendored
Normal file
84
packages/discord-types/src/common/Channel.d.ts
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { DiscordRecord } from "./Record";
|
||||
|
||||
export class Channel extends DiscordRecord {
|
||||
constructor(channel: object);
|
||||
application_id: number | undefined;
|
||||
bitrate: number;
|
||||
defaultAutoArchiveDuration: number | undefined;
|
||||
flags: number;
|
||||
guild_id: string;
|
||||
icon: string;
|
||||
id: string;
|
||||
lastMessageId: string;
|
||||
lastPinTimestamp: string | undefined;
|
||||
member: unknown;
|
||||
memberCount: number | undefined;
|
||||
memberIdsPreview: string[] | undefined;
|
||||
memberListId: unknown;
|
||||
messageCount: number | undefined;
|
||||
name: string;
|
||||
nicks: Record<string, unknown>;
|
||||
nsfw: boolean;
|
||||
originChannelId: unknown;
|
||||
ownerId: string;
|
||||
parent_id: string;
|
||||
permissionOverwrites: {
|
||||
[role: string]: {
|
||||
id: string;
|
||||
type: number;
|
||||
deny: bigint;
|
||||
allow: bigint;
|
||||
};
|
||||
};
|
||||
position: number;
|
||||
rateLimitPerUser: number;
|
||||
rawRecipients: {
|
||||
id: string;
|
||||
avatar: string;
|
||||
username: string;
|
||||
public_flags: number;
|
||||
discriminator: string;
|
||||
}[];
|
||||
recipients: string[];
|
||||
rtcRegion: string;
|
||||
threadMetadata: {
|
||||
locked: boolean;
|
||||
archived: boolean;
|
||||
invitable: boolean;
|
||||
createTimestamp: string | undefined;
|
||||
autoArchiveDuration: number;
|
||||
archiveTimestamp: string | undefined;
|
||||
};
|
||||
topic: string;
|
||||
type: number;
|
||||
userLimit: number;
|
||||
videoQualityMode: undefined;
|
||||
|
||||
get accessPermissions(): bigint;
|
||||
get lastActiveTimestamp(): number;
|
||||
|
||||
computeLurkerPermissionsAllowList(): unknown;
|
||||
getApplicationId(): unknown;
|
||||
getGuildId(): string;
|
||||
getRecipientId(): unknown;
|
||||
hasFlag(flag: number): boolean;
|
||||
isActiveThread(): boolean;
|
||||
isArchivedThread(): boolean;
|
||||
isCategory(): boolean;
|
||||
isDM(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isForumChannel(): boolean;
|
||||
isGroupDM(): boolean;
|
||||
isGuildStageVoice(): boolean;
|
||||
isGuildVoice(): boolean;
|
||||
isGuildVocal(): boolean;
|
||||
isListenModeCapable(): boolean;
|
||||
isManaged(): boolean;
|
||||
isMultiUserDM(): boolean;
|
||||
isNSFW(): boolean;
|
||||
isOwner(): boolean;
|
||||
isPrivate(): boolean;
|
||||
isSystemDM(): boolean;
|
||||
isThread(): boolean;
|
||||
isVocal(): boolean;
|
||||
}
|
||||
64
packages/discord-types/src/common/Guild.d.ts
vendored
Normal file
64
packages/discord-types/src/common/Guild.d.ts
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { Role } from './Role';
|
||||
import { DiscordRecord } from './Record';
|
||||
|
||||
// copy(Object.keys(findByProps("CREATOR_MONETIZABLE")).map(JSON.stringify).join("|"))
|
||||
export type GuildFeatures =
|
||||
"INVITE_SPLASH" | "VIP_REGIONS" | "VANITY_URL" | "MORE_EMOJI" | "MORE_STICKERS" | "MORE_SOUNDBOARD" | "VERIFIED" | "COMMERCE" | "DISCOVERABLE" | "COMMUNITY" | "FEATURABLE" | "NEWS" | "HUB" | "PARTNERED" | "ANIMATED_ICON" | "BANNER" | "ENABLED_DISCOVERABLE_BEFORE" | "WELCOME_SCREEN_ENABLED" | "MEMBER_VERIFICATION_GATE_ENABLED" | "PREVIEW_ENABLED" | "ROLE_SUBSCRIPTIONS_ENABLED" | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE" | "CREATOR_MONETIZABLE" | "CREATOR_MONETIZABLE_PROVISIONAL" | "CREATOR_MONETIZABLE_WHITEGLOVE" | "CREATOR_MONETIZABLE_DISABLED" | "CREATOR_MONETIZABLE_RESTRICTED" | "CREATOR_STORE_PAGE" | "CREATOR_MONETIZABLE_PENDING_NEW_OWNER_ONBOARDING" | "PRODUCTS_AVAILABLE_FOR_PURCHASE" | "GUILD_WEB_PAGE_VANITY_URL" | "THREADS_ENABLED" | "THREADS_ENABLED_TESTING" | "NEW_THREAD_PERMISSIONS" | "ROLE_ICONS" | "TEXT_IN_STAGE_ENABLED" | "TEXT_IN_VOICE_ENABLED" | "HAS_DIRECTORY_ENTRY" | "ANIMATED_BANNER" | "LINKED_TO_HUB" | "EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT" | "GUILD_HOME_DEPRECATION_OVERRIDE" | "GUILD_HOME_TEST" | "GUILD_HOME_OVERRIDE" | "GUILD_ONBOARDING" | "GUILD_ONBOARDING_EVER_ENABLED" | "GUILD_ONBOARDING_HAS_PROMPTS" | "GUILD_SERVER_GUIDE" | "INTERNAL_EMPLOYEE_ONLY" | "AUTO_MODERATION" | "INVITES_DISABLED" | "BURST_REACTIONS" | "SOUNDBOARD" | "SHARD" | "ACTIVITY_FEED_ENABLED_BY_USER" | "ACTIVITY_FEED_DISABLED_BY_USER" | "SUMMARIES_ENABLED_GA" | "LEADERBOARD_ENABLED" | "SUMMARIES_ENABLED_BY_USER" | "SUMMARIES_OPT_OUT_EXPERIENCE" | "CHANNEL_ICON_EMOJIS_GENERATED" | "NON_COMMUNITY_RAID_ALERTS" | "RAID_ALERTS_DISABLED" | "AUTOMOD_TRIGGER_USER_PROFILE" | "ENABLED_MODERATION_EXPERIENCE_FOR_NON_COMMUNITY" | "GUILD_PRODUCTS_ALLOW_ARCHIVED_FILE" | "CLAN" | "MEMBER_VERIFICATION_MANUAL_APPROVAL" | "FORWARDING_DISABLED" | "MEMBER_VERIFICATION_ROLLOUT_TEST" | "AUDIO_BITRATE_128_KBPS" | "AUDIO_BITRATE_256_KBPS" | "AUDIO_BITRATE_384_KBPS" | "VIDEO_BITRATE_ENHANCED" | "MAX_FILE_SIZE_50_MB" | "MAX_FILE_SIZE_100_MB" | "GUILD_TAGS" | "ENHANCED_ROLE_COLORS" | "PREMIUM_TIER_3_OVERRIDE" | "REPORT_TO_MOD_PILOT" | "TIERLESS_BOOSTING_SYSTEM_MESSAGE";
|
||||
export type GuildPremiumFeatures =
|
||||
"ANIMATED_ICON" | "STAGE_CHANNEL_VIEWERS_150" | "ROLE_ICONS" | "GUILD_TAGS" | "BANNER" | "MAX_FILE_SIZE_50_MB" | "VIDEO_QUALITY_720_60FPS" | "STAGE_CHANNEL_VIEWERS_50" | "VIDEO_QUALITY_1080_60FPS" | "MAX_FILE_SIZE_100_MB" | "VANITY_URL" | "VIDEO_BITRATE_ENHANCED" | "STAGE_CHANNEL_VIEWERS_300" | "AUDIO_BITRATE_128_KBPS" | "ANIMATED_BANNER" | "TIERLESS_BOOSTING" | "ENHANCED_ROLE_COLORS" | "INVITE_SPLASH" | "AUDIO_BITRATE_256_KBPS" | "AUDIO_BITRATE_384_KBPS";
|
||||
|
||||
export class Guild extends DiscordRecord {
|
||||
constructor(guild: object);
|
||||
afkChannelId: string | undefined;
|
||||
afkTimeout: number;
|
||||
applicationCommandCounts: {
|
||||
0: number;
|
||||
1: number;
|
||||
2: number;
|
||||
};
|
||||
application_id: unknown;
|
||||
banner: string | undefined;
|
||||
defaultMessageNotifications: number;
|
||||
description: string | undefined;
|
||||
discoverySplash: string | undefined;
|
||||
explicitContentFilter: number;
|
||||
features: Set<GuildFeatures>;
|
||||
homeHeader: string | undefined;
|
||||
hubType: unknown;
|
||||
icon: string | undefined;
|
||||
id: string;
|
||||
joinedAt: Date;
|
||||
latestOnboardingQuestionId: string | undefined;
|
||||
maxMembers: number;
|
||||
maxStageVideoChannelUsers: number;
|
||||
maxVideoChannelUsers: number;
|
||||
mfaLevel: number;
|
||||
moderatorReporting: unknown;
|
||||
name: string;
|
||||
nsfwLevel: number;
|
||||
ownerConfiguredContentLevel: number;
|
||||
ownerId: string;
|
||||
preferredLocale: string;
|
||||
premiumFeatures: {
|
||||
additionalEmojiSlots: number;
|
||||
additionalSoundSlots: number;
|
||||
additionalStickerSlots: number;
|
||||
features: Array<GuildPremiumFeatures>;
|
||||
};
|
||||
premiumProgressBarEnabled: boolean;
|
||||
premiumSubscriberCount: number;
|
||||
premiumTier: number;
|
||||
profile: {
|
||||
badge: string | undefined;
|
||||
tag: string | undefined;
|
||||
} | undefined;
|
||||
publicUpdatesChannelId: string | undefined;
|
||||
roles: Record<string, Role>;
|
||||
rulesChannelId: string | undefined;
|
||||
safetyAlertsChannelId: string | undefined;
|
||||
splash: string | undefined;
|
||||
systemChannelFlags: number;
|
||||
systemChannelId: string | undefined;
|
||||
vanityURLCode: string | undefined;
|
||||
verificationLevel: number;
|
||||
}
|
||||
26
packages/discord-types/src/common/GuildMember.d.ts
vendored
Normal file
26
packages/discord-types/src/common/GuildMember.d.ts
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
export interface GuildMember {
|
||||
avatar: string | undefined;
|
||||
avatarDecoration: string | undefined;
|
||||
banner: string | undefined;
|
||||
bio: string;
|
||||
colorRoleId: string | undefined;
|
||||
colorString: string;
|
||||
colorStrings: {
|
||||
primaryColor: string | undefined;
|
||||
secondaryColor: string | undefined;
|
||||
tertiaryColor: string | undefined;
|
||||
};
|
||||
communicationDisabledUntil: string | undefined;
|
||||
flags: number;
|
||||
fullProfileLoadedTimestamp: number;
|
||||
guildId: string;
|
||||
highestRoleId: string;
|
||||
hoistRoleId: string;
|
||||
iconRoleId: string;
|
||||
isPending: boolean | undefined;
|
||||
joinedAt: string | undefined;
|
||||
nick: string | undefined;
|
||||
premiumSince: string | undefined;
|
||||
roles: string[];
|
||||
userId: string;
|
||||
}
|
||||
12
packages/discord-types/src/common/Record.d.ts
vendored
Normal file
12
packages/discord-types/src/common/Record.d.ts
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
type Updater = (value: any) => any;
|
||||
|
||||
/**
|
||||
* Common Record class extended by various Discord data structures, like User, Channel, Guild, etc.
|
||||
*/
|
||||
export class DiscordRecord {
|
||||
toJS(): Record<string, any>;
|
||||
|
||||
set(key: string, value: any): this;
|
||||
merge(data: Record<string, any>): this;
|
||||
update(key: string, defaultValueOrUpdater: Updater | any, updater?: Updater): this;
|
||||
}
|
||||
33
packages/discord-types/src/common/Role.d.ts
vendored
Normal file
33
packages/discord-types/src/common/Role.d.ts
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
export interface Role {
|
||||
color: number;
|
||||
colorString: string | undefined;
|
||||
colorStrings: {
|
||||
primaryColor: string | undefined;
|
||||
secondaryColor: string | undefined;
|
||||
tertiaryColor: string | undefined;
|
||||
};
|
||||
colors: {
|
||||
primary_color: number | undefined;
|
||||
secondary_color: number | undefined;
|
||||
tertiary_color: number | undefined;
|
||||
};
|
||||
flags: number;
|
||||
hoist: boolean;
|
||||
icon: string | undefined;
|
||||
id: string;
|
||||
managed: boolean;
|
||||
mentionable: boolean;
|
||||
name: string;
|
||||
originalPosition: number;
|
||||
permissions: bigint;
|
||||
position: number;
|
||||
/**
|
||||
* probably incomplete
|
||||
*/
|
||||
tags: {
|
||||
bot_id: string;
|
||||
integration_id: string;
|
||||
premium_subscriber: unknown;
|
||||
} | undefined;
|
||||
unicodeEmoji: string | undefined;
|
||||
}
|
||||
65
packages/discord-types/src/common/User.d.ts
vendored
Normal file
65
packages/discord-types/src/common/User.d.ts
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// TODO: a lot of optional params can also be null, not just undef
|
||||
|
||||
import { DiscordRecord } from "./Record";
|
||||
|
||||
export class User extends DiscordRecord {
|
||||
constructor(user: object);
|
||||
accentColor: number;
|
||||
avatar: string;
|
||||
banner: string | null | undefined;
|
||||
bio: string;
|
||||
bot: boolean;
|
||||
desktop: boolean;
|
||||
discriminator: string;
|
||||
email: string | undefined;
|
||||
flags: number;
|
||||
globalName: string | undefined;
|
||||
guildMemberAvatars: Record<string, string>;
|
||||
id: string;
|
||||
mfaEnabled: boolean;
|
||||
mobile: boolean;
|
||||
nsfwAllowed: boolean | undefined;
|
||||
phone: string | undefined;
|
||||
premiumType: number | undefined;
|
||||
premiumUsageFlags: number;
|
||||
publicFlags: number;
|
||||
purchasedFlags: number;
|
||||
system: boolean;
|
||||
username: string;
|
||||
verified: boolean;
|
||||
|
||||
get createdAt(): Date;
|
||||
get hasPremiumPerks(): boolean;
|
||||
get tag(): string;
|
||||
get usernameNormalized(): string;
|
||||
|
||||
addGuildAvatarHash(guildId: string, avatarHash: string): User;
|
||||
getAvatarSource(guildId: string, canAnimate?: boolean): { uri: string; };
|
||||
getAvatarURL(guildId?: string | null, t?: unknown, canAnimate?: boolean): string;
|
||||
hasAvatarForGuild(guildId: string): boolean;
|
||||
hasDisabledPremium(): boolean;
|
||||
hasFlag(flag: number): boolean;
|
||||
hasFreePremium(): boolean;
|
||||
hasHadSKU(e: unknown): boolean;
|
||||
hasPremiumUsageFlag(flag: number): boolean;
|
||||
hasPurchasedFlag(flag: number): boolean;
|
||||
hasUrgentMessages(): boolean;
|
||||
isClaimed(): boolean;
|
||||
isLocalBot(): boolean;
|
||||
isNonUserBot(): boolean;
|
||||
isPhoneVerified(): boolean;
|
||||
isStaff(): boolean;
|
||||
isSystemUser(): boolean;
|
||||
isVerifiedBot(): boolean;
|
||||
removeGuildAvatarHash(guildId: string): User;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export interface UserJSON {
|
||||
avatar: string;
|
||||
avatarDecoration: unknown | undefined;
|
||||
discriminator: string;
|
||||
id: string;
|
||||
publicFlags: number;
|
||||
username: string;
|
||||
}
|
||||
9
packages/discord-types/src/common/index.d.ts
vendored
Normal file
9
packages/discord-types/src/common/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export * from "./Activity";
|
||||
export * from "./Application";
|
||||
export * from "./Channel";
|
||||
export * from "./Guild";
|
||||
export * from "./GuildMember";
|
||||
export * from "./messages";
|
||||
export * from "./Role";
|
||||
export * from "./User";
|
||||
export * from "./Record";
|
||||
61
packages/discord-types/src/common/messages/Commands.d.ts
vendored
Normal file
61
packages/discord-types/src/common/messages/Commands.d.ts
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { Channel } from "../Channel";
|
||||
import { Guild } from "../Guild";
|
||||
import { Promisable } from "type-fest";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "../../../enums";
|
||||
|
||||
export interface CommandContext {
|
||||
channel: Channel;
|
||||
guild?: Guild;
|
||||
}
|
||||
|
||||
export interface CommandOption {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
type: ApplicationCommandOptionType;
|
||||
description: string;
|
||||
displayDescription?: string;
|
||||
required?: boolean;
|
||||
options?: CommandOption[];
|
||||
choices?: Array<ChoicesOption>;
|
||||
}
|
||||
|
||||
export interface ChoicesOption {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export interface CommandReturnValue {
|
||||
content: string;
|
||||
// TODO: implement
|
||||
// cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface CommandArgument {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
value: string;
|
||||
focused: undefined;
|
||||
options: CommandArgument[];
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
id?: string;
|
||||
applicationId?: string;
|
||||
type?: ApplicationCommandType;
|
||||
inputType?: ApplicationCommandInputType;
|
||||
plugin?: string;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: CommandOption[];
|
||||
predicate?(ctx: CommandContext): boolean;
|
||||
|
||||
execute(args: CommandArgument[], ctx: CommandContext): Promisable<void | CommandReturnValue>;
|
||||
}
|
||||
70
packages/discord-types/src/common/messages/Embed.d.ts
vendored
Normal file
70
packages/discord-types/src/common/messages/Embed.d.ts
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
export interface Embed {
|
||||
author?: {
|
||||
name: string;
|
||||
url: string;
|
||||
iconURL: string | undefined;
|
||||
iconProxyURL: string | undefined;
|
||||
};
|
||||
color: string;
|
||||
fields: [];
|
||||
id: string;
|
||||
image?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxyURL: string;
|
||||
};
|
||||
provider?: {
|
||||
name: string;
|
||||
url: string | undefined;
|
||||
};
|
||||
rawDescription: string;
|
||||
rawTitle: string;
|
||||
referenceId: unknown;
|
||||
timestamp: string;
|
||||
thumbnail?: {
|
||||
height: number;
|
||||
proxyURL: string | undefined;
|
||||
url: string;
|
||||
width: number;
|
||||
};
|
||||
type: string;
|
||||
url: string | undefined;
|
||||
video?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxyURL: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EmbedJSON {
|
||||
author?: {
|
||||
name: string;
|
||||
url: string;
|
||||
icon_url: string;
|
||||
proxy_icon_url: string;
|
||||
};
|
||||
title: string;
|
||||
color: string;
|
||||
description: string;
|
||||
type: string;
|
||||
url: string | undefined;
|
||||
provider?: {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
timestamp: string;
|
||||
thumbnail?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxy_url: string | undefined;
|
||||
};
|
||||
video?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxy_url: string | undefined;
|
||||
};
|
||||
}
|
||||
42
packages/discord-types/src/common/messages/Emoji.d.ts
vendored
Normal file
42
packages/discord-types/src/common/messages/Emoji.d.ts
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
export type Emoji = CustomEmoji | UnicodeEmoji;
|
||||
|
||||
export interface CustomEmoji {
|
||||
type: 1;
|
||||
allNamesString: string;
|
||||
animated: boolean;
|
||||
available: boolean;
|
||||
guildId: string;
|
||||
id: string;
|
||||
managed: boolean;
|
||||
name: string;
|
||||
originalName?: string;
|
||||
require_colons: boolean;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export interface UnicodeEmoji {
|
||||
type: 0;
|
||||
diversityChildren: Record<any, any>;
|
||||
emojiObject: {
|
||||
names: string[];
|
||||
surrogates: string;
|
||||
unicodeVersion: number;
|
||||
};
|
||||
index: number;
|
||||
surrogates: string;
|
||||
uniqueName: string;
|
||||
useSpriteSheet: boolean;
|
||||
get allNamesString(): string;
|
||||
get animated(): boolean;
|
||||
get defaultDiversityChild(): any;
|
||||
get hasDiversity(): boolean | undefined;
|
||||
get hasDiversityParent(): boolean | undefined;
|
||||
get hasMultiDiversity(): boolean | undefined;
|
||||
get hasMultiDiversityParent(): boolean | undefined;
|
||||
get managed(): boolean;
|
||||
get name(): string;
|
||||
get names(): string[];
|
||||
get optionallyDiverseSequence(): string | undefined;
|
||||
get unicodeVersion(): number;
|
||||
get url(): string;
|
||||
}
|
||||
204
packages/discord-types/src/common/messages/Message.d.ts
vendored
Normal file
204
packages/discord-types/src/common/messages/Message.d.ts
vendored
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
import { CommandOption } from './Commands';
|
||||
import { User, UserJSON } from '../User';
|
||||
import { Embed, EmbedJSON } from './Embed';
|
||||
import { DiscordRecord } from "../Record";
|
||||
import { MessageFlags, MessageType, StickerFormatType } from "../../../enums";
|
||||
|
||||
/*
|
||||
* TODO: looks like discord has moved over to Date instead of Moment;
|
||||
*/
|
||||
export class Message extends DiscordRecord {
|
||||
constructor(message: object);
|
||||
activity: unknown;
|
||||
application: unknown;
|
||||
applicationId: string | unknown;
|
||||
attachments: MessageAttachment[];
|
||||
author: User;
|
||||
blocked: boolean;
|
||||
bot: boolean;
|
||||
call: {
|
||||
duration: moment.Duration;
|
||||
endedTimestamp: moment.Moment;
|
||||
participants: string[];
|
||||
};
|
||||
channel_id: string;
|
||||
/**
|
||||
* NOTE: not fully typed
|
||||
*/
|
||||
codedLinks: {
|
||||
code?: string;
|
||||
type: string;
|
||||
}[];
|
||||
colorString: unknown;
|
||||
components: unknown[];
|
||||
content: string;
|
||||
customRenderedContent: unknown;
|
||||
editedTimestamp: Date;
|
||||
embeds: Embed[];
|
||||
flags: MessageFlags;
|
||||
giftCodes: string[];
|
||||
id: string;
|
||||
interaction: {
|
||||
id: string;
|
||||
name: string;
|
||||
type: number;
|
||||
user: User;
|
||||
}[] | undefined;
|
||||
interactionData: {
|
||||
application_command: {
|
||||
application_id: string;
|
||||
default_member_permissions: unknown;
|
||||
default_permission: boolean;
|
||||
description: string;
|
||||
dm_permission: unknown;
|
||||
id: string;
|
||||
name: string;
|
||||
options: CommandOption[];
|
||||
permissions: unknown[];
|
||||
type: number;
|
||||
version: string;
|
||||
};
|
||||
attachments: MessageAttachment[];
|
||||
guild_id: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
options: {
|
||||
focused: unknown;
|
||||
name: string;
|
||||
type: number;
|
||||
value: string;
|
||||
}[];
|
||||
type: number;
|
||||
version: string;
|
||||
}[];
|
||||
interactionError: unknown[];
|
||||
isSearchHit: boolean;
|
||||
loggingName: unknown;
|
||||
mentionChannels: string[];
|
||||
mentionEveryone: boolean;
|
||||
mentionRoles: string[];
|
||||
mentioned: boolean;
|
||||
mentions: string[];
|
||||
messageReference: {
|
||||
guild_id?: string;
|
||||
channel_id: string;
|
||||
message_id: string;
|
||||
} | undefined;
|
||||
messageSnapshots: {
|
||||
message: Message;
|
||||
}[];
|
||||
nick: unknown; // probably a string
|
||||
nonce: string | undefined;
|
||||
pinned: boolean;
|
||||
reactions: MessageReaction[];
|
||||
state: string;
|
||||
stickerItems: {
|
||||
format_type: StickerFormatType;
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
stickers: unknown[];
|
||||
timestamp: moment.Moment;
|
||||
tts: boolean;
|
||||
type: MessageType;
|
||||
webhookId: string | undefined;
|
||||
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns a new message instance with the added reaction.
|
||||
*/
|
||||
addReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||
/**
|
||||
* Searches each reaction and if the provided string has an index above -1 it'll return the reaction object.
|
||||
*/
|
||||
getReaction(name: string): MessageReaction;
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns the message instance without the reaction searched with the provided emoji object.
|
||||
*/
|
||||
removeReactionsForEmoji(emoji: ReactionEmoji): Message;
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns the message instance without the reaction.
|
||||
*/
|
||||
removeReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||
|
||||
getChannelId(): string;
|
||||
hasFlag(flag: MessageFlags): boolean;
|
||||
isCommandType(): boolean;
|
||||
isEdited(): boolean;
|
||||
isSystemDM(): boolean;
|
||||
|
||||
/** Vencord added */
|
||||
deleted?: boolean;
|
||||
}
|
||||
|
||||
/** A smaller Message object found in FluxDispatcher and elsewhere. */
|
||||
export interface MessageJSON {
|
||||
attachments: MessageAttachment[];
|
||||
author: UserJSON;
|
||||
channel_id: string;
|
||||
components: unknown[];
|
||||
content: string;
|
||||
edited_timestamp: string;
|
||||
embeds: EmbedJSON[];
|
||||
flags: number;
|
||||
guild_id: string | undefined;
|
||||
id: string;
|
||||
loggingName: unknown;
|
||||
member: {
|
||||
avatar: string | undefined;
|
||||
communication_disabled_until: string | undefined;
|
||||
deaf: boolean;
|
||||
hoisted_role: string | undefined;
|
||||
is_pending: boolean;
|
||||
joined_at: string;
|
||||
mute: boolean;
|
||||
nick: string | boolean;
|
||||
pending: boolean;
|
||||
premium_since: string | undefined;
|
||||
roles: string[];
|
||||
} | undefined;
|
||||
mention_everyone: boolean;
|
||||
mention_roles: string[];
|
||||
mentions: UserJSON[];
|
||||
message_reference: {
|
||||
guild_id?: string;
|
||||
channel_id: string;
|
||||
message_id: string;
|
||||
} | undefined;
|
||||
nonce: string | undefined;
|
||||
pinned: boolean;
|
||||
referenced_message: MessageJSON | undefined;
|
||||
state: string;
|
||||
timestamp: string;
|
||||
tts: boolean;
|
||||
type: number;
|
||||
}
|
||||
|
||||
export interface MessageAttachment {
|
||||
filename: string;
|
||||
id: string;
|
||||
proxy_url: string;
|
||||
size: number;
|
||||
spoiler: boolean;
|
||||
url: string;
|
||||
content_type?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface ReactionEmoji {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
animated: boolean;
|
||||
}
|
||||
|
||||
export interface MessageReaction {
|
||||
count: number;
|
||||
emoji: ReactionEmoji;
|
||||
me: boolean;
|
||||
}
|
||||
|
||||
// Object.keys(findByProps("REPLYABLE")).map(JSON.stringify).join("|")
|
||||
export type MessageTypeSets = Record<
|
||||
"UNDELETABLE" | "GUILD_DISCOVERY_STATUS" | "USER_MESSAGE" | "NOTIFIABLE_SYSTEM_MESSAGE" | "REPLYABLE" | "FORWARDABLE" | "REFERENCED_MESSAGE_AVAILABLE" | "AVAILABLE_IN_GUILD_FEED" | "DEADCHAT_PROMPTS" | "NON_COLLAPSIBLE" | "NON_PARSED" | "AUTOMOD_INCIDENT_ACTIONS" | "SELF_MENTIONABLE_SYSTEM" | "SCHEDULABLE",
|
||||
Set<MessageType>
|
||||
>;
|
||||
35
packages/discord-types/src/common/messages/Sticker.d.ts
vendored
Normal file
35
packages/discord-types/src/common/messages/Sticker.d.ts
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { StickerFormatType, StickerType } from "../../../enums";
|
||||
|
||||
interface BaseSticker {
|
||||
asset: string;
|
||||
available: boolean;
|
||||
description: string;
|
||||
format_type: StickerFormatType;
|
||||
id: string;
|
||||
name: string;
|
||||
sort_value?: number;
|
||||
/** a comma separated string */
|
||||
tags: string;
|
||||
}
|
||||
|
||||
export interface PackSticker extends BaseSticker {
|
||||
pack_id: string;
|
||||
type: StickerType.STANDARD;
|
||||
}
|
||||
|
||||
export interface GuildSticker extends BaseSticker {
|
||||
guild_id: string;
|
||||
type: StickerType.GUILD;
|
||||
}
|
||||
|
||||
export type Sticker = PackSticker | GuildSticker;
|
||||
|
||||
export interface PremiumStickerPack {
|
||||
banner_asset_id?: string;
|
||||
cover_sticker_id?: string;
|
||||
description: string;
|
||||
id: string;
|
||||
name: string;
|
||||
sku_id: string;
|
||||
stickers: PackSticker[];
|
||||
}
|
||||
5
packages/discord-types/src/common/messages/index.d.ts
vendored
Normal file
5
packages/discord-types/src/common/messages/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./Commands";
|
||||
export * from "./Message";
|
||||
export * from "./Embed";
|
||||
export * from "./Emoji";
|
||||
export * from "./Sticker";
|
||||
|
|
@ -1,72 +1,40 @@
|
|||
/*
|
||||
* 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 { Moment } from "moment";
|
||||
import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref, RefObject } from "react";
|
||||
|
||||
|
||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
|
||||
export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`;
|
||||
// #region Old compability
|
||||
|
||||
export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`;
|
||||
export type Margins = Record<"marginTop16" | "marginTop8" | "marginBottom8" | "marginTop20" | "marginBottom20", string>;
|
||||
|
||||
// copy(find(m => Array.isArray(m) && m.includes("heading-sm/normal")).map(JSON.stringify).join("|"))
|
||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-sm/extrabold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-md/extrabold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-lg/extrabold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/semibold" | "heading-xl/bold" | "heading-xl/extrabold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/semibold" | "heading-xxl/bold" | "heading-xxl/extrabold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold";
|
||||
|
||||
export type TextProps = PropsWithChildren<HtmlHTMLAttributes<HTMLDivElement> & {
|
||||
variant?: TextVariant;
|
||||
tag?: "div" | "span" | "p" | "strong" | HeadingTag;
|
||||
selectable?: boolean;
|
||||
lineClamp?: number;
|
||||
}>;
|
||||
|
||||
export type Text = ComponentType<TextProps>;
|
||||
export type Heading = ComponentType<TextProps>;
|
||||
|
||||
export type FormTitle = ComponentType<HTMLProps<HTMLTitleElement> & PropsWithChildren<{
|
||||
/** default is h5 */
|
||||
tag?: HeadingTag;
|
||||
faded?: boolean;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
error?: ReactNode;
|
||||
}>>;
|
||||
export interface ButtonProps extends PropsWithChildren<Omit<HTMLProps<HTMLButtonElement>, "size">> {
|
||||
/** Button.Looks.FILLED */
|
||||
look?: string;
|
||||
/** Button.Colors.BRAND */
|
||||
color?: string;
|
||||
/** Button.Sizes.MEDIUM */
|
||||
size?: string;
|
||||
|
||||
export type FormSection = ComponentType<PropsWithChildren<{
|
||||
/** default is h5 */
|
||||
tag?: HeadingTag;
|
||||
className?: string;
|
||||
titleClassName?: string;
|
||||
titleId?: string;
|
||||
title?: ReactNode;
|
||||
disabled?: boolean;
|
||||
htmlFor?: unknown;
|
||||
}>>;
|
||||
}
|
||||
|
||||
export type FormDivider = ComponentType<{
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}>;
|
||||
export type Button = ComponentType<ButtonProps> & {
|
||||
Colors: Record<"BRAND" | "RED" | "GREEN" | "PRIMARY" | "LINK" | "WHITE" | "TRANSPARENT" | "CUSTOM", string>;
|
||||
Looks: Record<"FILLED" | "LINK", string>;
|
||||
Sizes: Record<"NONE" | "SMALL" | "MEDIUM" | "LARGE" | "XLARGE" | "MIN", string>;
|
||||
};
|
||||
|
||||
|
||||
export type FormText = ComponentType<PropsWithChildren<{
|
||||
disabled?: boolean;
|
||||
selectable?: boolean;
|
||||
/** defaults to FormText.Types.DEFAULT */
|
||||
type?: string;
|
||||
}> & TextProps> & { Types: FormTextTypes; };
|
||||
// #endregion
|
||||
|
||||
export type Tooltip = ComponentType<{
|
||||
text: ReactNode | ComponentType;
|
||||
|
|
@ -144,7 +112,6 @@ export type ComboboxPopout = ComponentType<PropsWithChildren<{
|
|||
className?: string;
|
||||
listClassName?: string;
|
||||
|
||||
|
||||
autoFocus?: boolean;
|
||||
multiSelect?: boolean;
|
||||
maxVisibleItems?: number;
|
||||
|
|
@ -152,51 +119,6 @@ export type ComboboxPopout = ComponentType<PropsWithChildren<{
|
|||
|
||||
}>>;
|
||||
|
||||
export interface ButtonProps extends PropsWithChildren<Omit<HTMLProps<HTMLButtonElement>, "size">> {
|
||||
/** Button.Looks.FILLED */
|
||||
look?: string;
|
||||
/** Button.Colors.BRAND */
|
||||
color?: string;
|
||||
/** Button.Sizes.MEDIUM */
|
||||
size?: string;
|
||||
/** Button.BorderColors.BLACK */
|
||||
borderColor?: string;
|
||||
|
||||
wrapperClassName?: string;
|
||||
className?: string;
|
||||
innerClassName?: string;
|
||||
|
||||
buttonRef?: Ref<HTMLButtonElement>;
|
||||
focusProps?: any;
|
||||
submitting?: boolean;
|
||||
|
||||
submittingStartedLabel?: string;
|
||||
submittingFinishedLabel?: string;
|
||||
}
|
||||
|
||||
export type Button = ComponentType<ButtonProps> & {
|
||||
BorderColors: Record<"BLACK" | "BRAND" | "BRAND_NEW" | "GREEN" | "LINK" | "PRIMARY" | "RED" | "TRANSPARENT" | "WHITE" | "YELLOW", string>;
|
||||
Colors: Record<"BRAND" | "RED" | "GREEN" | "YELLOW" | "PRIMARY" | "LINK" | "WHITE" | "BLACK" | "TRANSPARENT" | "BRAND_NEW" | "CUSTOM", string>;
|
||||
Hovers: Record<"DEFAULT" | "BRAND" | "RED" | "GREEN" | "YELLOW" | "PRIMARY" | "LINK" | "WHITE" | "BLACK" | "TRANSPARENT", string>;
|
||||
Looks: Record<"FILLED" | "INVERTED" | "OUTLINED" | "LINK" | "BLANK", string>;
|
||||
Sizes: Record<"NONE" | "TINY" | "SMALL" | "MEDIUM" | "LARGE" | "XLARGE" | "MIN" | "MAX" | "ICON", string>;
|
||||
|
||||
Link: any;
|
||||
};
|
||||
|
||||
export type Switch = ComponentType<PropsWithChildren<{
|
||||
value: boolean;
|
||||
onChange(value: boolean): void;
|
||||
|
||||
disabled?: boolean;
|
||||
hideBorder?: boolean;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
|
||||
note?: ReactNode;
|
||||
tooltipNote?: ReactNode;
|
||||
}>>;
|
||||
|
||||
export type CheckboxAligns = {
|
||||
CENTER: "center";
|
||||
TOP: "top";
|
||||
|
|
@ -262,8 +184,10 @@ export type TextInput = ComponentType<PropsWithChildren<{
|
|||
Sizes: Record<"DEFAULT" | "MINI", string>;
|
||||
};
|
||||
|
||||
// FIXME: this is wrong, it's not actually just HTMLTextAreaElement
|
||||
export type TextArea = ComponentType<Omit<HTMLProps<HTMLTextAreaElement>, "onChange"> & {
|
||||
onChange(v: string): void;
|
||||
inputRef?: Ref<HTMLTextAreaElement>;
|
||||
}>;
|
||||
|
||||
export interface SelectOption {
|
||||
|
|
@ -491,19 +415,66 @@ export type MaskedLink = ComponentType<PropsWithChildren<{
|
|||
channelId?: string;
|
||||
}>>;
|
||||
|
||||
export type ScrollerThin = ComponentType<PropsWithChildren<{
|
||||
export interface ScrollerBaseProps {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
|
||||
dir?: "ltr";
|
||||
orientation?: "horizontal" | "vertical" | "auto";
|
||||
paddingFix?: boolean;
|
||||
fade?: boolean;
|
||||
|
||||
onClose?(): void;
|
||||
onScroll?(): void;
|
||||
}
|
||||
|
||||
export type ScrollerThin = ComponentType<PropsWithChildren<ScrollerBaseProps & {
|
||||
orientation?: "horizontal" | "vertical" | "auto";
|
||||
fade?: boolean;
|
||||
}>>;
|
||||
|
||||
interface BaseListItem {
|
||||
anchorId: any;
|
||||
listIndex: number;
|
||||
offsetTop: number;
|
||||
section: number;
|
||||
}
|
||||
interface ListSection extends BaseListItem {
|
||||
type: "section";
|
||||
}
|
||||
interface ListRow extends BaseListItem {
|
||||
type: "row";
|
||||
row: number;
|
||||
rowIndex: number;
|
||||
}
|
||||
|
||||
export type ListScrollerThin = ComponentType<ScrollerBaseProps & {
|
||||
sections: number[];
|
||||
renderSection?: (item: ListSection) => React.ReactNode;
|
||||
renderRow: (item: ListRow) => React.ReactNode;
|
||||
renderFooter?: (item: any) => React.ReactNode;
|
||||
renderSidebar?: (listVisible: boolean, sidebarVisible: boolean) => React.ReactNode;
|
||||
wrapSection?: (section: number, children: React.ReactNode) => React.ReactNode;
|
||||
|
||||
sectionHeight: number;
|
||||
rowHeight: number;
|
||||
footerHeight?: number;
|
||||
sidebarHeight?: number;
|
||||
|
||||
chunkSize?: number;
|
||||
|
||||
paddingTop?: number;
|
||||
paddingBottom?: number;
|
||||
fade?: boolean;
|
||||
onResize?: Function;
|
||||
getAnchorId?: any;
|
||||
|
||||
innerTag?: string;
|
||||
innerId?: string;
|
||||
innerClassName?: string;
|
||||
innerRole?: string;
|
||||
innerAriaLabel?: string;
|
||||
// Yes, Discord uses this casing
|
||||
innerAriaMultiselectable?: boolean;
|
||||
innerAriaOrientation?: "vertical" | "horizontal";
|
||||
}>;
|
||||
|
||||
export type Clickable = <T extends "a" | "div" | "span" | "li" = "div">(props: PropsWithChildren<ComponentPropsWithRef<T>> & {
|
||||
tag?: T;
|
||||
}) => ReactNode;
|
||||
|
|
@ -544,3 +515,10 @@ export type Icon = ComponentType<JSX.IntrinsicElements["svg"] & {
|
|||
colorClass?: string;
|
||||
} & Record<string, any>>;
|
||||
|
||||
export type ColorPicker = ComponentType<{
|
||||
color: number | null;
|
||||
showEyeDropper?: boolean;
|
||||
suggestedColors?: string[];
|
||||
label?: ReactNode;
|
||||
onChange(value: number | null): void;
|
||||
}>;
|
||||
30
packages/discord-types/src/flux.d.ts
vendored
Normal file
30
packages/discord-types/src/flux.d.ts
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { FluxStore } from "./stores/FluxStore";
|
||||
|
||||
export class FluxEmitter {
|
||||
constructor();
|
||||
|
||||
changeSentinel: number;
|
||||
changedStores: Set<FluxStore>;
|
||||
isBatchEmitting: boolean;
|
||||
isDispatching: boolean;
|
||||
isPaused: boolean;
|
||||
pauseTimer: NodeJS.Timeout | null;
|
||||
reactChangedStores: Set<FluxStore>;
|
||||
|
||||
batched(batch: (...args: any[]) => void): void;
|
||||
destroy(): void;
|
||||
emit(): void;
|
||||
emitNonReactOnce(): void;
|
||||
emitReactOnce(): void;
|
||||
getChangeSentinel(): number;
|
||||
getIsPaused(): boolean;
|
||||
injectBatchEmitChanges(batch: (...args: any[]) => void): void;
|
||||
markChanged(store: FluxStore): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
}
|
||||
|
||||
export interface Flux {
|
||||
Store: typeof FluxStore;
|
||||
Emitter: FluxEmitter;
|
||||
}
|
||||
22
packages/discord-types/src/fluxEvents.d.ts
vendored
Normal file
22
packages/discord-types/src/fluxEvents.d.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
11
packages/discord-types/src/index.d.ts
vendored
Normal file
11
packages/discord-types/src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export * from "./common";
|
||||
export * from "./classes";
|
||||
export * from "./components";
|
||||
export * from "./flux";
|
||||
export * from "./fluxEvents";
|
||||
export * from "./menu";
|
||||
export * from "./modules";
|
||||
export * from "./stores";
|
||||
export * from "./passiveupdatestate";
|
||||
export * from "./utils";
|
||||
export * as Webpack from "../webpack";
|
||||
|
|
@ -1,21 +1,3 @@
|
|||
/*
|
||||
* 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 type { ComponentType, CSSProperties, MouseEvent, PropsWithChildren, ReactNode, UIEvent } from "react";
|
||||
|
||||
type RC<C> = ComponentType<PropsWithChildren<C & Record<string, any>>>;
|
||||
|
|
@ -73,7 +55,7 @@ export interface Menu {
|
|||
renderValue?(value: number): string,
|
||||
}>;
|
||||
MenuSearchControl: RC<{
|
||||
query: string
|
||||
query: string;
|
||||
onChange(query: string): void;
|
||||
placeholder?: string;
|
||||
}>;
|
||||
72
packages/discord-types/src/modules/CloudUpload.d.ts
vendored
Normal file
72
packages/discord-types/src/modules/CloudUpload.d.ts
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import EventEmitter from "events";
|
||||
import { CloudUploadPlatform } from "../../enums";
|
||||
|
||||
interface BaseUploadItem {
|
||||
platform: CloudUploadPlatform;
|
||||
id?: string;
|
||||
origin?: string;
|
||||
isThumbnail?: boolean;
|
||||
clip?: unknown;
|
||||
}
|
||||
|
||||
export interface ReactNativeUploadItem extends BaseUploadItem {
|
||||
platform: CloudUploadPlatform.REACT_NATIVE;
|
||||
uri: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
durationSecs?: number;
|
||||
waveform?: string;
|
||||
}
|
||||
|
||||
export interface WebUploadItem extends BaseUploadItem {
|
||||
platform: CloudUploadPlatform.WEB;
|
||||
file: File;
|
||||
}
|
||||
|
||||
export type CloudUploadItem = ReactNativeUploadItem | WebUploadItem;
|
||||
|
||||
export class CloudUpload extends EventEmitter {
|
||||
constructor(item: CloudUploadItem, channelId: string, showLargeMessageDialog?: boolean, reactNativeFileIndex?: number);
|
||||
|
||||
channelId: string;
|
||||
classification: string;
|
||||
clip: unknown;
|
||||
contentHash: unknown;
|
||||
currentSize: number;
|
||||
description: string | null;
|
||||
durationSecs: number | undefined;
|
||||
etag: string | undefined;
|
||||
error: unknown;
|
||||
filename: string;
|
||||
id: string;
|
||||
isImage: boolean;
|
||||
isThumbnail: boolean;
|
||||
isVideo: boolean;
|
||||
item: {
|
||||
file: File;
|
||||
platform: CloudUploadPlatform;
|
||||
origin: string;
|
||||
};
|
||||
loaded: number;
|
||||
mimeType: string;
|
||||
origin: string;
|
||||
postCompressionSize: number | undefined;
|
||||
preCompressionSize: number;
|
||||
responseUrl: string;
|
||||
sensitive: boolean;
|
||||
showLargeMessageDialog: boolean;
|
||||
spoiler: boolean;
|
||||
startTime: number;
|
||||
status: "NOT_STARTED" | "STARTED" | "UPLOADING" | "ERROR" | "COMPLETED" | "CANCELLED" | "REMOVED_FROM_MSG_DRAFT";
|
||||
uniqueId: string;
|
||||
uploadedFilename: string;
|
||||
waveform: string | undefined;
|
||||
|
||||
// there are many more methods than just these but I didn't find them particularly useful
|
||||
upload(): Promise<void>;
|
||||
cancel(): void;
|
||||
delete(): Promise<void>;
|
||||
getSize(): number;
|
||||
maybeConvertToWebP(): Promise<void>;
|
||||
removeFromMsgDraft(): void;
|
||||
}
|
||||
1
packages/discord-types/src/modules/index.d.ts
vendored
Normal file
1
packages/discord-types/src/modules/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./CloudUpload";
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { VoiceState } from "./voicestate";
|
||||
import { VoiceState } from "./stores";
|
||||
|
||||
export interface PassiveUpdateState {
|
||||
type: string;
|
||||
11
packages/discord-types/src/stores/AuthenticationStore.d.ts
vendored
Normal file
11
packages/discord-types/src/stores/AuthenticationStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class AuthenticationStore extends FluxStore {
|
||||
/**
|
||||
* Gets the id of the current user
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
// This Store has a lot more methods related to everything Auth, but they really should
|
||||
// not be needed, so they are not typed
|
||||
}
|
||||
24
packages/discord-types/src/stores/ChannelStore.d.ts
vendored
Normal file
24
packages/discord-types/src/stores/ChannelStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { Channel, FluxStore } from "..";
|
||||
|
||||
export class ChannelStore extends FluxStore {
|
||||
getChannel(channelId: string): Channel;
|
||||
getBasicChannel(channelId: string): Channel | undefined;
|
||||
hasChannel(channelId: string): boolean;
|
||||
|
||||
getChannelIds(guildId?: string | null): string[];
|
||||
getMutableBasicGuildChannelsForGuild(guildId: string): Record<string, Channel>;
|
||||
getMutableGuildChannelsForGuild(guildId: string): Record<string, Channel>;
|
||||
getAllThreadsForGuild(guildId: string): Channel[];
|
||||
getAllThreadsForParent(channelId: string): Channel[];
|
||||
|
||||
getDMFromUserId(userId: string): string;
|
||||
getDMChannelFromUserId(userId: string): Channel | undefined;
|
||||
getDMUserIds(): string[];
|
||||
getMutableDMsByUserIds(): Record<string, string>;
|
||||
getMutablePrivateChannels(): Record<string, Channel>;
|
||||
getSortedPrivateChannels(): Channel[];
|
||||
|
||||
getGuildChannelsVersion(guildId: string): number;
|
||||
getPrivateChannelsVersion(): number;
|
||||
getInitialOverlayState(): Record<string, Channel>;
|
||||
}
|
||||
43
packages/discord-types/src/stores/DraftStore.d.ts
vendored
Normal file
43
packages/discord-types/src/stores/DraftStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export enum DraftType {
|
||||
ChannelMessage = 0,
|
||||
ThreadSettings = 1,
|
||||
FirstThreadMessage = 2,
|
||||
ApplicationLauncherCommand = 3,
|
||||
Poll = 4,
|
||||
SlashCommand = 5,
|
||||
ForwardContextMessage = 6
|
||||
}
|
||||
|
||||
export interface Draft {
|
||||
timestamp: number;
|
||||
draft: string;
|
||||
}
|
||||
|
||||
export interface ThreadSettingsDraft {
|
||||
timestamp: number;
|
||||
parentMessageId?: string;
|
||||
name?: string;
|
||||
isPrivate?: boolean;
|
||||
parentChannelId?: string;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
export type ChannelDrafts = {
|
||||
[DraftType.ThreadSettings]: ThreadSettingsDraft;
|
||||
} & {
|
||||
[key in Exclude<DraftType, DraftType.ThreadSettings>]: Draft;
|
||||
};
|
||||
|
||||
export type UserDrafts = Partial<Record<string, ChannelDrafts>>;
|
||||
export type DraftState = Partial<Record<string, UserDrafts>>;
|
||||
|
||||
export class DraftStore extends FluxStore {
|
||||
getState(): DraftState;
|
||||
getRecentlyEditedDrafts(type: DraftType): Array<Draft & { channelId: string; }>;
|
||||
getDraft(channelId: string, type: DraftType): string;
|
||||
|
||||
getThreadSettings(channelId: string): ThreadSettingsDraft | null | undefined;
|
||||
getThreadDraftWithParentMessageId(parentMessageId: string): ThreadSettingsDraft | null | undefined;
|
||||
}
|
||||
57
packages/discord-types/src/stores/EmojiStore.d.ts
vendored
Normal file
57
packages/discord-types/src/stores/EmojiStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { Channel, CustomEmoji, Emoji, FluxStore } from "..";
|
||||
|
||||
export class EmojiStore extends FluxStore {
|
||||
getCustomEmojiById(id?: string | null): CustomEmoji | undefined;
|
||||
getUsableCustomEmojiById(id?: string | null): CustomEmoji | undefined;
|
||||
getGuilds(): Record<string, {
|
||||
id: string;
|
||||
get emojis(): CustomEmoji[];
|
||||
get rawEmojis(): CustomEmoji[];
|
||||
get usableEmojis(): CustomEmoji[];
|
||||
get emoticons(): any[];
|
||||
getEmoji(id: string): CustomEmoji | undefined;
|
||||
isUsable(emoji: CustomEmoji): boolean;
|
||||
}>;
|
||||
getGuildEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getNewlyAddedEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getTopEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getTopEmojisMetadata(guildId?: string | null): {
|
||||
emojiIds: string[];
|
||||
topEmojisTTL: number;
|
||||
};
|
||||
hasPendingUsage(): boolean;
|
||||
hasUsableEmojiInAnyGuild(): boolean;
|
||||
searchWithoutFetchingLatest(data: any): any;
|
||||
getSearchResultsOrder(...args: any[]): any;
|
||||
getState(): {
|
||||
pendingUsages: { key: string, timestamp: number; }[];
|
||||
};
|
||||
searchWithoutFetchingLatest(data: {
|
||||
channel: Channel;
|
||||
query: string;
|
||||
count?: number;
|
||||
intention: number;
|
||||
includeExternalGuilds?: boolean;
|
||||
matchComparator?(name: string): boolean;
|
||||
}): Record<"locked" | "unlocked", Emoji[]>;
|
||||
|
||||
getDisambiguatedEmojiContext(): {
|
||||
backfillTopEmojis: Record<any, any>;
|
||||
customEmojis: Record<string, CustomEmoji>;
|
||||
emojisById: Record<string, CustomEmoji>;
|
||||
emojisByName: Record<string, CustomEmoji>;
|
||||
emoticonRegex: RegExp | null;
|
||||
emoticonsByName: Record<string, any>;
|
||||
escapedEmoticonNames: string;
|
||||
favoriteNamesAndIds?: any;
|
||||
favorites?: any;
|
||||
frequentlyUsed?: any;
|
||||
groupedCustomEmojis: Record<string, CustomEmoji[]>;
|
||||
guildId?: string;
|
||||
isFavoriteEmojiWithoutFetchingLatest(e: Emoji): boolean;
|
||||
newlyAddedEmoji: Record<string, CustomEmoji[]>;
|
||||
topEmojis?: any;
|
||||
unicodeAliases: Record<string, string>;
|
||||
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
|
||||
};
|
||||
}
|
||||
44
packages/discord-types/src/stores/FluxStore.d.ts
vendored
Normal file
44
packages/discord-types/src/stores/FluxStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { FluxDispatcher, FluxEvents } from "..";
|
||||
|
||||
type Callback = () => void;
|
||||
|
||||
/*
|
||||
For some reason, this causes type errors when you try to destructure it:
|
||||
```ts
|
||||
interface FluxEvent {
|
||||
type: FluxEvents;
|
||||
[key: string]: any;
|
||||
}
|
||||
```
|
||||
*/
|
||||
export type FluxEvent = any;
|
||||
|
||||
export type ActionHandler = (event: FluxEvent) => void;
|
||||
export type ActionHandlers = Partial<Record<FluxEvents, ActionHandler>>;
|
||||
|
||||
export class FluxStore {
|
||||
constructor(dispatcher: FluxDispatcher, actionHandlers?: ActionHandlers);
|
||||
|
||||
getName(): string;
|
||||
|
||||
addChangeListener(callback: Callback): void;
|
||||
/** Listener will be removed once the callback returns false. */
|
||||
addConditionalChangeListener(callback: () => boolean, preemptive?: boolean): void;
|
||||
addReactChangeListener(callback: Callback): void;
|
||||
removeChangeListener(callback: Callback): void;
|
||||
removeReactChangeListener(callback: Callback): void;
|
||||
|
||||
doEmitChanges(event: FluxEvent): void;
|
||||
emitChange(): void;
|
||||
|
||||
getDispatchToken(): string;
|
||||
initialize(): void;
|
||||
initializeIfNeeded(): void;
|
||||
/** this is a setter */
|
||||
mustEmitChanges(actionHandler: ActionHandler | undefined): void;
|
||||
registerActionHandlers(actionHandlers: ActionHandlers): void;
|
||||
syncWith(stores: FluxStore[], callback: Callback, timeout?: number): void;
|
||||
waitFor(...stores: FluxStore[]): void;
|
||||
|
||||
static getAll(): FluxStore[];
|
||||
}
|
||||
27
packages/discord-types/src/stores/GuildMemberStore.d.ts
vendored
Normal file
27
packages/discord-types/src/stores/GuildMemberStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { FluxStore, GuildMember } from "..";
|
||||
|
||||
export class GuildMemberStore extends FluxStore {
|
||||
/** @returns Format: [guildId-userId: Timestamp (string)] */
|
||||
getCommunicationDisabledUserMap(): Record<string, string>;
|
||||
getCommunicationDisabledVersion(): number;
|
||||
|
||||
getMutableAllGuildsAndMembers(): Record<string, Record<string, GuildMember>>;
|
||||
|
||||
getMember(guildId: string, userId: string): GuildMember | null;
|
||||
getTrueMember(guildId: string, userId: string): GuildMember | null;
|
||||
getMemberIds(guildId: string): string[];
|
||||
getMembers(guildId: string): GuildMember[];
|
||||
|
||||
getCachedSelfMember(guildId: string): GuildMember | null;
|
||||
getSelfMember(guildId: string): GuildMember | null;
|
||||
getSelfMemberJoinedAt(guildId: string): Date | null;
|
||||
|
||||
getNick(guildId: string, userId: string): string | null;
|
||||
getNicknameGuildsMapping(userId: string): Record<string, string[]>;
|
||||
getNicknames(userId: string): string[];
|
||||
|
||||
isMember(guildId: string, userId: string): boolean;
|
||||
isMember(guildId: string, userId: string): boolean;
|
||||
isGuestOrLurker(guildId: string, userId: string): boolean;
|
||||
isCurrentUserGuest(guildId: string): boolean;
|
||||
}
|
||||
13
packages/discord-types/src/stores/GuildRoleStore.d.ts
vendored
Normal file
13
packages/discord-types/src/stores/GuildRoleStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { FluxStore, Guild, Role } from "..";
|
||||
|
||||
// TODO: add the rest of the methods for GuildRoleStore
|
||||
export class GuildRoleStore extends FluxStore {
|
||||
serializeAllGuildRoles(): Array<{ partitionKey: string; values: Record<string, Role>; }>;
|
||||
getUnsafeMutableRoles(guildId: string): Record<string, Role>;
|
||||
getManyRoles(guildId: string, roleIds: string[]): Role[];
|
||||
getRole(guildId: string, roleId: string): Role;
|
||||
getNumRoles(guildId: string): number;
|
||||
getEveryoneRole(guild: Guild): Role;
|
||||
getRolesSnapshot(guildId: string): Record<string, Role>;
|
||||
getSortedRoles(guildId: string): Role[];
|
||||
}
|
||||
8
packages/discord-types/src/stores/GuildStore.d.ts
vendored
Normal file
8
packages/discord-types/src/stores/GuildStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { Guild, FluxStore } from "..";
|
||||
|
||||
export class GuildStore extends FluxStore {
|
||||
getGuild(guildId: string): Guild;
|
||||
getGuildCount(): number;
|
||||
getGuilds(): Record<string, Guild>;
|
||||
getGuildIds(): string[];
|
||||
}
|
||||
17
packages/discord-types/src/stores/MediaEngineStore.d.ts
vendored
Normal file
17
packages/discord-types/src/stores/MediaEngineStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class MediaEngineStore extends FluxStore {
|
||||
isSelfMute(): boolean;
|
||||
isSelfDeaf(): boolean;
|
||||
isLocalMute(userId): boolean;
|
||||
isLocalVideoDisabled(userId): boolean;
|
||||
|
||||
setAv1Enabled(AV1: boolean): void;
|
||||
setH265Enabled(H265: boolean): void;
|
||||
setH264Enabled(H264: boolean): void;
|
||||
|
||||
getAutomaticGainControl(): boolean;
|
||||
getInputDeviceId(): string;
|
||||
getMediaEngine(): any;
|
||||
getCodecCapabilities(e: any): any;
|
||||
}
|
||||
13
packages/discord-types/src/stores/MessageStore.d.ts
vendored
Normal file
13
packages/discord-types/src/stores/MessageStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { MessageJSON, FluxStore, Message } from "..";
|
||||
|
||||
export class MessageStore extends FluxStore {
|
||||
getMessage(channelId: string, messageId: string): Message;
|
||||
/** @returns This return object is fucking huge; I'll type it later. */
|
||||
getMessages(channelId: string): unknown;
|
||||
getRawMessages(channelId: string): Record<string | number, MessageJSON>;
|
||||
hasCurrentUserSentMessage(channelId: string): boolean;
|
||||
hasPresent(channelId: string): boolean;
|
||||
isLoadingMessages(channelId: string): boolean;
|
||||
jumpedMessageId(channelId: string): string | undefined;
|
||||
whenReady(channelId: string, callback: () => void): void;
|
||||
}
|
||||
38
packages/discord-types/src/stores/RelationshipStore.d.ts
vendored
Normal file
38
packages/discord-types/src/stores/RelationshipStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class RelationshipStore extends FluxStore {
|
||||
getFriendIDs(): string[];
|
||||
getIgnoredIDs(): string[];
|
||||
getBlockedIDs(): string[];
|
||||
getBlockedOrIgnoredIDs(): string[];
|
||||
|
||||
getPendingCount(): number;
|
||||
getRelationshipCount(): number;
|
||||
|
||||
/** Related to friend nicknames. */
|
||||
getNickname(userId: string): string;
|
||||
|
||||
/** @returns Enum value from constants.RelationshipTypes */
|
||||
getRelationshipType(userId: string): number;
|
||||
isBlockedOrIgnored(userId: string): boolean;
|
||||
isBlockedOrIgnoredForMessage(userId: string): boolean;
|
||||
isBlocked(userId: string): boolean;
|
||||
isBlockedForMessage(userId: string): boolean;
|
||||
isFriend(userId: string): boolean;
|
||||
isIgnored(userId: string): boolean;
|
||||
isIgnoredForMessage(userId: string): boolean;
|
||||
isUnfilteredPendingIncoming(userId: string): boolean;
|
||||
isSpam(userId): boolean;
|
||||
getSince(userId: string): string;
|
||||
getSinces(): Record<number, string>;
|
||||
|
||||
getFriendCount(): number;
|
||||
getRelationshipCount(): number;
|
||||
getOutgoingCount(): number;
|
||||
getPendingCount(): number;
|
||||
getPendingIgnoredCount(): number;
|
||||
getSpamCount(): number;
|
||||
|
||||
getMutableRelationships(): Map<string, number>;
|
||||
getVersion(): number;
|
||||
}
|
||||
14
packages/discord-types/src/stores/SelectedChannelStore.d.ts
vendored
Normal file
14
packages/discord-types/src/stores/SelectedChannelStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class SelectedChannelStore extends FluxStore {
|
||||
getChannelId(guildId?: string | null): string;
|
||||
getVoiceChannelId(): string | undefined;
|
||||
getCurrentlySelectedChannelId(guildId?: string): string | undefined;
|
||||
getMostRecentSelectedTextChannelId(guildId: string): string | undefined;
|
||||
getLastSelectedChannelId(guildId?: string): string;
|
||||
// yes this returns a string
|
||||
getLastSelectedChannels(guildId?: string): string;
|
||||
|
||||
/** If you follow an announcement channel, this will return whichever channel you chose as destination */
|
||||
getLastChannelFollowingDestination(): { guildId?: string; channelId?: string; } | undefined;
|
||||
}
|
||||
14
packages/discord-types/src/stores/SelectedGuildStore.d.ts
vendored
Normal file
14
packages/discord-types/src/stores/SelectedGuildStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export interface SelectedGuildState {
|
||||
selectedGuildTimestampMillis: Record<string | number, number>;
|
||||
selectedGuildId: string | null;
|
||||
lastSelectedGuildId: string | null;
|
||||
}
|
||||
|
||||
export class SelectedGuildStore extends FluxStore {
|
||||
getGuildId(): string | null;
|
||||
getLastSelectedGuildId(): string | null;
|
||||
getLastSelectedTimestamp(guildId: string): number | null;
|
||||
getState(): SelectedGuildState | undefined;
|
||||
}
|
||||
5
packages/discord-types/src/stores/SpellCheckStore.d.ts
vendored
Normal file
5
packages/discord-types/src/stores/SpellCheckStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class SpellCheckStore extends FluxStore {
|
||||
isEnabled(): boolean;
|
||||
}
|
||||
47
packages/discord-types/src/stores/StickersStore.d.ts
vendored
Normal file
47
packages/discord-types/src/stores/StickersStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { FluxStore, GuildSticker, PremiumStickerPack, Sticker } from "..";
|
||||
|
||||
export type StickerGuildMap = Map<string, GuildSticker[]>;
|
||||
|
||||
export interface StickerPackMeta {
|
||||
id: string;
|
||||
title: string;
|
||||
author?: {
|
||||
name: string;
|
||||
url?: string;
|
||||
};
|
||||
logo: Sticker;
|
||||
|
||||
dynamic?: DynamicStickerPackMeta["dynamic"];
|
||||
}
|
||||
|
||||
export interface DynamicStickerPackMeta extends StickerPackMeta {
|
||||
dynamic: {
|
||||
version?: string;
|
||||
refreshUrl: string;
|
||||
authHeaders?: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface StickerPack extends StickerPackMeta {
|
||||
stickers: Sticker[];
|
||||
}
|
||||
|
||||
|
||||
export interface StickerCategoryType {
|
||||
id: string;
|
||||
name: string;
|
||||
iconUrl?: string;
|
||||
}
|
||||
|
||||
export class StickersStore extends FluxStore {
|
||||
getAllGuildStickers(): StickerGuildMap;
|
||||
getRawStickersByGuild(): StickerGuildMap;
|
||||
getPremiumPacks(): PremiumStickerPack[];
|
||||
getAllStickersIterator(): IterableIterator<Sticker>;
|
||||
|
||||
getStickerById(id: string): Sticker | undefined;
|
||||
getStickerPack(id: string): PremiumStickerPack | undefined;
|
||||
getStickersByGuildId(guildId: string): Sticker[] | undefined;
|
||||
|
||||
isPremiumPack(id: string): boolean;
|
||||
}
|
||||
11
packages/discord-types/src/stores/StreamerModeStore.d.ts
vendored
Normal file
11
packages/discord-types/src/stores/StreamerModeStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { FluxStore } from "@vencord/discord-types";
|
||||
|
||||
export class StreamerModeStore extends FluxStore {
|
||||
get autoToggle(): boolean;
|
||||
get disableNotifications(): boolean;
|
||||
get disableSounds(): boolean;
|
||||
get enableContentProtection(): boolean;
|
||||
get enabled(): boolean;
|
||||
get hideInstantInvites(): boolean;
|
||||
get hidePersonalInformation(): boolean;
|
||||
}
|
||||
18
packages/discord-types/src/stores/ThemeStore.d.ts
vendored
Normal file
18
packages/discord-types/src/stores/ThemeStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export type ThemePreference = "dark" | "light" | "unknown";
|
||||
export type SystemTheme = "dark" | "light";
|
||||
export type Theme = "light" | "dark" | "darker" | "midnight";
|
||||
|
||||
export interface ThemeState {
|
||||
theme: Theme;
|
||||
status: 0 | 1;
|
||||
preferences: Record<ThemePreference, Theme>;
|
||||
}
|
||||
export class ThemeStore extends FluxStore {
|
||||
get theme(): Theme;
|
||||
get darkSidebar(): boolean;
|
||||
get systemTheme(): SystemTheme;
|
||||
themePreferenceForSystemTheme(preference: ThemePreference): Theme;
|
||||
getState(): ThemeState;
|
||||
}
|
||||
9
packages/discord-types/src/stores/TypingStore.d.ts
vendored
Normal file
9
packages/discord-types/src/stores/TypingStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class TypingStore extends FluxStore {
|
||||
/**
|
||||
* returns a map of user ids to timeout ids
|
||||
*/
|
||||
getTypingUsers(channelId: string): Record<string, number>;
|
||||
isTyping(channelId: string, userId: string): boolean;
|
||||
}
|
||||
151
packages/discord-types/src/stores/UserProfileStore.d.ts
vendored
Normal file
151
packages/discord-types/src/stores/UserProfileStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { FluxStore, Guild, User, Application, ApplicationInstallParams } from "..";
|
||||
import { ApplicationIntegrationType } from "../../enums";
|
||||
|
||||
export interface MutualFriend {
|
||||
/**
|
||||
* the userid of the mutual friend
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* the status of the mutual friend
|
||||
*/
|
||||
status: "online" | "offline" | "idle" | "dnd";
|
||||
/**
|
||||
* the user object of the mutual friend
|
||||
*/
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface MutualGuild {
|
||||
/**
|
||||
* the guild object of the mutual guild
|
||||
*/
|
||||
guild: Guild;
|
||||
/**
|
||||
* the user's nickname in the guild, if any
|
||||
*/
|
||||
nick: string | null;
|
||||
|
||||
}
|
||||
|
||||
export interface ProfileBadge {
|
||||
id: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
export interface ConnectedAccount {
|
||||
type: "twitch" | "youtube" | "skype" | "steam" | "leagueoflegends" | "battlenet" | "bluesky" | "bungie" | "reddit" | "twitter" | "twitter_legacy" | "spotify" | "facebook" | "xbox" | "samsung" | "contacts" | "instagram" | "mastodon" | "soundcloud" | "github" | "playstation" | "playstation-stg" | "epicgames" | "riotgames" | "roblox" | "paypal" | "ebay" | "tiktok" | "crunchyroll" | "domain" | "amazon-music";
|
||||
/**
|
||||
* underlying id of connected account
|
||||
* eg. account uuid
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* display name of connected account
|
||||
*/
|
||||
name: string;
|
||||
verified: boolean;
|
||||
metadata?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ProfileApplication {
|
||||
id: string;
|
||||
customInstallUrl: string | undefined;
|
||||
installParams: ApplicationInstallParams | undefined;
|
||||
flags: number;
|
||||
popularApplicationCommandIds?: string[];
|
||||
integrationTypesConfig: Record<ApplicationIntegrationType, Partial<{
|
||||
oauth2_install_params: ApplicationInstallParams;
|
||||
}>>;
|
||||
primarySkuId: string | undefined;
|
||||
storefront_available: boolean;
|
||||
}
|
||||
|
||||
export interface UserProfileBase extends Pick<User, "banner"> {
|
||||
accentColor: number | null;
|
||||
/**
|
||||
* often empty for guild profiles, get the user profile for badges
|
||||
*/
|
||||
badges: ProfileBadge[];
|
||||
bio: string | undefined;
|
||||
popoutAnimationParticleType: string | null;
|
||||
profileEffectExpiresAt: number | Date | undefined;
|
||||
profileEffectId: undefined | string;
|
||||
/**
|
||||
* often an empty string when not set
|
||||
*/
|
||||
pronouns: string | "" | undefined;
|
||||
themeColors: [number, number] | undefined;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export interface ApplicationRoleConnection {
|
||||
application: Application;
|
||||
application_metadata: Record<string, any>;
|
||||
metadata: Record<string, any>;
|
||||
platform_name: string;
|
||||
platform_username: string;
|
||||
}
|
||||
|
||||
export interface UserProfile extends UserProfileBase, Pick<User, "premiumType"> {
|
||||
/** If this is a bot user profile, this will be its application */
|
||||
application: ProfileApplication | null;
|
||||
applicationRoleConnections: ApplicationRoleConnection[] | undefined;
|
||||
connectedAccounts: ConnectedAccount[] | undefined;
|
||||
fetchStartedAt: number;
|
||||
fetchEndedAt: number;
|
||||
legacyUsername: string | undefined;
|
||||
premiumGuildSince: Date | null;
|
||||
premiumSince: Date | null;
|
||||
}
|
||||
|
||||
export class UserProfileStore extends FluxStore {
|
||||
/**
|
||||
* @param userId the user ID of the profile being fetched.
|
||||
* @param guildId the guild ID to of the profile being fetched.
|
||||
* defaults to the internal symbol `NO GUILD ID` if nullish
|
||||
*
|
||||
* @returns true if the profile is being fetched, false otherwise.
|
||||
*/
|
||||
isFetchingProfile(userId: string, guildId?: string): boolean;
|
||||
/**
|
||||
* Check if mutual friends for {@link userId} are currently being fetched.
|
||||
*
|
||||
* @param userId the user ID of the mutual friends being fetched.
|
||||
*
|
||||
* @returns true if mutual friends are being fetched, false otherwise.
|
||||
*/
|
||||
isFetchingFriends(userId: string): boolean;
|
||||
|
||||
get isSubmitting(): boolean;
|
||||
|
||||
getUserProfile(userId: string): UserProfile | undefined;
|
||||
|
||||
getGuildMemberProfile(userId: string, guildId: string | undefined): UserProfileBase | null;
|
||||
/**
|
||||
* Get the mutual friends of a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual friends of.
|
||||
*
|
||||
* @returns an array of mutual friends, or undefined if the user has no mutual friends
|
||||
*/
|
||||
getMutualFriends(userId: string): MutualFriend[] | undefined;
|
||||
/**
|
||||
* Get the count of mutual friends for a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual friends count of.
|
||||
*
|
||||
* @returns the count of mutual friends, or undefined if the user has no mutual friends
|
||||
*/
|
||||
getMutualFriendsCount(userId: string): number | undefined;
|
||||
/**
|
||||
* Get the mutual guilds of a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual guilds of.
|
||||
*
|
||||
* @returns an array of mutual guilds, or undefined if the user has no mutual guilds
|
||||
*/
|
||||
getMutualGuilds(userId: string): MutualGuild[] | undefined;
|
||||
}
|
||||
10
packages/discord-types/src/stores/UserStore.d.ts
vendored
Normal file
10
packages/discord-types/src/stores/UserStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { FluxStore, User } from "..";
|
||||
|
||||
export class UserStore extends FluxStore {
|
||||
filter(filter: (user: User) => boolean, sort?: boolean): Record<string, User>;
|
||||
findByTag(username: string, discriminator: string): User;
|
||||
forEach(action: (user: User) => void): void;
|
||||
getCurrentUser(): User;
|
||||
getUser(userId: string): User;
|
||||
getUsers(): Record<string, User>;
|
||||
}
|
||||
45
packages/discord-types/src/stores/VoiceStateStore.d.ts
vendored
Normal file
45
packages/discord-types/src/stores/VoiceStateStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { DiscordRecord } from "../common";
|
||||
import { FluxStore } from "./FluxStore";
|
||||
|
||||
export type UserVoiceStateRecords = Record<string, VoiceState>;
|
||||
export type VoiceStates = Record<string, UserVoiceStateRecords>;
|
||||
|
||||
export interface VoiceState extends DiscordRecord {
|
||||
userId: string;
|
||||
channelId: string | null | undefined;
|
||||
sessionId: string | null | undefined;
|
||||
mute: boolean;
|
||||
deaf: boolean;
|
||||
selfMute: boolean;
|
||||
selfDeaf: boolean;
|
||||
selfVideo: boolean;
|
||||
selfStream: boolean | undefined;
|
||||
suppress: boolean;
|
||||
requestToSpeakTimestamp: string | null | undefined;
|
||||
discoverable: boolean;
|
||||
|
||||
isVoiceMuted(): boolean;
|
||||
isVoiceDeafened(): boolean;
|
||||
|
||||
oldChannelId?: string;
|
||||
guildId?: string;
|
||||
}
|
||||
|
||||
export class VoiceStateStore extends FluxStore {
|
||||
getAllVoiceStates(): VoiceStates;
|
||||
|
||||
getVoiceStates(guildId?: string | null): UserVoiceStateRecords;
|
||||
getVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
|
||||
getVideoVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
|
||||
|
||||
getVoiceState(guildId: string | null, userId: string): VoiceState | undefined;
|
||||
getUserVoiceChannelId(guildId: string | null, userId: string): string | undefined;
|
||||
getVoiceStateForChannel(channelId: string, userId?: string): VoiceState | undefined;
|
||||
getVoiceStateForUser(userId: string): VoiceState | undefined;
|
||||
|
||||
getCurrentClientVoiceChannelId(guildId: string | null): string | undefined;
|
||||
isCurrentClientInVoiceChannel(): boolean;
|
||||
|
||||
isInChannel(channelId: string, userId?: string): boolean;
|
||||
hasVideo(channelId: string): boolean;
|
||||
}
|
||||
7
packages/discord-types/src/stores/WindowStore.d.ts
vendored
Normal file
7
packages/discord-types/src/stores/WindowStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class WindowStore extends FluxStore {
|
||||
isElementFullScreen(): boolean;
|
||||
isFocused(): boolean;
|
||||
windowSize(): Record<"width" | "height", number>;
|
||||
}
|
||||
41
packages/discord-types/src/stores/index.d.ts
vendored
Normal file
41
packages/discord-types/src/stores/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// please keep in alphabetical order
|
||||
export * from "./AuthenticationStore";
|
||||
export * from "./ChannelStore";
|
||||
export * from "./DraftStore";
|
||||
export * from "./EmojiStore";
|
||||
export * from "./FluxStore";
|
||||
export * from "./GuildMemberStore";
|
||||
export * from "./GuildRoleStore";
|
||||
export * from "./GuildStore";
|
||||
export * from "./MessageStore";
|
||||
export * from "./RelationshipStore";
|
||||
export * from "./MediaEngineStore";
|
||||
export * from "./StreamerModeStore";
|
||||
export * from "./SpellCheckStore";
|
||||
export * from "./SelectedChannelStore";
|
||||
export * from "./SelectedGuildStore";
|
||||
export * from "./StickersStore";
|
||||
export * from "./StreamerModeStore";
|
||||
export * from "./ThemeStore";
|
||||
export * from "./TypingStore";
|
||||
export * from "./UserProfileStore";
|
||||
export * from "./UserStore";
|
||||
export * from "./VoiceStateStore";
|
||||
export * from "./WindowStore";
|
||||
|
||||
/**
|
||||
* React hook that returns stateful data for one or more stores
|
||||
* You might need a custom comparator (4th argument) if your store data is an object
|
||||
* @param stores The stores to listen to
|
||||
* @param mapper A function that returns the data you need
|
||||
* @param dependencies An array of reactive values which the hook depends on. Use this if your mapper or equality function depends on the value of another hook
|
||||
* @param isEqual A custom comparator for the data returned by mapper
|
||||
*
|
||||
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
||||
*/
|
||||
export type useStateFromStores = <T>(
|
||||
stores: any[],
|
||||
mapper: () => T,
|
||||
dependencies?: any,
|
||||
isEqual?: (old: T, newer: T) => boolean
|
||||
) => T;
|
||||
|
|
@ -1,22 +1,4 @@
|
|||
/*
|
||||
* 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 { Channel, Guild, GuildMember, Message, User } from "discord-types/general";
|
||||
import { Channel, Guild, GuildMember, Message, User } from ".";
|
||||
import type { ReactNode } from "react";
|
||||
import { LiteralUnion } from "type-fest";
|
||||
|
||||
|
|
@ -24,15 +6,17 @@ import type { FluxEvents } from "./fluxEvents";
|
|||
|
||||
export { FluxEvents };
|
||||
|
||||
type FluxEventsAutoComplete = LiteralUnion<FluxEvents, string>;
|
||||
|
||||
export interface FluxDispatcher {
|
||||
_actionHandlers: any;
|
||||
_interceptors: any;
|
||||
_subscriptions: any;
|
||||
addInterceptor(interceptor: any): void;
|
||||
dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise<void>;
|
||||
dispatch(event: { [key: string]: unknown; type: FluxEventsAutoComplete; }): Promise<void>;
|
||||
isDispatching(): boolean;
|
||||
subscribe(event: FluxEvents, callback: (data: any) => void): void;
|
||||
unsubscribe(event: FluxEvents, callback: (data: any) => void): void;
|
||||
subscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||
unsubscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||
wait(callback: () => void): void;
|
||||
}
|
||||
|
||||
|
|
@ -337,3 +321,19 @@ export interface DateUtils {
|
|||
dateFormat(date: Date, format: string): string;
|
||||
diffAsUnits(start: Date, end: Date, stopAtOneSecond?: boolean): Record<"days" | "hours" | "minutes" | "seconds", number>;
|
||||
}
|
||||
|
||||
export interface CommandOptions {
|
||||
type: number;
|
||||
name: string;
|
||||
description: string;
|
||||
required?: boolean;
|
||||
choices?: {
|
||||
name: string;
|
||||
values: string | number;
|
||||
}[];
|
||||
options?: CommandOptions[];
|
||||
channel_types?: number[];
|
||||
min_value?: number;
|
||||
max_value?: number;
|
||||
autocomplete?: boolean;
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* @vencord/discord-types
|
||||
* Copyright (c) 2024 Vendicated, Nuckyz and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { SYM_ORIGINAL_FACTORY, SYM_PATCHED_BY, SYM_PATCHED_SOURCE } from "./patchWebpack";
|
||||
|
||||
export type ModuleExports = any;
|
||||
|
||||
export type Module = {
|
||||
|
|
@ -215,24 +213,3 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
|
|||
/** rspack unique id */
|
||||
ruid: string;
|
||||
};
|
||||
|
||||
// Utility section for Vencord
|
||||
|
||||
export type AnyWebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & Partial<Omit<WebpackRequire, "m">> & {
|
||||
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
|
||||
m: Record<PropertyKey, AnyModuleFactory>;
|
||||
};
|
||||
|
||||
/** exports can be anything, however initially it is always an empty object */
|
||||
export type AnyModuleFactory = ((this: ModuleExports, module: Module, exports: ModuleExports, require: AnyWebpackRequire) => void) & {
|
||||
[SYM_PATCHED_SOURCE]?: string;
|
||||
[SYM_PATCHED_BY]?: Set<string>;
|
||||
};
|
||||
|
||||
export type PatchedModuleFactory = AnyModuleFactory & {
|
||||
[SYM_ORIGINAL_FACTORY]: AnyModuleFactory;
|
||||
[SYM_PATCHED_SOURCE]?: string;
|
||||
[SYM_PATCHED_BY]?: Set<string>;
|
||||
};
|
||||
|
||||
export type MaybePatchedModuleFactory = PatchedModuleFactory | AnyModuleFactory;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@vencord/types",
|
||||
"name": "@equicord/types",
|
||||
"private": false,
|
||||
"version": "1.11.5",
|
||||
"version": "1.13.3",
|
||||
"description": "",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
|
|
@ -19,10 +19,14 @@
|
|||
"dependencies": {
|
||||
"@types/lodash": "4.17.15",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"discord-types": "^1.3.26",
|
||||
"standalone-electron-types": "^34.2.0",
|
||||
"@vencord/discord-types": "^1.0.0",
|
||||
"highlight.js": "11.11.1",
|
||||
"moment": "^2.22.2",
|
||||
"ts-pattern": "^5.6.0",
|
||||
"type-fest": "^4.35.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
diff --git a/dist/index.js b/dist/index.js
|
||||
index 67de6fb139070fd0e49beca65e3b63c531202e16..aa2883c8126e4952a42872ee920f59547a066430 100644
|
||||
--- a/dist/index.js
|
||||
+++ b/dist/index.js
|
||||
@@ -1 +1 @@
|
||||
-var C=Object.create;var f=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},y=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!F.call(e,s)&&s!==r&&f(e,s,{get:()=>t[s],enumerable:!(i=I(t,s))||i.enumerable});return e};var b=(e,t,r)=>(r=e!=null?C(S(e)):{},y(t||!e||!e.__esModule?f(r,"default",{value:e,enumerable:!0}):r,e)),D=e=>y(f({},"__esModule",{value:!0}),e);var N={};$(N,{default:()=>J});module.exports=D(N);var h="eslint-plugin-path-alias",v="2.0.0";var l=require("path"),M=b(require("nanomatch"));function j(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}var R=require("get-tsconfig"),a=require("path"),w=b(require("find-pkg")),O=require("fs");function P(e){if(e.options[0]?.paths)return z(e);let t=e.getFilename?.()??e.filename,r=(0,R.getTsconfig)(t);if(r?.config?.compilerOptions?.paths)return q(r);let i=w.default.sync((0,a.dirname)(t));if(!i)return;let s=JSON.parse((0,O.readFileSync)(i).toString());if(s?.imports)return L(s,i)}function L(e,t){let r=new Map,i=e.imports??{},s=(0,a.dirname)(t);return Object.entries(i).forEach(([o,n])=>{if(!n||typeof n!="string")return;let p=(0,a.resolve)(s,n);r.set(o,[p])}),r}function q(e){let t=new Map,r=e?.config?.compilerOptions?.paths??{},i=(0,a.dirname)(e.path);return e.config.compilerOptions?.baseUrl&&(i=(0,a.resolve)((0,a.dirname)(e.path),e.config.compilerOptions.baseUrl)),Object.entries(r).forEach(([s,o])=>{s=s.replace(/\/\*$/,""),o=o.map(n=>(0,a.resolve)(i,n.replace(/\/\*$/,""))),t.set(s,o)}),t}function z(e){let t=new Map,r=e.options[0]?.paths??{};return Object.entries(r).forEach(([i,s])=>{if(!s||typeof s!="string")return;if(s.startsWith("/")){t.set(i,[s]);return}let o=e.getCwd?.()??e.cwd,n=(0,a.resolve)(o,s);t.set(i,[n])}),t}var T={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:j("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let t=e.options[0]?.exceptions,r=e.getFilename?.()??e.filename,i=P(e);return i?.size?{ImportExpression(s){if(s.source.type!=="Literal"||typeof s.source.value!="string")return;let o=s.source.raw,n=s.source.value;if(!/^(\.?\.\/)/.test(n))return;let p=(0,l.resolve)((0,l.dirname)(r),n);if(A(p,t))return;let c=k(p,i);c&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:c},fix(m){let g=E(p,c,i.get(c)),d=o.replace(n,g);return m.replaceText(s.source,d)}})},ImportDeclaration(s){if(typeof s.source.value!="string")return;let o=s.source.value;if(!/^(\.?\.\/)/.test(o))return;let n=(0,l.resolve)((0,l.dirname)(r),o),p=A(n,t),u=k(n,i);p||u&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:u},fix(c){let m=s.source.raw,g=E(n,u,i.get(u)),d=m.replace(o,g);return c.replaceText(s.source,d)}})}}:{}}};function k(e,t){return Array.from(t.keys()).find(r=>t.get(r).some(s=>e.indexOf(s)===0))}function A(e,t){if(!t)return!1;let r=(0,l.basename)(e);return(0,M.default)(r,t).includes(r)}function E(e,t,r){for(let i of r)if(e.indexOf(i)===0)return e.replace(i,t)}var J={name:h,version:v,meta:{name:h,version:v},rules:{"no-relative":T}};
|
||||
+var C=Object.create;var f=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},y=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!F.call(e,s)&&s!==r&&f(e,s,{get:()=>t[s],enumerable:!(i=I(t,s))||i.enumerable});return e};var b=(e,t,r)=>(r=e!=null?C(S(e)):{},y(t||!e||!e.__esModule?f(r,"default",{value:e,enumerable:!0}):r,e)),D=e=>y(f({},"__esModule",{value:!0}),e);var N={};$(N,{default:()=>J});module.exports=D(N);var h="eslint-plugin-path-alias",v="2.0.0";var l=require("path"),M=b(require("nanomatch"));function j(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}var R=require("get-tsconfig"),a=require("path"),w=b(require("find-pkg")),O=require("fs");function P(e){if(e.options[0]?.paths)return z(e);let t=e.getFilename?.()??e.filename,r=(0,R.getTsconfig)(t);if(r?.config?.compilerOptions?.paths)return q(r);let i=w.default.sync((0,a.dirname)(t));if(!i)return;let s=JSON.parse((0,O.readFileSync)(i).toString());if(s?.imports)return L(s,i)}function L(e,t){let r=new Map,i=e.imports??{},s=(0,a.dirname)(t);return Object.entries(i).forEach(([o,n])=>{if(!n||typeof n!="string")return;let p=(0,a.resolve)(s,n);r.set(o,[p])}),r}function q(e){let t=new Map,r=e?.config?.compilerOptions?.paths??{},i=(0,a.dirname)(e.path);return e.config.compilerOptions?.baseUrl&&(i=(0,a.resolve)((0,a.dirname)(e.path),e.config.compilerOptions.baseUrl)),Object.entries(r).forEach(([s,o])=>{s=s.replace(/\/\*$/,""),o=o.map(n=>(0,a.resolve)(i,n.replace(/\/\*$/,""))),t.set(s,o)}),t}function z(e){let t=new Map,r=e.options[0]?.paths??{};return Object.entries(r).forEach(([i,s])=>{if(!s||typeof s!="string")return;if(s.startsWith("/")){t.set(i,[s]);return}let o=e.getCwd?.()??e.cwd,n=(0,a.resolve)(o,s);t.set(i,[n])}),t}var T={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:j("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let t=e.options[0]?.exceptions,r=e.getFilename?.()??e.filename,i=P(e);return i?.size?{ImportExpression(s){if(s.source.type!=="Literal"||typeof s.source.value!="string")return;let o=s.source.raw,n=s.source.value;if(!/^(\.\.\/)/.test(n))return;let p=(0,l.resolve)((0,l.dirname)(r),n);if(A(p,t))return;let c=k(p,i);c&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:c},fix(m){let g=E(p,c,i.get(c)),d=o.replace(n,g);return m.replaceText(s.source,d)}})},ImportDeclaration(s){if(typeof s.source.value!="string")return;let o=s.source.value;if(!/^(\.\.\/)/.test(o))return;let n=(0,l.resolve)((0,l.dirname)(r),o),p=A(n,t),u=k(n,i);p||u&&e.report({node:s,messageId:"shouldUseAlias",data:{alias:u},fix(c){let m=s.source.raw,g=E(n,u,i.get(u)),d=m.replace(o,g);return c.replaceText(s.source,d)}})}}:{}}};function k(e,t){return Array.from(t.keys()).find(r=>t.get(r).some(s=>e.indexOf(s)===0))}function A(e,t){if(!t)return!1;let r=(0,l.basename)(e);return(0,M.default)(r,t).includes(r)}function E(e,t,r){for(let i of r)if(e.indexOf(i)===0)return e.replace(i,t)}var J={name:h,version:v,meta:{name:h,version:v},rules:{"no-relative":T}};
|
||||
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||
index 96de18e06d4cc413e11af038cd760e4804c32e59..27e8c4e3e2c942400cc3982e52159904ca6eedfa 100644
|
||||
--- a/dist/index.mjs
|
||||
+++ b/dist/index.mjs
|
||||
@@ -1 +1 @@
|
||||
-var d="eslint-plugin-path-alias",h="2.0.0";import{dirname as x,resolve as j,basename as I}from"path";import U from"nanomatch";function y(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}import{getTsconfig as k}from"get-tsconfig";import{resolve as c,dirname as u}from"path";import A from"find-pkg";import{readFileSync as E}from"fs";function b(e){if(e.options[0]?.paths)return C(e);let s=e.getFilename?.()??e.filename,i=k(s);if(i?.config?.compilerOptions?.paths)return T(i);let r=A.sync(u(s));if(!r)return;let t=JSON.parse(E(r).toString());if(t?.imports)return M(t,r)}function M(e,s){let i=new Map,r=e.imports??{},t=u(s);return Object.entries(r).forEach(([o,n])=>{if(!n||typeof n!="string")return;let a=c(t,n);i.set(o,[a])}),i}function T(e){let s=new Map,i=e?.config?.compilerOptions?.paths??{},r=u(e.path);return e.config.compilerOptions?.baseUrl&&(r=c(u(e.path),e.config.compilerOptions.baseUrl)),Object.entries(i).forEach(([t,o])=>{t=t.replace(/\/\*$/,""),o=o.map(n=>c(r,n.replace(/\/\*$/,""))),s.set(t,o)}),s}function C(e){let s=new Map,i=e.options[0]?.paths??{};return Object.entries(i).forEach(([r,t])=>{if(!t||typeof t!="string")return;if(t.startsWith("/")){s.set(r,[t]);return}let o=e.getCwd?.()??e.cwd,n=c(o,t);s.set(r,[n])}),s}var P={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:y("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let s=e.options[0]?.exceptions,i=e.getFilename?.()??e.filename,r=b(e);return r?.size?{ImportExpression(t){if(t.source.type!=="Literal"||typeof t.source.value!="string")return;let o=t.source.raw,n=t.source.value;if(!/^(\.?\.\/)/.test(n))return;let a=j(x(i),n);if(w(a,s))return;let l=R(a,r);l&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:l},fix(f){let m=O(a,l,r.get(l)),g=o.replace(n,m);return f.replaceText(t.source,g)}})},ImportDeclaration(t){if(typeof t.source.value!="string")return;let o=t.source.value;if(!/^(\.?\.\/)/.test(o))return;let n=j(x(i),o),a=w(n,s),p=R(n,r);a||p&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:p},fix(l){let f=t.source.raw,m=O(n,p,r.get(p)),g=f.replace(o,m);return l.replaceText(t.source,g)}})}}:{}}};function R(e,s){return Array.from(s.keys()).find(i=>s.get(i).some(t=>e.indexOf(t)===0))}function w(e,s){if(!s)return!1;let i=I(e);return U(i,s).includes(i)}function O(e,s,i){for(let r of i)if(e.indexOf(r)===0)return e.replace(r,s)}var Q={name:d,version:h,meta:{name:d,version:h},rules:{"no-relative":P}};export{Q as default};
|
||||
+var d="eslint-plugin-path-alias",h="2.0.0";import{dirname as x,resolve as j,basename as I}from"path";import U from"nanomatch";function y(e){return`https://github/com/msfragala/eslint-plugin-path-alias/blob/master/docs/rules/${e}.md`}import{getTsconfig as k}from"get-tsconfig";import{resolve as c,dirname as u}from"path";import A from"find-pkg";import{readFileSync as E}from"fs";function b(e){if(e.options[0]?.paths)return C(e);let s=e.getFilename?.()??e.filename,i=k(s);if(i?.config?.compilerOptions?.paths)return T(i);let r=A.sync(u(s));if(!r)return;let t=JSON.parse(E(r).toString());if(t?.imports)return M(t,r)}function M(e,s){let i=new Map,r=e.imports??{},t=u(s);return Object.entries(r).forEach(([o,n])=>{if(!n||typeof n!="string")return;let a=c(t,n);i.set(o,[a])}),i}function T(e){let s=new Map,i=e?.config?.compilerOptions?.paths??{},r=u(e.path);return e.config.compilerOptions?.baseUrl&&(r=c(u(e.path),e.config.compilerOptions.baseUrl)),Object.entries(i).forEach(([t,o])=>{t=t.replace(/\/\*$/,""),o=o.map(n=>c(r,n.replace(/\/\*$/,""))),s.set(t,o)}),s}function C(e){let s=new Map,i=e.options[0]?.paths??{};return Object.entries(i).forEach(([r,t])=>{if(!t||typeof t!="string")return;if(t.startsWith("/")){s.set(r,[t]);return}let o=e.getCwd?.()??e.cwd,n=c(o,t);s.set(r,[n])}),s}var P={meta:{type:"suggestion",docs:{description:"Ensure imports use path aliases whenever possible vs. relative paths",url:y("no-relative")},fixable:"code",schema:[{type:"object",properties:{exceptions:{type:"array",items:{type:"string"}},paths:{type:"object"}},additionalProperties:!1}],messages:{shouldUseAlias:"Import should use path alias instead of relative path"}},create(e){let s=e.options[0]?.exceptions,i=e.getFilename?.()??e.filename,r=b(e);return r?.size?{ImportExpression(t){if(t.source.type!=="Literal"||typeof t.source.value!="string")return;let o=t.source.raw,n=t.source.value;if(!/^(\.\.\/)/.test(n))return;let a=j(x(i),n);if(w(a,s))return;let l=R(a,r);l&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:l},fix(f){let m=O(a,l,r.get(l)),g=o.replace(n,m);return f.replaceText(t.source,g)}})},ImportDeclaration(t){if(typeof t.source.value!="string")return;let o=t.source.value;if(!/^(\.\.\/)/.test(o))return;let n=j(x(i),o),a=w(n,s),p=R(n,r);a||p&&e.report({node:t,messageId:"shouldUseAlias",data:{alias:p},fix(l){let f=t.source.raw,m=O(n,p,r.get(p)),g=f.replace(o,m);return l.replaceText(t.source,g)}})}}:{}}};function R(e,s){return Array.from(s.keys()).find(i=>s.get(i).some(t=>e.indexOf(t)===0))}function w(e,s){if(!s)return!1;let i=I(e);return U(i,s).includes(i)}function O(e,s,i){for(let r of i)if(e.indexOf(r)===0)return e.replace(r,s)}var Q={name:d,version:h,meta:{name:d,version:h},rules:{"no-relative":P}};export{Q as default};
|
||||
|
|
@ -24,7 +24,7 @@ import { readdir, writeFile } from "fs/promises";
|
|||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_COMPANION_TEST, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, buildOrWatchAll, stringifyValues } from "./common.mjs";
|
||||
import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_COMPANION_TEST, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, commonRendererPlugins, watch, buildOrWatchAll, stringifyValues, IS_ANTI_CRASH_TEST } from "./common.mjs";
|
||||
|
||||
const defines = stringifyValues({
|
||||
IS_STANDALONE,
|
||||
|
|
@ -32,6 +32,7 @@ const defines = stringifyValues({
|
|||
IS_REPORTER,
|
||||
IS_COMPANION_TEST,
|
||||
IS_UPDATER_DISABLED,
|
||||
IS_ANTI_CRASH_TEST,
|
||||
IS_WEB: false,
|
||||
IS_EXTENSION: false,
|
||||
IS_USERSCRIPT: false,
|
||||
|
|
@ -54,7 +55,7 @@ const nodeCommonOpts = {
|
|||
format: "cjs",
|
||||
platform: "node",
|
||||
target: ["esnext"],
|
||||
// @ts-ignore this is never undefined
|
||||
// @ts-expect-error this is never undefined
|
||||
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@
|
|||
|
||||
import { readFileSync } from "fs";
|
||||
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import Zip from "zip-local";
|
||||
import path, { join } from "path";
|
||||
|
||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, IS_COMPANION_TEST, VERSION, commonRendererPlugins, buildOrWatchAll, stringifyValues } from "./common.mjs";
|
||||
import { BUILD_TIMESTAMP, commonOpts, globPlugins, IS_DEV, IS_REPORTER, IS_COMPANION_TEST, VERSION, commonRendererPlugins, buildOrWatchAll, stringifyValues, IS_ANTI_CRASH_TEST } from "./common.mjs";
|
||||
import AdmZip from "adm-zip";
|
||||
|
||||
/**
|
||||
* @type {import("esbuild").BuildOptions}
|
||||
|
|
@ -48,6 +48,7 @@ const commonOptions = {
|
|||
IS_DEV,
|
||||
IS_REPORTER,
|
||||
IS_COMPANION_TEST,
|
||||
IS_ANTI_CRASH_TEST,
|
||||
IS_DISCORD_DESKTOP: false,
|
||||
IS_VESKTOP: false,
|
||||
IS_EQUIBOP: false,
|
||||
|
|
@ -62,14 +63,6 @@ const MonacoWorkerEntryPoints = [
|
|||
"vs/editor/editor.worker.js"
|
||||
];
|
||||
|
||||
const RnNoiseFiles = [
|
||||
"dist/rnnoise.wasm",
|
||||
"dist/rnnoise_simd.wasm",
|
||||
"dist/rnnoise/workletProcessor.js",
|
||||
"LICENSE"
|
||||
];
|
||||
|
||||
|
||||
/** @type {import("esbuild").BuildOptions[]} */
|
||||
const buildConfigs = [
|
||||
{
|
||||
|
|
@ -165,9 +158,6 @@ async function buildExtension(target, files) {
|
|||
"dist/Vencord.js": await readFile("dist/browser/extension.js"),
|
||||
"dist/Vencord.css": await readFile("dist/browser/extension.css"),
|
||||
...await loadDir("dist/browser/vendor/monaco", "dist/browser/"),
|
||||
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
|
||||
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
|
||||
))),
|
||||
...Object.fromEntries(await Promise.all(files.map(async f => {
|
||||
let content = await readFile(join("browser", f));
|
||||
if (f.startsWith("manifest")) {
|
||||
|
|
@ -196,7 +186,7 @@ async function buildExtension(target, files) {
|
|||
|
||||
const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => {
|
||||
const cssRuntime = `
|
||||
;document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(
|
||||
;document.addEventListener("DOMContentLoaded", () => document.body.insertAdjacentElement("afterend",
|
||||
Object.assign(document.createElement("style"), {
|
||||
textContent: \`${content.replaceAll("`", "\\`")}\`,
|
||||
id: "vencord-css-core"
|
||||
|
|
@ -214,14 +204,15 @@ if (!process.argv.includes("--skip-extension")) {
|
|||
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
|
||||
]);
|
||||
|
||||
Zip.zip("dist/browser/chromium-unpacked", (_err, zip) => {
|
||||
zip.compress().save("dist/extension-chrome.zip");
|
||||
const chromiumZip = new AdmZip();
|
||||
chromiumZip.addLocalFolder("dist/browser/chromium-unpacked");
|
||||
chromiumZip.writeZip(path.resolve("dist/extension-chrome.zip"));
|
||||
console.info("Packed Chromium Extension written to dist/extension-chrome.zip");
|
||||
});
|
||||
Zip.zip("dist/browser/firefox-unpacked", (_err, zip) => {
|
||||
zip.compress().save("dist/extension-firefox.zip");
|
||||
|
||||
const firefoxZip = new AdmZip();
|
||||
firefoxZip.addLocalFolder("dist/browser/firefox-unpacked");
|
||||
firefoxZip.writeZip(path.resolve("dist/extension-firefox.zip"));
|
||||
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
||||
});
|
||||
} else {
|
||||
await appendCssRuntime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,14 @@ export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now
|
|||
export const watch = process.argv.includes("--watch");
|
||||
export const IS_DEV = watch || process.argv.includes("--dev");
|
||||
export const IS_REPORTER = process.argv.includes("--reporter");
|
||||
export const IS_ANTI_CRASH_TEST = process.argv.includes("--anti-crash-test");
|
||||
export const IS_STANDALONE = process.argv.includes("--standalone");
|
||||
export const IS_COMPANION_TEST = IS_REPORTER && process.argv.includes("--companion-test");
|
||||
if (!IS_COMPANION_TEST && process.argv.includes("--companion-test"))
|
||||
console.error("--companion-test must be run with --reporter for any effect");
|
||||
|
||||
export const IS_UPDATER_DISABLED = process.argv.includes("--disable-updater");
|
||||
export const gitHash = process.env.EQUICORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
|
||||
export const gitHash = process.env.EQUICORD_HASH || execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
||||
|
||||
export const banner = {
|
||||
js: `
|
||||
|
|
@ -146,7 +147,7 @@ export const globPlugins = kind => ({
|
|||
});
|
||||
|
||||
build.onLoad({ filter, namespace: "import-plugins" }, async () => {
|
||||
const pluginDirs = ["plugins/_api", "plugins/_core", "plugins", "userplugins", "equicordplugins"];
|
||||
const pluginDirs = ["plugins/_api", "plugins/_core", "plugins", "userplugins", "equicordplugins", "equicordplugins/_api"];
|
||||
let code = "";
|
||||
let pluginsCode = "\n";
|
||||
let metaCode = "\n";
|
||||
|
|
@ -162,6 +163,7 @@ export const globPlugins = kind => ({
|
|||
const fileName = file.name;
|
||||
if (fileName.startsWith("_") || fileName.startsWith(".")) continue;
|
||||
if (fileName === "index.ts") continue;
|
||||
if (/\.(zip|rar|7z|tar|gz|bz2)/.test(fileName)) continue;
|
||||
|
||||
const target = getPluginTarget(fileName);
|
||||
|
||||
|
|
@ -362,6 +364,6 @@ export const commonRendererPlugins = [
|
|||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||
// @ts-ignore this is never undefined
|
||||
// @ts-expect-error this is never undefined
|
||||
...commonOpts.plugins
|
||||
];
|
||||
|
|
|
|||
121
scripts/generateDevsList.ts
Normal file
121
scripts/generateDevsList.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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 { readFileSync, writeFileSync } from "fs";
|
||||
import { BigIntLiteral, createSourceFile, Identifier, isCallExpression, isIdentifier, isObjectLiteralExpression, isPropertyAssignment, isSatisfiesExpression, isVariableStatement, NamedDeclaration, ObjectLiteralExpression, ScriptTarget, StringLiteral } from "typescript";
|
||||
|
||||
interface Dev {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const devs = {} as Record<string, Dev>;
|
||||
const equicordDevs = {} 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 (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse devs: not an object literal");
|
||||
|
||||
for (const prop of value.expression.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");
|
||||
}
|
||||
|
||||
function parseEquicordDevs() {
|
||||
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, "EquicordDevs"));
|
||||
if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue;
|
||||
|
||||
const value = devsDeclaration.initializer.arguments[0];
|
||||
|
||||
if (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse EquicordDevs: not an object literal");
|
||||
|
||||
for (const prop of value.expression.properties) {
|
||||
const name = (prop.name as Identifier).text;
|
||||
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
|
||||
|
||||
if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse EquicordDevs: ${name} is not an object literal`);
|
||||
|
||||
equicordDevs[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 EquicordDevs constant");
|
||||
}
|
||||
|
||||
(async () => {
|
||||
parseDevs();
|
||||
parseEquicordDevs();
|
||||
|
||||
const allDevs = {
|
||||
vencord: devs,
|
||||
equicord: equicordDevs,
|
||||
};
|
||||
|
||||
const data = JSON.stringify(allDevs, null, 2);
|
||||
if (process.argv.length > 2) {
|
||||
writeFileSync(process.argv[2], data);
|
||||
} else {
|
||||
console.log(data);
|
||||
}
|
||||
})();
|
||||
|
|
@ -45,8 +45,10 @@ interface PluginData {
|
|||
commands: Command[];
|
||||
required: boolean;
|
||||
enabledByDefault: boolean;
|
||||
target: "discordDesktop" | "vencordDesktop" | "equibop" | "desktop" | "web" | "dev";
|
||||
target: "discordDesktop" | "vesktop" | "equibop" | "desktop" | "web" | "dev";
|
||||
filePath: string;
|
||||
dirName: string;
|
||||
isModified: boolean;
|
||||
}
|
||||
|
||||
const devs = {} as Record<string, Dev>;
|
||||
|
|
@ -149,6 +151,7 @@ async function parseFile(fileName: string) {
|
|||
hasCommands: false,
|
||||
enabledByDefault: false,
|
||||
required: false,
|
||||
isModified: false,
|
||||
tags: [] as string[]
|
||||
} as PluginData;
|
||||
|
||||
|
|
@ -167,27 +170,33 @@ async function parseFile(fileName: string) {
|
|||
break;
|
||||
case "commands":
|
||||
data.hasCommands = true;
|
||||
if (!isArrayLiteralExpression(value)) throw fail("commands is not an array literal");
|
||||
if (isArrayLiteralExpression(value)) {
|
||||
data.commands = value.elements.map((e) => {
|
||||
if (isObjectLiteralExpression(e)) {
|
||||
const nameProperty = e.properties.find((p): p is PropertyAssignment => {
|
||||
return isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === 'name';
|
||||
});
|
||||
const descriptionProperty = e.properties.find((p): p is PropertyAssignment => {
|
||||
return isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === 'description';
|
||||
});
|
||||
const nameProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "name"
|
||||
);
|
||||
const descriptionProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "description"
|
||||
);
|
||||
if (!nameProperty || !descriptionProperty) throw fail("command missing required properties");
|
||||
const name = isStringLiteral(nameProperty.initializer) ? nameProperty.initializer.text : '';
|
||||
const description = isStringLiteral(descriptionProperty.initializer) ? descriptionProperty.initializer.text : '';
|
||||
const name = isStringLiteral(nameProperty.initializer) ? nameProperty.initializer.text : "";
|
||||
const description = isStringLiteral(descriptionProperty.initializer) ? descriptionProperty.initializer.text : "";
|
||||
return { name, description };
|
||||
} else if (isCallExpression(e) && isIdentifier(e.expression)) {
|
||||
const [nameArg] = e.arguments;
|
||||
if (!isStringLiteral(nameArg)) throw fail("first argument must be a string");
|
||||
const name = nameArg.text;
|
||||
return { name, description: "" };
|
||||
return { name: nameArg.text, description: "" };
|
||||
} else if (e.kind === SyntaxKind.SpreadElement) {
|
||||
return undefined;
|
||||
}
|
||||
throw fail("commands array contains invalid elements");
|
||||
});
|
||||
}).filter((c): c is { name: string; description: string; } => Boolean(c)) as Command[];
|
||||
} else if (isIdentifier(value)) {
|
||||
data.commands = [];
|
||||
} else {
|
||||
throw fail("commands is not an array literal or identifier");
|
||||
}
|
||||
break;
|
||||
case "authors":
|
||||
if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal");
|
||||
|
|
@ -212,6 +221,7 @@ async function parseFile(fileName: string) {
|
|||
data.dependencies = (elements as NodeArray<StringLiteral>).map(e => e.text);
|
||||
break;
|
||||
case "required":
|
||||
case "isModified":
|
||||
case "enabledByDefault":
|
||||
data[key] = value.kind === SyntaxKind.TrueKeyword;
|
||||
break;
|
||||
|
|
@ -227,10 +237,15 @@ async function parseFile(fileName: string) {
|
|||
}
|
||||
|
||||
data.filePath = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "");
|
||||
|
||||
data.dirName = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "")
|
||||
.replace(/^src\/plugins\//, "");
|
||||
.replace(/^src\/equicordplugins\//, "");
|
||||
|
||||
return [data] as const;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ interface PluginData {
|
|||
enabledByDefault: boolean;
|
||||
target: "discordDesktop" | "vesktop" | "equibop" | "desktop" | "web" | "dev";
|
||||
filePath: string;
|
||||
dirName: string;
|
||||
isModified: boolean;
|
||||
}
|
||||
|
||||
const devs = {} as Record<string, Dev>;
|
||||
|
|
@ -149,6 +151,7 @@ async function parseFile(fileName: string) {
|
|||
hasCommands: false,
|
||||
enabledByDefault: false,
|
||||
required: false,
|
||||
isModified: false,
|
||||
tags: [] as string[]
|
||||
} as PluginData;
|
||||
|
||||
|
|
@ -167,27 +170,33 @@ async function parseFile(fileName: string) {
|
|||
break;
|
||||
case "commands":
|
||||
data.hasCommands = true;
|
||||
if (!isArrayLiteralExpression(value)) throw fail("commands is not an array literal");
|
||||
if (isArrayLiteralExpression(value)) {
|
||||
data.commands = value.elements.map((e) => {
|
||||
if (isObjectLiteralExpression(e)) {
|
||||
const nameProperty = e.properties.find((p): p is PropertyAssignment => {
|
||||
return isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === 'name';
|
||||
});
|
||||
const descriptionProperty = e.properties.find((p): p is PropertyAssignment => {
|
||||
return isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === 'description';
|
||||
});
|
||||
const nameProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "name"
|
||||
);
|
||||
const descriptionProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "description"
|
||||
);
|
||||
if (!nameProperty || !descriptionProperty) throw fail("command missing required properties");
|
||||
const name = isStringLiteral(nameProperty.initializer) ? nameProperty.initializer.text : '';
|
||||
const description = isStringLiteral(descriptionProperty.initializer) ? descriptionProperty.initializer.text : '';
|
||||
const name = isStringLiteral(nameProperty.initializer) ? nameProperty.initializer.text : "";
|
||||
const description = isStringLiteral(descriptionProperty.initializer) ? descriptionProperty.initializer.text : "";
|
||||
return { name, description };
|
||||
} else if (isCallExpression(e) && isIdentifier(e.expression)) {
|
||||
const [nameArg] = e.arguments;
|
||||
if (!isStringLiteral(nameArg)) throw fail("first argument must be a string");
|
||||
const name = nameArg.text;
|
||||
return { name, description: "" };
|
||||
return { name: nameArg.text, description: "" };
|
||||
} else if (e.kind === SyntaxKind.SpreadElement) {
|
||||
return undefined;
|
||||
}
|
||||
throw fail("commands array contains invalid elements");
|
||||
});
|
||||
}).filter((c): c is { name: string; description: string; } => Boolean(c)) as Command[];
|
||||
} else if (isIdentifier(value)) {
|
||||
data.commands = [];
|
||||
} else {
|
||||
throw fail("commands is not an array literal or identifier");
|
||||
}
|
||||
break;
|
||||
case "authors":
|
||||
if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal");
|
||||
|
|
@ -212,6 +221,7 @@ async function parseFile(fileName: string) {
|
|||
data.dependencies = (elements as NodeArray<StringLiteral>).map(e => e.text);
|
||||
break;
|
||||
case "required":
|
||||
case "isModified":
|
||||
case "enabledByDefault":
|
||||
data[key] = value.kind === SyntaxKind.TrueKeyword;
|
||||
break;
|
||||
|
|
@ -227,10 +237,16 @@ async function parseFile(fileName: string) {
|
|||
}
|
||||
|
||||
data.filePath = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "");
|
||||
|
||||
data.dirName = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "")
|
||||
.replace(/^src\/plugins\//, "");
|
||||
.replace(/^src\/plugins\//, "")
|
||||
.replace(/^src\/equicordplugins\//, "");
|
||||
|
||||
return [data] as const;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
/// <reference types="../src/modules" />
|
||||
|
||||
import { createHmac } from "crypto";
|
||||
import { readFileSync } from "fs";
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import pup, { JSHandle } from "puppeteer-core";
|
||||
|
||||
const logStderr = (...data: any[]) => console.error(`${CANARY ? "CANARY" : "STABLE"} ---`, ...data);
|
||||
|
|
|
|||
297
scripts/generateVencordPluginList.ts
Normal file
297
scripts/generateVencordPluginList.ts
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* 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, sep } from "path";
|
||||
import { normalize as posixNormalize, sep as posixSep } from "path/posix";
|
||||
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, PropertyAssignment, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||
|
||||
import { getPluginTarget } from "./utils.mjs";
|
||||
|
||||
interface Dev {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface Command {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface PluginData {
|
||||
name: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
authors: Dev[];
|
||||
dependencies: string[];
|
||||
hasPatches: boolean;
|
||||
hasCommands: boolean;
|
||||
commands: Command[];
|
||||
required: boolean;
|
||||
enabledByDefault: boolean;
|
||||
target: "discordDesktop" | "vesktop" | "equibop" | "desktop" | "web" | "dev";
|
||||
filePath: string;
|
||||
dirName: string;
|
||||
isModified: boolean;
|
||||
}
|
||||
const devs = {} as Record<string, Dev>;
|
||||
const equicordDevs = {} 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 (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse devs: not an object literal");
|
||||
|
||||
for (const prop of value.expression.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");
|
||||
}
|
||||
|
||||
function parseEquicordDevs() {
|
||||
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, "EquicordDevs"));
|
||||
if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue;
|
||||
|
||||
const value = devsDeclaration.initializer.arguments[0];
|
||||
|
||||
if (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse EquicordDevs: not an object literal");
|
||||
|
||||
for (const prop of value.expression.properties) {
|
||||
const name = (prop.name as Identifier).text;
|
||||
const value = isPropertyAssignment(prop) ? prop.initializer : prop;
|
||||
|
||||
if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse EquicordDevs: ${name} is not an object literal`);
|
||||
|
||||
equicordDevs[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 EquicordDevs 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,
|
||||
isModified: false,
|
||||
tags: [] as string[]
|
||||
} 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;
|
||||
if (isArrayLiteralExpression(value)) {
|
||||
data.commands = value.elements.map((e) => {
|
||||
if (isObjectLiteralExpression(e)) {
|
||||
const nameProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "name"
|
||||
);
|
||||
const descriptionProperty = e.properties.find((p): p is PropertyAssignment =>
|
||||
isPropertyAssignment(p) && isIdentifier(p.name) && p.name.escapedText === "description"
|
||||
);
|
||||
if (!nameProperty || !descriptionProperty) throw fail("command missing required properties");
|
||||
const name = isStringLiteral(nameProperty.initializer) ? nameProperty.initializer.text : "";
|
||||
const description = isStringLiteral(descriptionProperty.initializer) ? descriptionProperty.initializer.text : "";
|
||||
return { name, description };
|
||||
} else if (isCallExpression(e) && isIdentifier(e.expression)) {
|
||||
const [nameArg] = e.arguments;
|
||||
if (!isStringLiteral(nameArg)) throw fail("first argument must be a string");
|
||||
return { name: nameArg.text, description: "" };
|
||||
} else if (e.kind === SyntaxKind.SpreadElement) {
|
||||
return undefined;
|
||||
}
|
||||
throw fail("commands array contains invalid elements");
|
||||
}).filter((c): c is { name: string; description: string; } => Boolean(c)) as Command[];
|
||||
} else if (isIdentifier(value)) {
|
||||
data.commands = [];
|
||||
} else {
|
||||
throw fail("commands is not an array literal or identifier");
|
||||
}
|
||||
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");
|
||||
const d = devs[getName(e)!] || equicordDevs[getName(e)!];
|
||||
if (!d) throw fail(`couldn't look up author ${getName(e)}`);
|
||||
return d;
|
||||
});
|
||||
break;
|
||||
case "tags":
|
||||
if (!isArrayLiteralExpression(value)) throw fail("tags is not an array literal");
|
||||
data.tags = value.elements.map(e => {
|
||||
if (!isStringLiteral(e)) throw fail("tags array contains non-string literals");
|
||||
return e.text;
|
||||
});
|
||||
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 "isModified":
|
||||
case "enabledByDefault":
|
||||
data[key] = value.kind === SyntaxKind.TrueKeyword;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing");
|
||||
|
||||
const target = getPluginTarget(fileName);
|
||||
if (target) {
|
||||
if (!["web", "discordDesktop", "vesktop", "equibop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`);
|
||||
data.target = target as any;
|
||||
}
|
||||
|
||||
data.filePath = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "");
|
||||
|
||||
data.dirName = posixNormalize(fileName)
|
||||
.split(sep)
|
||||
.join(posixSep)
|
||||
.replace(/\/index\.([jt]sx?)$/, "")
|
||||
.replace(/^src\/plugins\//, "");
|
||||
|
||||
return [data] as const;
|
||||
}
|
||||
|
||||
throw fail("no default export called 'definePlugin' found");
|
||||
}
|
||||
|
||||
async function getEntryPoint(dir: string, dirent: Dirent) {
|
||||
const base = join(dir, 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`);
|
||||
}
|
||||
|
||||
function isPluginFile({ name }: { name: string; }) {
|
||||
if (name === "index.ts") return false;
|
||||
return !name.startsWith("_") && !name.startsWith(".");
|
||||
}
|
||||
|
||||
(async () => {
|
||||
parseDevs();
|
||||
parseEquicordDevs();
|
||||
|
||||
const plugins = [] as PluginData[];
|
||||
|
||||
await Promise.all(["src/plugins", "src/plugins/_core"].flatMap(dir =>
|
||||
readdirSync(dir, { withFileTypes: true })
|
||||
.filter(isPluginFile)
|
||||
.map(async dirent => {
|
||||
const [data] = await parseFile(await getEntryPoint(dir, dirent));
|
||||
plugins.sort().push(data);
|
||||
})
|
||||
));
|
||||
|
||||
const data = JSON.stringify(plugins);
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
writeFileSync(process.argv[2], data);
|
||||
} else {
|
||||
console.log(data);
|
||||
}
|
||||
})();
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// DO NOT REMOVE UNLESS YOU WISH TO FACE THE WRATH OF THE CIRCULAR DEPENDENCY DEMON!!!!!!!
|
||||
import "~plugins";
|
||||
|
||||
export * as Api from "./api";
|
||||
export * as Components from "./components";
|
||||
export * as Plugins from "./plugins";
|
||||
|
|
@ -29,7 +32,10 @@ export { PlainSettings, Settings };
|
|||
import "./utils/quickCss";
|
||||
import "./webpack/patchWebpack";
|
||||
|
||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||
import { addVencordUiStyles } from "@components/css";
|
||||
import { openUpdaterModal } from "@components/settings/tabs/updater";
|
||||
import { IS_WINDOWS } from "@utils/constants";
|
||||
import { createAndAppendStyle } from "@utils/css";
|
||||
import { StartAt } from "@utils/types";
|
||||
|
||||
import { get as dsGet } from "./api/DataStore";
|
||||
|
|
@ -45,7 +51,6 @@ import { SettingsRouter } from "./webpack/common";
|
|||
|
||||
if (IS_REPORTER) {
|
||||
require("./debug/runReporter");
|
||||
Settings.plugins.CharacterCounter.enabled = false;
|
||||
}
|
||||
|
||||
async function syncSettings() {
|
||||
|
|
@ -56,7 +61,7 @@ async function syncSettings() {
|
|||
// User switched to an account that isn't connected to cloud
|
||||
showNotification({
|
||||
title: "Cloud Settings",
|
||||
body: "Cloud sync was disabled because this account isn't connected to the Vencloud App. You can enable it again by connecting this account in Cloud Settings. (note: it will store your preferences separately)",
|
||||
body: "Cloud sync was disabled because this account isn't connected to the cloud App. You can enable it again by connecting this account in Cloud Settings. (note: it will store your preferences separately)",
|
||||
color: "var(--yellow-360)",
|
||||
onClick: () => SettingsRouter.open("VencordCloud")
|
||||
});
|
||||
|
|
@ -151,7 +156,7 @@ async function init() {
|
|||
|
||||
syncSettings();
|
||||
|
||||
if (!IS_WEB && !IS_UPDATER_DISABLED) {
|
||||
if (!IS_DEV && !IS_WEB && !IS_UPDATER_DISABLED) {
|
||||
runUpdateCheck();
|
||||
|
||||
// this tends to get really annoying, so only do this if the user has auto-update without notification enabled
|
||||
|
|
@ -178,11 +183,12 @@ startAllPlugins(StartAt.Init);
|
|||
init();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
addVencordUiStyles();
|
||||
|
||||
startAllPlugins(StartAt.DOMContentLoaded);
|
||||
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
||||
document.head.append(Object.assign(document.createElement("style"), {
|
||||
id: "vencord-native-titlebar-style",
|
||||
textContent: "[class*=titleBar]{display: none!important}"
|
||||
}));
|
||||
|
||||
// FIXME
|
||||
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && IS_WINDOWS) {
|
||||
createAndAppendStyle("vencord-native-titlebar-style").textContent = "[class*=titleBar]{display: none!important}";
|
||||
}
|
||||
}, { once: true });
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import type { Settings } from "@api/Settings";
|
||||
import { CspRequestResult } from "@main/csp/manager";
|
||||
import { PluginIpcMappings } from "@main/ipcPlugins";
|
||||
import { IpcEvents } from "@shared/IpcEvents";
|
||||
import { IpcRes } from "@utils/types";
|
||||
|
|
@ -36,6 +37,8 @@ export default {
|
|||
getThemesList: () => invoke<Array<{ fileName: string; content: string; }>>(IpcEvents.GET_THEMES_LIST),
|
||||
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
|
||||
getSystemValues: () => invoke<Record<string, string>>(IpcEvents.GET_THEME_SYSTEM_VALUES),
|
||||
|
||||
openFolder: () => invoke<void>(IpcEvents.OPEN_THEMES_FOLDER),
|
||||
},
|
||||
|
||||
updater: {
|
||||
|
|
@ -49,6 +52,8 @@ export default {
|
|||
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
||||
set: (settings: Settings, pathToNotify?: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings, pathToNotify),
|
||||
getSettingsDir: () => invoke<string>(IpcEvents.GET_SETTINGS_DIR),
|
||||
|
||||
openFolder: () => invoke<void>(IpcEvents.OPEN_SETTINGS_FOLDER),
|
||||
},
|
||||
|
||||
quickCss: {
|
||||
|
|
@ -72,5 +77,17 @@ export default {
|
|||
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
|
||||
},
|
||||
|
||||
csp: {
|
||||
/**
|
||||
* Note: Only supports full explicit matches, not wildcards.
|
||||
*
|
||||
* If `*.example.com` is allowed, `isDomainAllowed("https://sub.example.com")` will return false.
|
||||
*/
|
||||
isDomainAllowed: (url: string, directives: string[]) => invoke<boolean>(IpcEvents.CSP_IS_DOMAIN_ALLOWED, url, directives),
|
||||
removeOverride: (url: string) => invoke<boolean>(IpcEvents.CSP_REMOVE_OVERRIDE, url),
|
||||
requestAddOverride: (url: string, directives: string[], callerName: string) =>
|
||||
invoke<CspRequestResult>(IpcEvents.CSP_REQUEST_ADD_OVERRIDE, url, directives, callerName),
|
||||
},
|
||||
|
||||
pluginHelpers: PluginHelpers
|
||||
};
|
||||
|
|
|
|||
260
src/api/AudioPlayer.ts
Normal file
260
src/api/AudioPlayer.ts
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByCodeLazy, findLazy } from "webpack";
|
||||
|
||||
let defaultSounds: null | string[] = null;
|
||||
const findDefaultSounds = findLazy(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")), false);
|
||||
const AudioPlayerConstructor = findByCodeLazy("could not play audio");
|
||||
|
||||
export type AudioProcessor = (data: PreprocessAudioData) => void;
|
||||
export type AudioCallback = (() => void);
|
||||
export type AudioErrorHandler = ((error: Error) => void);
|
||||
export const audioProcessorFunctions: Record<string, AudioProcessor> = {};
|
||||
|
||||
export enum AudioType {
|
||||
/** An external URL that follows the Content Security Policy. */
|
||||
URL = "url",
|
||||
/** A base64-encoded data URI. */
|
||||
DATA = "data-uri",
|
||||
/** A Blob URI. */
|
||||
BLOB = "blob",
|
||||
/** A file path. */
|
||||
PATH = "file-path",
|
||||
/** An internal Discord audio filename (e.g. "discodo"). */
|
||||
DISCORD = "discord",
|
||||
/** Any other unrecognized audio type. */
|
||||
OTHER = "other"
|
||||
}
|
||||
|
||||
export interface PreprocessAudioData {
|
||||
/** The original audio string passed to the player. */
|
||||
audio: string;
|
||||
/** The read-only type of audio of the original audio string. */
|
||||
readonly type: AudioType;
|
||||
/** The volume of the original audio between 0 and 100. */
|
||||
volume: number;
|
||||
/** The playback speed of the original audio between 0.0625 and 16. */
|
||||
speed: number;
|
||||
}
|
||||
|
||||
export interface AudioPlayerInternal {
|
||||
preprocessDataOriginal: PreprocessAudioData;
|
||||
preprocessDataPrevious: PreprocessAudioData | null;
|
||||
preprocessDataCurrent: PreprocessAudioData;
|
||||
audio: string;
|
||||
_audio: null | Promise<HTMLAudioElement>;
|
||||
_volume: number;
|
||||
_speed: number;
|
||||
outputChannel: string;
|
||||
type: AudioType;
|
||||
preload: boolean;
|
||||
persistent: boolean;
|
||||
onEnded?: AudioCallback;
|
||||
onError?: AudioErrorHandler;
|
||||
processAudio: () => void;
|
||||
ensureAudio(): Promise<HTMLAudioElement>;
|
||||
destroyAudio(): void;
|
||||
loop(): void;
|
||||
play(): void;
|
||||
pause(): void;
|
||||
stop(restart?: boolean): void;
|
||||
}
|
||||
|
||||
export interface AudioPlayerInterface {
|
||||
/** The internal Discord audio filename (e.g. "discodo"), a data URI, or an external URL that follows the CSP. */
|
||||
audio: string;
|
||||
/** The read-only type of audio determined during processing. */
|
||||
readonly type: AudioType;
|
||||
/** The duration of the audio in seconds, or null if not yet loaded. */
|
||||
readonly duration: Promise<number> | null;
|
||||
/** The current time of the audio in seconds, or null if not yet loaded. */
|
||||
time: Promise<number> | null;
|
||||
/** The paused state of the audio, or null if not yet loaded. */
|
||||
paused: Promise<boolean> | null;
|
||||
/** The muted state of the audio, or null if not yet loaded. */
|
||||
muted: Promise<boolean> | null;
|
||||
/** The volume of the audio between 0 and 100. */
|
||||
volume: number;
|
||||
/** The playback speed of the audio between 0.0625 and 16. */
|
||||
speed: number;
|
||||
/** Whether to load the audio immediately. If persistent is false, this will only apply until the first playback. */
|
||||
preload: boolean;
|
||||
/** Whether the audio element is persistent and not recreated for every playback. */
|
||||
persistent: boolean;
|
||||
/** Preloads the audio before playback. Automatically called when persistent is true. */
|
||||
load(): void;
|
||||
/** Sets the audio to loop until paused or stopped. */
|
||||
loop(): void;
|
||||
/** Plays the audio. */
|
||||
play(): void;
|
||||
/** Pauses the audio. */
|
||||
pause(): void;
|
||||
/** Stops the audio. */
|
||||
stop(): void;
|
||||
/** Plays the audio from the beginning. */
|
||||
restart(): void;
|
||||
/** Seeks to a specific time in seconds. */
|
||||
seek(time: number): void;
|
||||
/** Mutes the audio. */
|
||||
mute(): void;
|
||||
/** Unmutes the audio. */
|
||||
unmute(): void;
|
||||
/** Deletes the audio element. Necessary if persistent is true. */
|
||||
delete(): void;
|
||||
}
|
||||
|
||||
export interface AudioPlayerOptions {
|
||||
/** The volume of the audio, between 0 and 100, defaulting to 100. */
|
||||
volume?: number;
|
||||
/** The playback speed of the audio, between 0.0625 and 16, defaulting to 1. */
|
||||
speed?: number;
|
||||
/** Whether to preload the audio as soon as the player is created. */
|
||||
preload?: boolean;
|
||||
/** Whether the audio element is persistent and not recreated for every playback. If persistent, you must call delete() to free the memory. Defaults to false. */
|
||||
persistent?: boolean;
|
||||
/** An optional callback that is called every time the audio finishes playing. */
|
||||
onEnded?: AudioCallback;
|
||||
/** An optional error handler that is called when an error occurs during audio playback. */
|
||||
onError?: AudioErrorHandler;
|
||||
}
|
||||
|
||||
// Wrap the player to allow reprocessing the audio when properties are changed and to alleviate
|
||||
// the confusion between the public API accepting 0-100 volume while the internal API uses 0-1 volume.
|
||||
class AudioPlayerWrapper implements AudioPlayerInterface {
|
||||
private internalPlayer: AudioPlayerInternal;
|
||||
constructor(internalPlayer: AudioPlayerInternal) { this.internalPlayer = internalPlayer; }
|
||||
|
||||
get audio(): string { return this.internalPlayer.audio; }
|
||||
set audio(value: string) { this.internalPlayer.preprocessDataOriginal.audio = value; this.internalPlayer.processAudio(); }
|
||||
|
||||
get volume(): number { return this.internalPlayer._volume * 100; }
|
||||
set volume(value: number) { this.internalPlayer.preprocessDataOriginal.volume = Math.max(0, Math.min(1, value / 100)); this.internalPlayer.processAudio(); }
|
||||
|
||||
get speed(): number { return this.internalPlayer._speed; }
|
||||
set speed(value: number) { this.internalPlayer.preprocessDataOriginal.speed = Math.max(0.0625, Math.min(16, value)); this.internalPlayer.processAudio(); }
|
||||
|
||||
get time(): Promise<number> | null { return this.internalPlayer._audio?.then(audio => audio.currentTime) ?? null; }
|
||||
set time(value: number) { this.internalPlayer.ensureAudio().then(audio => audio.currentTime = value); }
|
||||
|
||||
get persistent(): boolean { return this.internalPlayer.persistent; }
|
||||
set persistent(value: boolean) { this.internalPlayer.persistent = value; }
|
||||
|
||||
get preload(): boolean { return this.internalPlayer.preload; }
|
||||
set preload(value: boolean) { this.internalPlayer.preload = value; value && this.internalPlayer.ensureAudio(); }
|
||||
|
||||
get muted(): Promise<boolean> | null { return this.internalPlayer._audio?.then(audio => audio.muted) ?? null; }
|
||||
set muted(value: boolean) { this.internalPlayer.ensureAudio().then(audio => audio.muted = value); }
|
||||
|
||||
get paused(): Promise<boolean> | null { return this.internalPlayer._audio?.then(audio => audio.paused) ?? null; }
|
||||
set paused(value: boolean) { value ? this.internalPlayer.pause() : this.internalPlayer.play(); }
|
||||
|
||||
get type(): AudioType { return this.internalPlayer.type; }
|
||||
get duration(): Promise<number> | null { return this.internalPlayer._audio?.then(audio => audio.duration) ?? null; }
|
||||
|
||||
load(): void { this.internalPlayer.ensureAudio(); }
|
||||
loop(): void { this.internalPlayer.loop(); }
|
||||
play(): void { this.internalPlayer.play(); }
|
||||
pause(): void { this.internalPlayer.pause(); }
|
||||
stop(restart?: boolean): void { this.internalPlayer.stop(restart); }
|
||||
restart(): void { this.internalPlayer.stop(true); }
|
||||
seek(time: number): void { this.internalPlayer.ensureAudio().then(audio => audio.currentTime = time); }
|
||||
mute(): void { this.internalPlayer.ensureAudio().then(audio => audio.muted = true); }
|
||||
unmute(): void { this.internalPlayer.ensureAudio().then(audio => audio.muted = false); }
|
||||
delete(): void { this.internalPlayer.destroyAudio(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an audio player.
|
||||
* @param audio The internal Discord audio filename (e.g. "discodo"), a data URI, or an external URL that follows the CSP.
|
||||
* @param options Additional options for the audio player.
|
||||
* @param options.volume The volume of the audio, between 0 and 100, defaulting to 100.
|
||||
* @param options.speed The playback speed of the audio, between 0.0625 and 16, defaulting to 1.
|
||||
* @param options.preload Whether to load the audio immediately. If persistent is false, this will only apply until the first playback.
|
||||
* @param options.persistent Whether the audio element is persistent and not recreated for every playback. If persistent, you must call delete() to free the memory. Defaults to false.
|
||||
* @param options.onEnded An optional callback that is called every time the audio finishes playing.
|
||||
* @param options.onError An optional error handler that is passed an Error object when an error occurs during audio playback.
|
||||
* @return The created audio player.
|
||||
*/
|
||||
export function createAudioPlayer(
|
||||
audio: string,
|
||||
options: AudioPlayerOptions = {}
|
||||
): AudioPlayerInterface {
|
||||
const internalPlayer: AudioPlayerInternal = new AudioPlayerConstructor(
|
||||
audio,
|
||||
null,
|
||||
null,
|
||||
"default",
|
||||
options
|
||||
);
|
||||
|
||||
return new AudioPlayerWrapper(internalPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an audio instantly and returns the player.
|
||||
* @param audio The internal Discord audio filename (e.g. "discodo"), a data URI, or an external URL that follows the CSP.
|
||||
* @param options Additional options for the audio player.
|
||||
* @param options.volume The volume of the audio, between 0 and 100, defaulting to 100.
|
||||
* @param options.speed The playback speed of the audio, between 0.0625 and 16, defaulting to 1.
|
||||
* @param options.preload Whether to load the audio immediately. If persistent is false, this will only apply until the first playback.
|
||||
* @param options.persistent Whether the audio element is persistent and not recreated for every playback. If persistent, you must call delete() to free the memory. Defaults to false.
|
||||
* @param options.onEnded An optional callback that is called every time the audio finishes playing.
|
||||
* @param options.onError An optional error handler that is passed an Error object when an error occurs during audio playback.
|
||||
* @return The created audio player.
|
||||
*/
|
||||
export function playAudio(audio: string, options: AudioPlayerOptions = {}): AudioPlayerInterface {
|
||||
const player = createAudioPlayer(audio, options);
|
||||
player.play();
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the type of audio based on its string.
|
||||
* @param audio The audio string to identify.
|
||||
* @returns The identified AudioType.
|
||||
*/
|
||||
export function identifyAudioType(audio: string): AudioType {
|
||||
if (defaultAudioNames().includes(audio)) return AudioType.DISCORD;
|
||||
|
||||
try {
|
||||
const url = new URL(audio);
|
||||
if (url.protocol === "http:" || url.protocol === "https:") return AudioType.URL;
|
||||
if (url.protocol === "data:") return AudioType.DATA;
|
||||
if (url.protocol === "blob:") return AudioType.BLOB;
|
||||
if (url.protocol === "file:") return AudioType.PATH;
|
||||
return AudioType.OTHER;
|
||||
} catch {
|
||||
return AudioType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function to process an audio before it is played.
|
||||
* @param key A unique identifier for this audio processor. Plugin name is recommended.
|
||||
* @param processor A function that takes an audio, volume (0-100), and type (AudioType), and returns the replacement audio and volume.
|
||||
*/
|
||||
export function addAudioProcessor(key: string, processor: AudioProcessor): void {
|
||||
audioProcessorFunctions[key] = processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an audio processor by its key.
|
||||
* @param key The unique identifier of the audio processor to remove.
|
||||
*/
|
||||
export function removeAudioProcessor(key: string): void {
|
||||
delete audioProcessorFunctions[key];
|
||||
}
|
||||
|
||||
/** Returns an array of all internal Discord audio filenames. */
|
||||
export function defaultAudioNames(): string[] {
|
||||
defaultSounds ??= (findDefaultSounds.keys() || []).map(key => {
|
||||
const match = key.match(/((?:\w|-)+)\.mp3$/);
|
||||
return match ? match[1] : null;
|
||||
}).filter(Boolean) as string[];
|
||||
|
||||
return defaultSounds;
|
||||
}
|
||||
|
|
@ -17,10 +17,9 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import BadgeAPIPlugin from "plugins/_api/badges";
|
||||
import { ComponentType, HTMLProps } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
export const enum BadgePosition {
|
||||
START,
|
||||
END
|
||||
|
|
@ -35,7 +34,9 @@ export interface ProfileBadge {
|
|||
image?: string;
|
||||
link?: string;
|
||||
/** Action to perform when you click the badge */
|
||||
onClick?(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, props: BadgeUserArgs): void;
|
||||
onClick?(event: React.MouseEvent, props: ProfileBadge & BadgeUserArgs): void;
|
||||
/** Action to perform when you right click the badge */
|
||||
onContextMenu?(event: React.MouseEvent, props: BadgeUserArgs & BadgeUserArgs): void;
|
||||
/** Should the user display this badge? */
|
||||
shouldShow?(userInfo: BadgeUserArgs): boolean;
|
||||
/** Optional props (e.g. style) for the badge, ignored for component badges */
|
||||
|
|
@ -77,23 +78,44 @@ export function removeProfileBadge(badge: ProfileBadge) {
|
|||
export function _getBadges(args: BadgeUserArgs) {
|
||||
const badges = [] as ProfileBadge[];
|
||||
for (const badge of Badges) {
|
||||
if (!badge.shouldShow || badge.shouldShow(args)) {
|
||||
const b = badge.getBadges
|
||||
? badge.getBadges(args).map(b => {
|
||||
b.component &&= ErrorBoundary.wrap(b.component, { noop: true });
|
||||
return b;
|
||||
})
|
||||
: [{ ...badge, ...args }];
|
||||
if (badge.shouldShow && !badge.shouldShow(args)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
badge.position === BadgePosition.START
|
||||
? badges.unshift(...b)
|
||||
: badges.push(...b);
|
||||
const b = badge.getBadges
|
||||
? badge.getBadges(args).map(badge => ({
|
||||
...args,
|
||||
...badge,
|
||||
component: badge.component && ErrorBoundary.wrap(badge.component, { noop: true })
|
||||
}))
|
||||
: [{ ...args, ...badge }];
|
||||
|
||||
if (badge.position === BadgePosition.START) {
|
||||
badges.unshift(...b);
|
||||
} else {
|
||||
badges.push(...b);
|
||||
}
|
||||
}
|
||||
const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId);
|
||||
const equicordDonorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getEquicordDonorBadges(args.userId);
|
||||
if (donorBadges) badges.unshift(...donorBadges);
|
||||
if (equicordDonorBadges) badges.unshift(...equicordDonorBadges);
|
||||
|
||||
const donorBadges = BadgeAPIPlugin.getDonorBadges(args.userId);
|
||||
const equicordDonorBadges = BadgeAPIPlugin.getEquicordDonorBadges(args.userId);
|
||||
if (donorBadges) {
|
||||
badges.unshift(
|
||||
...donorBadges.map(badge => ({
|
||||
...args,
|
||||
...badge,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
if (equicordDonorBadges) {
|
||||
badges.unshift(
|
||||
...equicordDonorBadges.map(badge => ({
|
||||
...args,
|
||||
...badge,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { classes } from "@utils/misc";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { waitFor } from "@webpack";
|
||||
import { Button, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { ButtonWrapperClasses, Clickable, Tooltip } from "@webpack/common";
|
||||
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
|
|
@ -97,32 +98,30 @@ export const removeChatBarButton = (id: string) => buttonFactories.delete(id);
|
|||
export interface ChatBarButtonProps {
|
||||
children: ReactNode;
|
||||
tooltip: string;
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
onContextMenu?: MouseEventHandler<HTMLButtonElement>;
|
||||
onAuxClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
buttonProps?: Omit<HTMLProps<HTMLButtonElement>, "size" | "onClick" | "onContextMenu" | "onAuxClick">;
|
||||
onClick: MouseEventHandler;
|
||||
onContextMenu?: MouseEventHandler;
|
||||
onAuxClick?: MouseEventHandler;
|
||||
buttonProps?: Omit<HTMLProps<HTMLDivElement>, "size" | "onClick" | "onContextMenu" | "onAuxClick">;
|
||||
}
|
||||
export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => {
|
||||
return (
|
||||
<Tooltip text={props.tooltip}>
|
||||
{({ onMouseEnter, onMouseLeave }) => (
|
||||
<div className={`expression-picker-chat-input-button ${ChannelTextAreaClasses?.buttonContainer ?? ""} vc-chatbar-button`}>
|
||||
<Button
|
||||
<Clickable
|
||||
aria-label={props.tooltip}
|
||||
size=""
|
||||
look={Button.Looks.BLANK}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
innerClassName={`${ButtonWrapperClasses.button} ${ChannelTextAreaClasses?.button}`}
|
||||
className={classes(ButtonWrapperClasses?.button, ChannelTextAreaClasses?.button)}
|
||||
onClick={props.onClick}
|
||||
onContextMenu={props.onContextMenu}
|
||||
onAuxClick={props.onAuxClick}
|
||||
{...props.buttonProps}
|
||||
>
|
||||
<div className={ButtonWrapperClasses.buttonWrapper}>
|
||||
<div className={ButtonWrapperClasses?.buttonWrapper}>
|
||||
{props.children}
|
||||
</div>
|
||||
</Button>
|
||||
</Clickable>
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -17,13 +17,11 @@
|
|||
*/
|
||||
|
||||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { CommandArgument, Message } from "@vencord/discord-types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
|
||||
export function generateId() {
|
||||
|
|
@ -51,8 +49,8 @@ export function sendBotMessage(channelId: string, message: PartialDeep<Message>)
|
|||
* @param fallbackValue Fallback value in case this option wasn't passed
|
||||
* @returns Value
|
||||
*/
|
||||
export function findOption<T>(args: Argument[], name: string): T & {} | undefined;
|
||||
export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {};
|
||||
export function findOption(args: Argument[], name: string, fallbackValue?: any) {
|
||||
export function findOption<T>(args: CommandArgument[], name: string): T & {} | undefined;
|
||||
export function findOption<T>(args: CommandArgument[], name: string, fallbackValue: T): T & {};
|
||||
export function findOption(args: CommandArgument[], name: string, fallbackValue?: any) {
|
||||
return (args.find(a => a.name === name)?.value ?? fallbackValue) as any;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,38 +18,39 @@
|
|||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
import { CommandArgument, CommandContext, CommandOption } from "@vencord/discord-types";
|
||||
|
||||
import { sendBotMessage } from "./commandHelpers";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, VencordCommand } from "./types";
|
||||
|
||||
export * from "./commandHelpers";
|
||||
export * from "./types";
|
||||
|
||||
export let BUILT_IN: Command[];
|
||||
export const commands = {} as Record<string, Command>;
|
||||
export let BUILT_IN: VencordCommand[];
|
||||
export const commands = {} as Record<string, VencordCommand>;
|
||||
|
||||
// hack for plugins being evaluated before we can grab these from webpack
|
||||
const OptPlaceholder = Symbol("OptionalMessageOption") as any as Option;
|
||||
const ReqPlaceholder = Symbol("RequiredMessageOption") as any as Option;
|
||||
const OptPlaceholder = Symbol("OptionalMessageOption") as any as CommandOption;
|
||||
const ReqPlaceholder = Symbol("RequiredMessageOption") as any as CommandOption;
|
||||
|
||||
/**
|
||||
* Optional message option named "message" you can use in commands.
|
||||
* Used in "tableflip" or "shrug"
|
||||
* @see {@link RequiredMessageOption}
|
||||
*/
|
||||
export let OptionalMessageOption: Option = OptPlaceholder;
|
||||
export let OptionalMessageOption: CommandOption = OptPlaceholder;
|
||||
/**
|
||||
* Required message option named "message" you can use in commands.
|
||||
* Used in "me"
|
||||
* @see {@link OptionalMessageOption}
|
||||
*/
|
||||
export let RequiredMessageOption: Option = ReqPlaceholder;
|
||||
export let RequiredMessageOption: CommandOption = ReqPlaceholder;
|
||||
|
||||
// Discord's command list has random gaps for some reason, which can cause issues while rendering the commands
|
||||
// Add this offset to every added command to keep them unique
|
||||
let commandIdOffset: number;
|
||||
|
||||
export const _init = function (cmds: Command[]) {
|
||||
export const _init = function (cmds: VencordCommand[]) {
|
||||
try {
|
||||
BUILT_IN = cmds;
|
||||
OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0];
|
||||
|
|
@ -61,7 +62,7 @@ export const _init = function (cmds: Command[]) {
|
|||
return cmds;
|
||||
} as never;
|
||||
|
||||
export const _handleCommand = function (cmd: Command, args: Argument[], ctx: CommandContext) {
|
||||
export const _handleCommand = function (cmd: VencordCommand, args: CommandArgument[], ctx: CommandContext) {
|
||||
if (!cmd.isVencordCommand)
|
||||
return cmd.execute(args, ctx);
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ export const _handleCommand = function (cmd: Command, args: Argument[], ctx: Com
|
|||
* Prepare a Command Option for Discord by filling missing fields
|
||||
* @param opt
|
||||
*/
|
||||
export function prepareOption<O extends Option | Command>(opt: O): O {
|
||||
export function prepareOption<O extends CommandOption | VencordCommand>(opt: O): O {
|
||||
opt.displayName ||= opt.name;
|
||||
opt.displayDescription ||= opt.description;
|
||||
opt.options?.forEach((opt, i, opts) => {
|
||||
|
|
@ -109,7 +110,7 @@ export function prepareOption<O extends Option | Command>(opt: O): O {
|
|||
// Yes, Discord registers individual commands for each subcommand
|
||||
// TODO: This probably doesn't support nested subcommands. If that is ever needed,
|
||||
// investigate
|
||||
function registerSubCommands(cmd: Command, plugin: string) {
|
||||
function registerSubCommands(cmd: VencordCommand, plugin: string) {
|
||||
cmd.options?.forEach(o => {
|
||||
if (o.type !== ApplicationCommandOptionType.SUB_COMMAND)
|
||||
throw new Error("When specifying sub-command options, all options must be sub-commands.");
|
||||
|
|
@ -132,7 +133,7 @@ function registerSubCommands(cmd: Command, plugin: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function registerCommand<C extends Command>(command: C, plugin: string) {
|
||||
export function registerCommand<C extends VencordCommand>(command: C, plugin: string) {
|
||||
if (!BUILT_IN) {
|
||||
console.warn(
|
||||
"[CommandsAPI]",
|
||||
|
|
|
|||
|
|
@ -1,106 +1,12 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 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/>.
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Channel, Guild } from "discord-types/general";
|
||||
import { Promisable } from "type-fest";
|
||||
import { Command } from "@vencord/discord-types";
|
||||
export { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "@vencord/discord-types/enums";
|
||||
|
||||
export interface CommandContext {
|
||||
channel: Channel;
|
||||
guild?: Guild;
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
MENTIONABLE = 9,
|
||||
NUMBER = 10,
|
||||
ATTACHMENT = 11,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandInputType {
|
||||
BUILT_IN = 0,
|
||||
BUILT_IN_TEXT = 1,
|
||||
BUILT_IN_INTEGRATION = 2,
|
||||
BOT = 3,
|
||||
PLACEHOLDER = 4,
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
type: ApplicationCommandOptionType;
|
||||
description: string;
|
||||
displayDescription?: string;
|
||||
required?: boolean;
|
||||
options?: Option[];
|
||||
choices?: Array<ChoicesOption>;
|
||||
}
|
||||
|
||||
export interface ChoicesOption {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandType {
|
||||
CHAT_INPUT = 1,
|
||||
USER = 2,
|
||||
MESSAGE = 3,
|
||||
}
|
||||
|
||||
export interface CommandReturnValue {
|
||||
content: string;
|
||||
/** TODO: implement */
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface Argument {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
value: string;
|
||||
focused: undefined;
|
||||
options: Argument[];
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
id?: string;
|
||||
applicationId?: string;
|
||||
type?: ApplicationCommandType;
|
||||
inputType?: ApplicationCommandInputType;
|
||||
plugin?: string;
|
||||
export interface VencordCommand extends Command {
|
||||
isVencordCommand?: boolean;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: Option[];
|
||||
predicate?(ctx: CommandContext): boolean;
|
||||
|
||||
execute(args: Argument[], ctx: CommandContext): Promisable<void | CommandReturnValue>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export function promisifyRequest<T = undefined>(
|
|||
request: IDBRequest<T> | IDBTransaction,
|
||||
): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// @ts-ignore - file size hacks
|
||||
// @ts-expect-error - file size hacks
|
||||
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
||||
// @ts-ignore - file size hacks
|
||||
// @ts-expect-error - file size hacks
|
||||
request.onabort = request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Channel, User } from "discord-types/general/index.js";
|
||||
import { Channel, User } from "@vencord/discord-types";
|
||||
import { JSX } from "react";
|
||||
|
||||
interface DecoratorProps {
|
||||
export type DecoratorProps = {
|
||||
type: "guild" | "dm";
|
||||
user: User;
|
||||
/** only present when this is a DM list item */
|
||||
channel: Channel;
|
||||
/** only present when this is a guild list item */
|
||||
isOwner: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type MemberListDecoratorFactory = (props: DecoratorProps) => JSX.Element | null;
|
||||
type OnlyIn = "guilds" | "dms";
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Channel, Message } from "discord-types/general/index.js";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import { JSX } from "react";
|
||||
|
||||
export interface MessageDecorationProps {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@
|
|||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import type { Channel, CloudUpload, CustomEmoji, Message } from "@vencord/discord-types";
|
||||
import { MessageStore } from "@webpack/common";
|
||||
import { CustomEmoji } from "@webpack/types";
|
||||
import type { Channel, Message } from "discord-types/general";
|
||||
import type { Promisable } from "type-fest";
|
||||
|
||||
const MessageEventsLogger = new Logger("MessageEvents", "#e5c890");
|
||||
|
|
@ -31,30 +30,6 @@ export interface MessageObject {
|
|||
tts: boolean;
|
||||
}
|
||||
|
||||
export interface Upload {
|
||||
classification: string;
|
||||
currentSize: number;
|
||||
description: string | null;
|
||||
filename: string;
|
||||
id: string;
|
||||
isImage: boolean;
|
||||
isVideo: boolean;
|
||||
item: {
|
||||
file: File;
|
||||
platform: number;
|
||||
};
|
||||
loaded: number;
|
||||
mimeType: string;
|
||||
preCompressionSize: number;
|
||||
responseUrl: string;
|
||||
sensitive: boolean;
|
||||
showLargeMessageDialog: boolean;
|
||||
spoiler: boolean;
|
||||
status: "NOT_STARTED" | "STARTED" | "UPLOADING" | "ERROR" | "COMPLETED" | "CANCELLED";
|
||||
uniqueId: string;
|
||||
uploadedFilename: string;
|
||||
}
|
||||
|
||||
export interface MessageReplyOptions {
|
||||
messageReference: Message["messageReference"];
|
||||
allowedMentions?: {
|
||||
|
|
@ -65,9 +40,9 @@ export interface MessageReplyOptions {
|
|||
};
|
||||
}
|
||||
|
||||
export interface MessageExtra {
|
||||
export interface MessageOptions {
|
||||
stickers?: string[];
|
||||
uploads?: Upload[];
|
||||
uploads?: CloudUpload[];
|
||||
replyOptions: MessageReplyOptions;
|
||||
content: string;
|
||||
channel: Channel;
|
||||
|
|
@ -75,17 +50,17 @@ export interface MessageExtra {
|
|||
openWarningPopout: (props: any) => any;
|
||||
}
|
||||
|
||||
export type MessageSendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
|
||||
export type MessageSendListener = (channelId: string, messageObj: MessageObject, options: MessageOptions) => Promisable<void | { cancel: boolean; }>;
|
||||
export type MessageEditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void | { cancel: boolean; }>;
|
||||
|
||||
const sendListeners = new Set<MessageSendListener>();
|
||||
const editListeners = new Set<MessageEditListener>();
|
||||
|
||||
export async function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra, replyOptions: MessageReplyOptions) {
|
||||
extra.replyOptions = replyOptions;
|
||||
export async function _handlePreSend(channelId: string, messageObj: MessageObject, options: MessageOptions, replyOptions: MessageReplyOptions) {
|
||||
options.replyOptions = replyOptions;
|
||||
for (const listener of sendListeners) {
|
||||
try {
|
||||
const result = await listener(channelId, messageObj, extra);
|
||||
const result = await listener(channelId, messageObj, options);
|
||||
if (result?.cancel) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Channel, Message } from "discord-types/general";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import type { ComponentType, MouseEventHandler } from "react";
|
||||
|
||||
const logger = new Logger("MessagePopover");
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue