Compare commits
26 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35509f7412 | ||
|
|
3abb5fcda8 | ||
|
|
ec6fbb190f | ||
|
|
2a52efbd97 | ||
|
|
aa52e1a42b | ||
|
|
8411026c51 | ||
|
|
8ec5b0a8d8 | ||
|
|
4c315b6886 | ||
|
|
c2d7b68950 | ||
|
|
c2fbcec3bd | ||
|
|
619b0ef858 | ||
|
|
3bbf885146 | ||
|
|
34fa7cba20 | ||
|
|
2f6dfd9bee | ||
|
|
0e75d8a42f | ||
|
|
bef4b01382 | ||
| 09ed2c78ba | |||
| 82f668ab92 | |||
| a202904f7d | |||
| f4888f29f1 | |||
| 826e774af1 | |||
| 9051269657 | |||
| 2a87063176 | |||
| 741af9eb43 | |||
| acccc963ae | |||
| 77084203c6 |
186 changed files with 2207 additions and 1007 deletions
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
|
@ -1,3 +1,12 @@
|
|||
# A fork. A fork. We're a fork.
|
||||
|
||||
Installing is the same as the Vencord devs laid out: [(Install)](https://docs.vencord.dev/installing/)
|
||||
* Beyond that, be sure to clone *this* repository instead of their upstream one.
|
||||
* `git clone https://git.dorkbutt.lol/dorkbutt/vencord`
|
||||
* After completing the steps, go to Settings > Vencord > Plugins and search for
|
||||
"FORKED - usrbg" and enable it. Be sure to tweak its settings!
|
||||
|
||||
|
||||
# Vencord
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.12.5",
|
||||
"version": "1.12.6",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
|
@ -53,8 +53,8 @@
|
|||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/yazl": "^2.4.5",
|
||||
"@vencord/discord-types": "link:packages/discord-types",
|
||||
"diff": "^7.0.0",
|
||||
"discord-types": "^1.3.26",
|
||||
"esbuild": "^0.25.1",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
|
|
|
|||
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.
|
||||
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
|
||||
}
|
||||
1
packages/discord-types/enums/index.ts
Normal file
1
packages/discord-types/enums/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./commands";
|
||||
19
packages/discord-types/package.json
Normal file
19
packages/discord-types/package.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "@vencord/discord-types",
|
||||
"author": "Vencord Contributors",
|
||||
"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": {
|
||||
"@types/react": "^19.0.10",
|
||||
"moment": "^2.22.2",
|
||||
"type-fest": "^4.41.0"
|
||||
}
|
||||
}
|
||||
21
packages/discord-types/src/classes.d.ts
vendored
Normal file
21
packages/discord-types/src/classes.d.ts
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
export interface ImageModalClasses {
|
||||
image: string,
|
||||
modal: string,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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[];
|
||||
}
|
||||
83
packages/discord-types/src/common/Channel.d.ts
vendored
Normal file
83
packages/discord-types/src/common/Channel.d.ts
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
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;
|
||||
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;
|
||||
}
|
||||
8
packages/discord-types/src/common/index.d.ts
vendored
Normal file
8
packages/discord-types/src/common/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
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;
|
||||
}
|
||||
191
packages/discord-types/src/common/messages/Message.d.ts
vendored
Normal file
191
packages/discord-types/src/common/messages/Message.d.ts
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import { CommandOption } from './Commands';
|
||||
import { User, UserJSON } from '../User';
|
||||
import { Embed, EmbedJSON } from './Embed';
|
||||
import { DiscordRecord } from "../Record";
|
||||
|
||||
/**
|
||||
* 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: number;
|
||||
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;
|
||||
nick: unknown; // probably a string
|
||||
nonce: string | undefined;
|
||||
pinned: boolean;
|
||||
reactions: MessageReaction[];
|
||||
state: string;
|
||||
stickerItems: {
|
||||
format_type: number;
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
stickers: unknown[];
|
||||
timestamp: moment.Moment;
|
||||
tts: boolean;
|
||||
type: number;
|
||||
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: number): boolean;
|
||||
isCommandType(): boolean;
|
||||
isEdited(): boolean;
|
||||
isSystemDM(): 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;
|
||||
}
|
||||
4
packages/discord-types/src/common/messages/index.d.ts
vendored
Normal file
4
packages/discord-types/src/common/messages/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export * from "./Commands";
|
||||
export * from "./Message";
|
||||
export * from "./Embed";
|
||||
export * from "./Emoji";
|
||||
|
|
@ -1,25 +1,7 @@
|
|||
/*
|
||||
* 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 { 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";
|
||||
// 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" | "eyebrow" | "heading-deprecated-12/normal" | "heading-deprecated-12/medium" | "heading-deprecated-12/semibold" | "heading-deprecated-12/bold" | "heading-deprecated-12/extrabold" | "redesign/heading-18/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" | "redesign/message-preview/normal" | "redesign/message-preview/medium" | "redesign/message-preview/semibold" | "redesign/message-preview/bold" | "redesign/channel-title/normal" | "redesign/channel-title/medium" | "redesign/channel-title/semibold" | "redesign/channel-title/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}`;
|
||||
|
||||
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
9
packages/discord-types/src/index.d.ts
vendored
Normal file
9
packages/discord-types/src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export * from "./common";
|
||||
export * from "./classes";
|
||||
export * from "./components";
|
||||
export * from "./flux";
|
||||
export * from "./fluxEvents";
|
||||
export * from "./menu";
|
||||
export * from "./stores";
|
||||
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;
|
||||
}>;
|
||||
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;
|
||||
}
|
||||
7
packages/discord-types/src/stores/GuildRoleStore.d.ts
vendored
Normal file
7
packages/discord-types/src/stores/GuildRoleStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { FluxStore, Role } from "..";
|
||||
|
||||
export class GuildRoleStore extends FluxStore {
|
||||
getRole(guildId: string, roleId: string): Role;
|
||||
getRoles(guildId: string): Record<string, Role>;
|
||||
getAllGuildRoles(): Record<string, Record<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[];
|
||||
}
|
||||
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;
|
||||
}
|
||||
21
packages/discord-types/src/stores/RelationshipStore.d.ts
vendored
Normal file
21
packages/discord-types/src/stores/RelationshipStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class RelationshipStore extends FluxStore {
|
||||
getFriendIDs(): string[];
|
||||
getIgnoredIDs(): string[];
|
||||
getBlockedIDs(): string[];
|
||||
|
||||
getPendingCount(): number;
|
||||
getRelationshipCount(): number;
|
||||
|
||||
/** Related to friend nicknames. */
|
||||
getNickname(userId: string): string;
|
||||
/** @returns Enum value from constants.RelationshipTypes */
|
||||
getRelationshipType(userId: string): number;
|
||||
isFriend(userId: string): boolean;
|
||||
isBlocked(userId: string): boolean;
|
||||
isIgnored(userId: string): boolean;
|
||||
getSince(userId: string): string;
|
||||
|
||||
getMutableRelationships(): Map<string, 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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>;
|
||||
}
|
||||
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>;
|
||||
}
|
||||
33
packages/discord-types/src/stores/index.d.ts
vendored
Normal file
33
packages/discord-types/src/stores/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// please keep in alphabetical order
|
||||
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 "./SelectedChannelStore";
|
||||
export * from "./SelectedGuildStore";
|
||||
export * from "./ThemeStore";
|
||||
export * from "./UserProfileStore";
|
||||
export * from "./UserStore";
|
||||
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";
|
||||
|
||||
|
|
@ -335,3 +317,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;
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
"@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",
|
||||
"type-fest": "^4.35.0"
|
||||
}
|
||||
|
|
|
|||
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
|
|
@ -65,12 +65,12 @@ importers:
|
|||
'@types/yazl':
|
||||
specifier: ^2.4.5
|
||||
version: 2.4.6
|
||||
'@vencord/discord-types':
|
||||
specifier: link:packages/discord-types
|
||||
version: link:packages/discord-types
|
||||
diff:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
discord-types:
|
||||
specifier: ^1.3.26
|
||||
version: 1.3.26
|
||||
esbuild:
|
||||
specifier: ^0.25.1
|
||||
version: 0.25.1
|
||||
|
|
@ -141,6 +141,18 @@ importers:
|
|||
specifier: ^0.3.5
|
||||
version: 0.3.5
|
||||
|
||||
packages/discord-types:
|
||||
dependencies:
|
||||
'@types/react':
|
||||
specifier: ^19.0.10
|
||||
version: 19.0.12
|
||||
moment:
|
||||
specifier: ^2.22.2
|
||||
version: 2.30.1
|
||||
type-fest:
|
||||
specifier: ^4.41.0
|
||||
version: 4.41.0
|
||||
|
||||
packages/vencord-types:
|
||||
dependencies:
|
||||
'@types/lodash':
|
||||
|
|
@ -155,9 +167,6 @@ importers:
|
|||
'@types/react-dom':
|
||||
specifier: 18.3.1
|
||||
version: 18.3.1
|
||||
discord-types:
|
||||
specifier: ^1.3.26
|
||||
version: 1.3.26
|
||||
standalone-electron-types:
|
||||
specifier: ^34.2.0
|
||||
version: 34.2.0
|
||||
|
|
@ -522,9 +531,6 @@ packages:
|
|||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
'@types/react@17.0.2':
|
||||
resolution: {integrity: sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==}
|
||||
|
||||
'@types/react@18.3.1':
|
||||
resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==}
|
||||
|
||||
|
|
@ -956,9 +962,6 @@ packages:
|
|||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
discord-types@1.3.26:
|
||||
resolution: {integrity: sha512-ToG51AOCH+JTQf7b+8vuYQe5Iqwz7nZ7StpECAZ/VZcI1ZhQk13pvt9KkRTfRv1xNvwJ2qib4e3+RifQlo8VPQ==}
|
||||
|
||||
doctrine@2.1.0:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -2328,6 +2331,10 @@ packages:
|
|||
resolution: {integrity: sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -2764,11 +2771,6 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/react': 19.0.12
|
||||
|
||||
'@types/react@17.0.2':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/react@18.3.1':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
|
|
@ -3255,11 +3257,6 @@ snapshots:
|
|||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
discord-types@1.3.26:
|
||||
dependencies:
|
||||
'@types/react': 17.0.2
|
||||
moment: 2.30.1
|
||||
|
||||
doctrine@2.1.0:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
|
@ -4923,6 +4920,8 @@ snapshots:
|
|||
|
||||
type-fest@4.38.0: {}
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
|
|
|
|||
|
|
@ -51,7 +51,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]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -363,6 +363,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
|
||||
];
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { waitFor } from "@webpack";
|
||||
import { Button, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
|
|
|
|||
|
|
@ -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,7 +17,7 @@
|
|||
*/
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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, 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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { FluxStore, Message } from "@vencord/discord-types";
|
||||
import { MessageCache, MessageStore } from "@webpack/common";
|
||||
import { FluxStore } from "@webpack/types";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
/**
|
||||
* Update and re-render a message
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ type ResolveUseSettings<T extends object> = {
|
|||
[Key in keyof T]:
|
||||
Key extends string
|
||||
? T[Key] extends Record<string, unknown>
|
||||
// @ts-ignore "Type instantiation is excessively deep and possibly infinite"
|
||||
// @ts-expect-error "Type instantiation is excessively deep and possibly infinite"
|
||||
? UseSettings<T[Key]> extends string ? `${Key}.${UseSettings<T[Key]>}` : never
|
||||
: Key
|
||||
: never;
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ButtonProps } from "@vencord/discord-types";
|
||||
import { Button } from "@webpack/common";
|
||||
import { ButtonProps } from "@webpack/types";
|
||||
|
||||
import { Heart } from "./Heart";
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import { DevsById } from "@utils/constants";
|
|||
import { fetchUserProfile } from "@utils/discord";
|
||||
import { classes, pluralise } from "@utils/misc";
|
||||
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { Forms, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@ import { Flex } from "@components/Flex";
|
|||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
import { PluginMeta } from "~plugins";
|
||||
|
|
@ -212,7 +212,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
const pluginMeta = PluginMeta[plugin.name];
|
||||
|
||||
return (
|
||||
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM} className="vc-text-selectable">
|
||||
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{plugin.name}</Text>
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
</div>
|
||||
</Forms.FormSection>
|
||||
{!!plugin.settingsAboutComponent && (
|
||||
<div className={classes(Margins.bottom8, "vc-text-selectable")}>
|
||||
<div className={Margins.bottom8}>
|
||||
<Forms.FormSection>
|
||||
<ErrorBoundary message="An error occurred while rendering this plugin's custom Info Component">
|
||||
<plugin.settingsAboutComponent tempSettings={tempSettings} />
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() =>
|
|||
const cl = classNameFactory("vc-plugins-");
|
||||
const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error");
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ function showErrorToast(message: string) {
|
|||
|
||||
function ReloadRequiredCard({ required }: { required: boolean; }) {
|
||||
return (
|
||||
<Card className={cl("info-card", { "restart-card": required })}>
|
||||
<Card className={classes(cl("info-card"), required && "vc-warning-card")}>
|
||||
{required ? (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Restart required!</Forms.FormTitle>
|
||||
|
|
@ -349,7 +349,7 @@ export default function PluginSettings() {
|
|||
select={onStatusChange}
|
||||
isSelected={v => v === searchValue.status}
|
||||
closeOnSelect={true}
|
||||
className={InputStyles.inputDefault}
|
||||
className={InputStyles.input}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -66,13 +66,6 @@
|
|||
gap: 0.25em;
|
||||
}
|
||||
|
||||
.vc-plugins-restart-card {
|
||||
padding: 1em;
|
||||
background: var(--info-warning-background);
|
||||
border: 1px solid var(--info-warning-foreground);
|
||||
color: var(--info-warning-foreground);
|
||||
}
|
||||
|
||||
.vc-plugins-restart-button {
|
||||
margin-top: 0.5em;
|
||||
background: var(--info-warning-foreground) !important;
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
|
|||
error={error ?? replacementError}
|
||||
/>
|
||||
{!isFunc && (
|
||||
<div className="vc-text-selectable">
|
||||
<div>
|
||||
<Forms.FormTitle className={Margins.top8}>Cheat Sheet</Forms.FormTitle>
|
||||
{Object.entries({
|
||||
"\\i": "Special regex escape sequence that matches identifiers (varnames, classnames, etc.)",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import { getStylusWebStoreUrl } from "@utils/web";
|
||||
import { findLazy } from "@webpack";
|
||||
import { Alerts, Button, Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||
|
|
@ -52,62 +52,6 @@ const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue &
|
|||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
function Validator({ link }: { link: string; }) {
|
||||
const [res, err, pending] = useAwaiter(() => fetch(link).then(res => {
|
||||
if (res.status > 300) throw `${res.status} ${res.statusText}`;
|
||||
const contentType = res.headers.get("Content-Type");
|
||||
if (!contentType?.startsWith("text/css") && !contentType?.startsWith("text/plain"))
|
||||
throw "Not a CSS file. Remember to use the raw link!";
|
||||
|
||||
return "Okay!";
|
||||
}));
|
||||
|
||||
const text = pending
|
||||
? "Checking..."
|
||||
: err
|
||||
? `Error: ${err instanceof Error ? err.message : String(err)}`
|
||||
: "Valid!";
|
||||
|
||||
return <Forms.FormText style={{
|
||||
color: pending ? "var(--text-muted)" : err ? "var(--text-danger)" : "var(--status-positive)"
|
||||
}}>{text}</Forms.FormText>;
|
||||
}
|
||||
|
||||
function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
||||
if (!themeLinks.length) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Forms.FormTitle className={Margins.top20} tag="h5">Validator</Forms.FormTitle>
|
||||
<Forms.FormText>This section will tell you whether your themes can successfully be loaded</Forms.FormText>
|
||||
<div>
|
||||
{themeLinks.map(rawLink => {
|
||||
const { label, link } = (() => {
|
||||
const match = /^@(light|dark) (.*)/.exec(rawLink);
|
||||
if (!match) return { label: rawLink, link: rawLink };
|
||||
|
||||
const [, mode, link] = match;
|
||||
return { label: `[${mode} mode only] ${link}`, link };
|
||||
})();
|
||||
|
||||
return <Card style={{
|
||||
padding: ".5em",
|
||||
marginBottom: ".5em",
|
||||
marginTop: ".5em"
|
||||
}} key={link}>
|
||||
<Forms.FormTitle tag="h5" style={{
|
||||
overflowWrap: "break-word"
|
||||
}}>
|
||||
{label}
|
||||
</Forms.FormTitle>
|
||||
<Validator link={link} />
|
||||
</Card>;
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ThemeCardProps {
|
||||
theme: UserThemeHeader;
|
||||
enabled: boolean;
|
||||
|
|
@ -309,7 +253,13 @@ function ThemesTab() {
|
|||
function renderOnlineThemes() {
|
||||
return (
|
||||
<>
|
||||
<Card className="vc-settings-card vc-text-selectable">
|
||||
<Card className={classes("vc-warning-card", Margins.bottom16)}>
|
||||
<Forms.FormText>
|
||||
This section is for advanced users. If you are having difficulties using it, use the
|
||||
Local Themes tab instead.
|
||||
</Forms.FormText>
|
||||
</Card>
|
||||
<Card className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle>
|
||||
<Forms.FormText>One link per line</Forms.FormText>
|
||||
<Forms.FormText>You can prefix lines with @light or @dark to toggle them based on your Discord theme</Forms.FormText>
|
||||
|
|
@ -321,12 +271,11 @@ function ThemesTab() {
|
|||
value={themeText}
|
||||
onChange={setThemeText}
|
||||
className={"vc-settings-theme-links"}
|
||||
placeholder="Theme Links"
|
||||
placeholder="Enter Theme Links..."
|
||||
spellCheck={false}
|
||||
onBlur={onBlur}
|
||||
rows={10}
|
||||
/>
|
||||
<Validators themeLinks={settings.themeLinks} />
|
||||
</Forms.FormSection>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ function Updater() {
|
|||
|
||||
<Forms.FormTitle tag="h5">Repo</Forms.FormTitle>
|
||||
|
||||
<Forms.FormText className="vc-text-selectable">
|
||||
<Forms.FormText>
|
||||
{repoPending
|
||||
? repo
|
||||
: err
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.vc-settings-tab-bar {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 2px solid var(--background-modifier-accent);
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.vc-settings-tab-bar-item {
|
||||
|
|
@ -20,6 +20,13 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vc-warning-card {
|
||||
padding: 1em;
|
||||
background: var(--info-warning-background);
|
||||
border: 1px solid var(--info-warning-foreground);
|
||||
color: var(--info-warning-foreground);
|
||||
}
|
||||
|
||||
.vc-backup-restore-card {
|
||||
background-color: var(--info-warning-background);
|
||||
border-color: var(--info-warning-foreground);
|
||||
|
|
@ -30,19 +37,20 @@
|
|||
/* Needed to fix bad themes that hide certain textarea elements for whatever eldritch reason */
|
||||
display: inline-block !important;
|
||||
color: var(--text-default) !important;
|
||||
padding: 0.5em;
|
||||
border: 1px solid var(--background-modifier-accent);
|
||||
padding: 0.5em 1em;
|
||||
border: 1px solid var(--input-border);
|
||||
max-height: unset;
|
||||
background-color: transparent;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
line-height: 2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.vc-settings-theme-links::placeholder {
|
||||
color: var(--header-secondary);
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.vc-settings-theme-links:focus {
|
||||
|
|
@ -60,15 +68,6 @@
|
|||
background-color: var(--button-danger-background);
|
||||
}
|
||||
|
||||
.vc-text-selectable,
|
||||
.vc-text-selectable :where([class*="text" i], [class*="title" i]) {
|
||||
/* make text selectable, silly discord makes the entirety of settings not selectable */
|
||||
user-select: text;
|
||||
|
||||
/* discord also sets cursor: default which prevents the cursor from showing as text */
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
.vc-updater-modal {
|
||||
padding: 1.5em !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.vc-settings-card {
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vc-special-card-special {
|
||||
padding: 1em 1.5em;
|
||||
margin-bottom: 1em;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import { ModuleFactory } from "@vencord/discord-types/webpack";
|
||||
import * as Webpack from "@webpack";
|
||||
import { wreq } from "@webpack";
|
||||
import { AnyModuleFactory, ModuleFactory } from "@webpack/wreq.d";
|
||||
import { AnyModuleFactory } from "webpack";
|
||||
|
||||
export async function loadLazyChunks() {
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ async function runReporter() {
|
|||
}
|
||||
}, "Vencord Reporter");
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
Vencord.Webpack._initReporter = function () {
|
||||
// initReporter is called in the patched entry point of Discord
|
||||
// setImmediate to only start searching for lazy chunks after Discord initialized the app
|
||||
|
|
@ -83,7 +83,6 @@ async function runReporter() {
|
|||
result = Webpack.mapMangledModule(code, mapper, includeBlacklistedExports);
|
||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
result = Webpack[method](...args);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ export const CspPolicies: PolicyMap = {
|
|||
"dearrow-thumb.ajay.app": ImageSrc, // Dearrow Thumbnail CDN
|
||||
"usrbg.is-hardly.online": ImageSrc, // USRBG API
|
||||
"icons.duckduckgo.com": ImageSrc, // DuckDuckGo Favicon API (Reverse Image Search)
|
||||
|
||||
// forked addition
|
||||
"*.dorkbutt.lol": ImageAndCssSrc,
|
||||
};
|
||||
|
||||
const findHeader = (headers: PolicyMap, headerName: Lowercase<string>) => {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const asarPath = join(dirname(injectorPath), "..", asarName);
|
|||
const discordPkg = require(join(asarPath, "package.json"));
|
||||
require.main!.filename = join(asarPath, discordPkg.main);
|
||||
|
||||
// @ts-ignore Untyped method? Dies from cringe
|
||||
// @ts-expect-error Untyped method? Dies from cringe
|
||||
app.setAppPath(asarPath);
|
||||
|
||||
if (!IS_VANILLA) {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import { Margins } from "@utils/margins";
|
|||
import { shouldShowContributorBadge } from "@utils/misc";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { Forms, Toasts, UserStore } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/emojis/1092089799109775453.png?size=64";
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||
replacement: {
|
||||
match: /(?<=:null),(.{0,40}togglePopout:.+?}\)),(.+?)\]}\):null,(?<=\((\i\.\i),{label:.+?children:\[(\i&&!\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||
match: /(?<=:null),(.{0,40}togglePopout:.+?}\)),(.+?)\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||
replace: (_, ReactButton, PotionButton, ButtonComponent, showReactButton, message) => "" +
|
||||
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,${showReactButton}&&${PotionButton},`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { WebpackRequire } from "@webpack/wreq.d";
|
||||
import { WebpackRequire } from "@vencord/discord-types/webpack";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableAnalytics: {
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ export default definePlugin({
|
|||
get chromiumVersion() {
|
||||
try {
|
||||
return VencordNative.native.getVersions().chrome
|
||||
// @ts-ignore Typescript will add userAgentData IMMEDIATELY
|
||||
// @ts-expect-error Typescript will add userAgentData IMMEDIATELY
|
||||
|| navigator.userAgentData?.brands?.find(b => b.brand === "Chromium" || b.brand === "Google Chrome")?.version
|
||||
|| null;
|
||||
} catch { // inb4 some stupid browser throws unsupported error for navigator.userAgentData, it's only in chromium
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ import { onlyOnce } from "@utils/onlyOnce";
|
|||
import { makeCodeblock } from "@utils/text";
|
||||
import definePlugin from "@utils/types";
|
||||
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, PermissionsBits, PermissionStore, RelationshipStore, showToast, Text, Toasts, UserStore } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { JSX } from "react";
|
||||
|
||||
import gitHash from "~git-hash";
|
||||
|
|
@ -196,7 +196,6 @@ export default definePlugin({
|
|||
}
|
||||
}
|
||||
|
||||
// @ts-ignore outdated type
|
||||
const roles = GuildMemberStore.getSelfMember(VENCORD_GUILD_ID)?.roles;
|
||||
if (!roles || TrustedRolesIds.some(id => roles.includes(id))) return;
|
||||
|
||||
|
|
@ -319,7 +318,7 @@ export default definePlugin({
|
|||
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
||||
|
||||
return (
|
||||
<Card className={`vc-plugins-restart-card ${Margins.top8}`}>
|
||||
<Card className={`vc-warning-card ${Margins.top8}`}>
|
||||
Please do not private message Vencord plugin developers for support!
|
||||
<br />
|
||||
Instead, use the Vencord support channel: {Parser.parse("https://discord.com/channels/1015060230222131221/1026515880080842772")}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
interface UserProfileProps {
|
||||
popoutProps: Record<string, any>;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import { ScreenshareIcon } from "@components/Icons";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { openImageModal } from "@utils/discord";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Channel, User } from "@vencord/discord-types";
|
||||
import { Menu } from "@webpack/common";
|
||||
import { Channel, User } from "discord-types/general";
|
||||
|
||||
import { ApplicationStreamingStore, ApplicationStreamPreviewStore } from "./webpack/stores";
|
||||
import { ApplicationStream, Stream } from "./webpack/types/stores";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FluxStore } from "@webpack/types";
|
||||
import { FluxStore } from "@vencord/discord-types";
|
||||
|
||||
export interface ApplicationStreamPreviewStore extends FluxStore {
|
||||
getIsPreviewLoading: (guildId: string | bigint | null, channelId: string | bigint, ownerId: string | bigint) => boolean;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
|
||||
.vc-clientTheme-container [class^="swatch"] {
|
||||
border: thin solid var(--background-modifier-accent) !important;
|
||||
border: thin solid var(--input-border) !important;
|
||||
}
|
||||
|
||||
.vc-clientTheme-buttons-container {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import { LinkIcon } from "@components/Icons";
|
|||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import type { Channel, User } from "@vencord/discord-types";
|
||||
import { Menu } from "@webpack/common";
|
||||
import type { Channel, User } from "discord-types/general";
|
||||
|
||||
interface UserContextProps {
|
||||
channel: Channel;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { useEffect, useState, zustandCreate } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import { AvatarDecoration } from "../../";
|
||||
import { getUsersDecorations } from "../api";
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Queue } from "@utils/Queue";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import { Decoration, getPresets, Preset } from "../../lib/api";
|
||||
import { GUILD_ID, INVITE_KEY } from "../../lib/constants";
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ export default definePlugin({
|
|||
{
|
||||
find: 'type:"user",revision',
|
||||
replacement: {
|
||||
match: /!(\i)&&"CONNECTION_OPEN".+?;/g,
|
||||
replace: "$1=!0;"
|
||||
match: /!(\i)(?=&&"CONNECTION_OPEN")/,
|
||||
replace: "!($1=true)"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -116,11 +116,19 @@ export default definePlugin({
|
|||
},
|
||||
// Fix some tricky experiments name causing a client crash
|
||||
{
|
||||
match: /.getRegisteredExperiments\(\)(?<=(\i)=.+?).+?if\(null==(\i)(?=\)return null;)/,
|
||||
replace: "$&||!Object.hasOwn($1,$2)"
|
||||
match: /.getExperimentBucketName.+?if\(null==(\i)\|\|null==\i(?=\)return null;)/,
|
||||
replace: "$&||({})[$1]!=null"
|
||||
}
|
||||
]
|
||||
},
|
||||
// Fix another function which cases crashes with tricky experiment names and the experiment embed
|
||||
{
|
||||
find: "}getServerAssignment(",
|
||||
replacement: {
|
||||
match: /}getServerAssignment\((\i),\i,\i\){/,
|
||||
replace: "$&if($1==null)return;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport),
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co
|
|||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getGuildAcronym } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Guild } from "@vencord/discord-types";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||
import { Guild } from "discord-types/general";
|
||||
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, IconUtils, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
const StickersStore = findStoreLazy("StickersStore");
|
||||
|
|
@ -250,13 +251,18 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
|
|||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
src={g.getIconURL(512, true)}
|
||||
src={IconUtils.getGuildIconURL({
|
||||
id: g.id,
|
||||
icon: g.icon,
|
||||
canAnimate: true,
|
||||
size: 512
|
||||
})}
|
||||
alt={g.name}
|
||||
/>
|
||||
) : (
|
||||
<Forms.FormText
|
||||
style={{
|
||||
fontSize: getFontSize(g.acronym),
|
||||
fontSize: getFontSize(getGuildAcronym(g)),
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
|
|
@ -264,7 +270,7 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
|
|||
cursor: isCloning ? "not-allowed" : "pointer",
|
||||
}}
|
||||
>
|
||||
{g.acronym}
|
||||
{getGuildAcronym(g)}
|
||||
</Forms.FormText>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,9 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
|||
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, Patch } from "@utils/types";
|
||||
import type { Emoji, Message } from "@vencord/discord-types";
|
||||
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import type { Emoji } from "@webpack/types";
|
||||
import type { Message } from "discord-types/general";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
|
||||
|
|
@ -813,7 +812,6 @@ export default definePlugin({
|
|||
|
||||
let isUsableTwitchSubEmote = false;
|
||||
if (e.managed && e.guildId) {
|
||||
// @ts-ignore outdated type
|
||||
const myRoles = GuildMemberStore.getSelfMember(e.guildId)?.roles ?? [];
|
||||
isUsableTwitchSubEmote = e.roles.some(r => myRoles.includes(r));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,19 +22,16 @@ import "./index.css";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { fetchUserProfile } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { User, UserProfile } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, ColorPicker, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { ReactElement } from "react";
|
||||
import virtualMerge from "virtual-merge";
|
||||
|
||||
interface UserProfile extends User {
|
||||
themeColors?: Array<number>;
|
||||
}
|
||||
|
||||
interface Colors {
|
||||
primary: number;
|
||||
accent: number;
|
||||
|
|
@ -85,14 +82,6 @@ const settings = definePluginSettings({
|
|||
}
|
||||
});
|
||||
|
||||
interface ColorPickerProps {
|
||||
color: number | null;
|
||||
label: ReactElement<any>;
|
||||
showEyeDropper?: boolean;
|
||||
suggestedColors?: string[];
|
||||
onChange(value: number | null): void;
|
||||
}
|
||||
|
||||
// I can't be bothered to figure out the semantics of this component. The
|
||||
// functions surely get some event argument sent to them and they likely aren't
|
||||
// all required. If anyone who wants to use this component stumbles across this
|
||||
|
|
@ -110,6 +99,100 @@ interface ProfileModalProps {
|
|||
|
||||
const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
|
||||
|
||||
function SettingsAboutComponentWrapper() {
|
||||
const [, , userProfileLoading] = useAwaiter(() => fetchUserProfile(UserStore.getCurrentUser().id));
|
||||
|
||||
return !userProfileLoading && <SettingsAboutComponent />;
|
||||
}
|
||||
|
||||
function SettingsAboutComponent() {
|
||||
const existingColors = decode(
|
||||
UserProfileStore.getUserProfile(UserStore.getCurrentUser().id)?.bio ?? ""
|
||||
) ?? [0, 0];
|
||||
const [color1, setColor1] = useState(existingColors[0]);
|
||||
const [color2, setColor2] = useState(existingColors[1]);
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
After enabling this plugin, you will see custom colors in
|
||||
the profiles of other people using compatible plugins.{" "}
|
||||
<br />
|
||||
To set your own colors:
|
||||
<ul>
|
||||
<li>
|
||||
• use the color pickers below to choose your colors
|
||||
</li>
|
||||
<li>• click the "Copy 3y3" button</li>
|
||||
<li>• paste the invisible text anywhere in your bio</li>
|
||||
</ul><br />
|
||||
<Forms.FormDivider
|
||||
className={classes(Margins.top8, Margins.bottom8)}
|
||||
/>
|
||||
<Forms.FormTitle tag="h3">Color pickers</Forms.FormTitle>
|
||||
<Flex
|
||||
direction={Flex.Direction.HORIZONTAL}
|
||||
style={{ gap: "1rem" }}
|
||||
>
|
||||
<ColorPicker
|
||||
color={color1}
|
||||
label={
|
||||
<Text
|
||||
variant={"text-xs/normal"}
|
||||
style={{ marginTop: "4px" }}
|
||||
>
|
||||
Primary
|
||||
</Text>
|
||||
}
|
||||
onChange={(color: number) => {
|
||||
setColor1(color);
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
color={color2}
|
||||
label={
|
||||
<Text
|
||||
variant={"text-xs/normal"}
|
||||
style={{ marginTop: "4px" }}
|
||||
>
|
||||
Accent
|
||||
</Text>
|
||||
}
|
||||
onChange={(color: number) => {
|
||||
setColor2(color);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const colorString = encode(color1, color2);
|
||||
copyWithToast(colorString);
|
||||
}}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.XLARGE}
|
||||
>
|
||||
Copy 3y3
|
||||
</Button>
|
||||
</Flex>
|
||||
<Forms.FormDivider
|
||||
className={classes(Margins.top8, Margins.bottom8)}
|
||||
/>
|
||||
<Forms.FormTitle tag="h3">Preview</Forms.FormTitle>
|
||||
<div className="vc-fpt-preview">
|
||||
<ProfileModal
|
||||
user={UserStore.getCurrentUser()}
|
||||
pendingThemeColors={[color1, color2]}
|
||||
onAvatarChange={() => { }}
|
||||
onBannerChange={() => { }}
|
||||
canUsePremiumCustomization={true}
|
||||
hideExampleButton={true}
|
||||
hideFakeActivity={true}
|
||||
isTryItOutFlow={true}
|
||||
/>
|
||||
</div>
|
||||
</Forms.FormText>
|
||||
</Forms.FormSection>);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "FakeProfileThemes",
|
||||
|
|
@ -121,107 +204,22 @@ export default definePlugin({
|
|||
replacement: {
|
||||
match: /(?<=getUserProfile\(\i\){return )(.+?)(?=})/,
|
||||
replace: "$self.colorDecodeHook($1)"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
find: "#{intl::USER_SETTINGS_RESET_PROFILE_THEME}",
|
||||
replacement: {
|
||||
match: /#{intl::USER_SETTINGS_RESET_PROFILE_THEME}\)}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||
match: /#{intl::USER_SETTINGS_RESET_PROFILE_THEME}\).+?}\)(?=\])(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
||||
}
|
||||
}
|
||||
],
|
||||
settingsAboutComponent: () => {
|
||||
const existingColors = decode(
|
||||
UserProfileStore.getUserProfile(UserStore.getCurrentUser().id).bio
|
||||
) ?? [0, 0];
|
||||
const [color1, setColor1] = useState(existingColors[0]);
|
||||
const [color2, setColor2] = useState(existingColors[1]);
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
After enabling this plugin, you will see custom colors in
|
||||
the profiles of other people using compatible plugins.{" "}
|
||||
<br />
|
||||
To set your own colors:
|
||||
<ul>
|
||||
<li>
|
||||
• use the color pickers below to choose your colors
|
||||
</li>
|
||||
<li>• click the "Copy 3y3" button</li>
|
||||
<li>• paste the invisible text anywhere in your bio</li>
|
||||
</ul><br />
|
||||
<Forms.FormDivider
|
||||
className={classes(Margins.top8, Margins.bottom8)}
|
||||
/>
|
||||
<Forms.FormTitle tag="h3">Color pickers</Forms.FormTitle>
|
||||
<Flex
|
||||
direction={Flex.Direction.HORIZONTAL}
|
||||
style={{ gap: "1rem" }}
|
||||
>
|
||||
<ColorPicker
|
||||
color={color1}
|
||||
label={
|
||||
<Text
|
||||
variant={"text-xs/normal"}
|
||||
style={{ marginTop: "4px" }}
|
||||
>
|
||||
Primary
|
||||
</Text>
|
||||
}
|
||||
onChange={(color: number) => {
|
||||
setColor1(color);
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
color={color2}
|
||||
label={
|
||||
<Text
|
||||
variant={"text-xs/normal"}
|
||||
style={{ marginTop: "4px" }}
|
||||
>
|
||||
Accent
|
||||
</Text>
|
||||
}
|
||||
onChange={(color: number) => {
|
||||
setColor2(color);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const colorString = encode(color1, color2);
|
||||
copyWithToast(colorString);
|
||||
}}
|
||||
color={Button.Colors.PRIMARY}
|
||||
size={Button.Sizes.XLARGE}
|
||||
>
|
||||
Copy 3y3
|
||||
</Button>
|
||||
</Flex>
|
||||
<Forms.FormDivider
|
||||
className={classes(Margins.top8, Margins.bottom8)}
|
||||
/>
|
||||
<Forms.FormTitle tag="h3">Preview</Forms.FormTitle>
|
||||
<div className="vc-fpt-preview">
|
||||
<ProfileModal
|
||||
user={UserStore.getCurrentUser()}
|
||||
pendingThemeColors={[color1, color2]}
|
||||
onAvatarChange={() => { }}
|
||||
onBannerChange={() => { }}
|
||||
canUsePremiumCustomization={true}
|
||||
hideExampleButton={true}
|
||||
hideFakeActivity={true}
|
||||
isTryItOutFlow={true}
|
||||
/>
|
||||
</div>
|
||||
</Forms.FormText>
|
||||
</Forms.FormSection>);
|
||||
},
|
||||
settingsAboutComponent: SettingsAboutComponentWrapper,
|
||||
|
||||
settings,
|
||||
colorDecodeHook(user: UserProfile) {
|
||||
if (user) {
|
||||
if (user?.bio) {
|
||||
// don't replace colors if already set with nitro
|
||||
if (settings.store.nitroFirst && user.themeColors) return user;
|
||||
const colors = decode(user.bio);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Emoji } from "@vencord/discord-types";
|
||||
import { EmojiStore } from "@webpack/common";
|
||||
import { Emoji } from "@webpack/types";
|
||||
|
||||
interface EmojiAutocompleteState {
|
||||
query?: {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Channel, User } from "@vencord/discord-types";
|
||||
import { GuildStore } from "@webpack/common";
|
||||
import { Channel, User } from "discord-types/general";
|
||||
|
||||
export default definePlugin({
|
||||
name: "ForceOwnerCrown",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue