mirror of
https://github.com/Vendicated/Vencord.git
synced 2024-09-20 06:30:35 +00:00
Compare commits
150 commits
064715f2ae
...
8324f8a015
Author | SHA1 | Date | |
---|---|---|---|
|
8324f8a015 | ||
|
8afd79dd50 | ||
|
65c5897dc3 | ||
|
6cce8a8bc4 | ||
|
1848b16536 | ||
|
c572116b97 | ||
|
e26986f66a | ||
|
4f6e980dc4 | ||
|
e7faf3321e | ||
|
187ca435b5 | ||
|
a3d4c1963f | ||
|
cb4be3d77b | ||
|
ae6290a605 | ||
|
0561b3890a | ||
|
ec4e97b187 | ||
|
3b20759c60 | ||
|
5e52a34205 | ||
|
2cd661f7b6 | ||
|
e88e3b3e13 | ||
|
db4c0d1446 | ||
|
1de442a84b | ||
|
c93f694faf | ||
|
f045554bfe | ||
|
5180df743b | ||
|
f84220eada | ||
|
588a40c43f | ||
|
56be5eb247 | ||
|
9ad6e1b3a2 | ||
|
c0842cb84c | ||
|
fc3618628f | ||
|
2721cd3d47 | ||
|
1da50eb8f4 | ||
|
8696ca0706 | ||
|
070b25e813 | ||
|
90c9dd20bb | ||
|
dcb9402f1c | ||
|
bc3093a41e | ||
|
6c19f52f26 | ||
|
b63becffb6 | ||
|
7a1a9d20e8 | ||
|
ae10deaee4 | ||
|
61eacb2106 | ||
|
b3a9d33411 | ||
|
e764018621 | ||
|
cafe4b19a2 | ||
|
422c18c37c | ||
|
d7036c5886 | ||
|
9ee1b5ba6e | ||
|
0d4ec94203 | ||
|
0df09f2aab | ||
|
2728102fbc | ||
|
249ea30995 | ||
|
8d2fa0e45f | ||
|
adc60648a5 | ||
|
9a9c1b0487 | ||
|
892de53603 | ||
|
c8602ef52b | ||
|
c88ac8d5ca | ||
|
de2598a87a | ||
|
c3af902ace | ||
|
e04225df1a | ||
|
dec282bf50 | ||
|
d29d5e9476 | ||
|
46e4fc664a | ||
|
e9cb2eccec | ||
|
f0483f9c24 | ||
|
13119c0b29 | ||
|
720aaf6f3e | ||
|
f04bd8a7ad | ||
|
acf77127c8 | ||
|
530e19e266 | ||
|
7f36d8569f | ||
|
28f70e2942 | ||
|
6d704d603e | ||
|
65787848c4 | ||
|
73ca8dc776 | ||
|
209c64afc4 | ||
|
b4366bdab6 | ||
|
70ae9eabf4 | ||
|
d739675413 | ||
|
2462544d61 | ||
|
6022317d5e | ||
|
8f0204b4ca | ||
|
f801ab3cb9 | ||
|
8c48731026 | ||
|
31993c0053 | ||
|
1dc98721ee | ||
|
6266d037e7 | ||
|
4c466e9c04 | ||
|
522d2fd35c | ||
|
e24d5d3f93 | ||
|
521647b4e1 | ||
|
23992bd213 | ||
|
bc64dd400a | ||
|
38647087e0 | ||
|
c5d03bab3b | ||
|
abf48db04d | ||
|
2c11a7ad84 | ||
|
570e41b95c | ||
|
b722513662 | ||
|
e608170820 | ||
|
082227301e | ||
|
f87c73685e | ||
|
04c4c85ea7 | ||
|
82cc75ecc8 | ||
|
39b3e747f9 | ||
|
83fedd4df1 | ||
|
9edb4c01f2 | ||
|
c542a81986 | ||
|
1d93a03654 | ||
|
1a106e755b | ||
|
c2a8671f31 | ||
|
3ec9f7cf89 | ||
|
7c71d9f52d | ||
|
2ac335c3f4 | ||
|
1947cedd55 | ||
|
610c48fe92 | ||
|
736fef879e | ||
|
8a09cb533b | ||
|
9c7c3d0c5d | ||
|
f43f404123 | ||
|
73a2f5548a | ||
|
2da6a64748 | ||
|
9fab41449a | ||
|
1225861843 | ||
|
9b600b9dad | ||
|
6df510d9f5 | ||
|
a6c7cb9517 | ||
|
4cb2661ae9 | ||
|
0fd7a59d8a | ||
|
f2b22bc349 | ||
|
2f1c10de3e | ||
|
1c3e33dbbd | ||
|
9060352977 | ||
|
4ea2d1ca5c | ||
|
284a951353 | ||
|
d02a241ff9 | ||
|
cedf10bdd2 | ||
|
4f68631080 | ||
|
278c4f0d60 | ||
|
ba0a9d0696 | ||
|
f41c6f4762 | ||
|
87e86271fe | ||
|
3dd65d520e | ||
|
369f66e810 | ||
|
0207b9fa12 | ||
|
2c20a85871 | ||
|
ff6f24b8c9 | ||
|
bde85e073a | ||
|
ccce4952cc |
25 changed files with 377 additions and 39 deletions
|
@ -292,10 +292,10 @@ export default function PluginSettings() {
|
|||
|
||||
if (!pluginFilter(p)) continue;
|
||||
|
||||
const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
const isRequired = p.required || p.isDependency || depMap[p.name]?.some(d => settings.plugins[d].enabled);
|
||||
|
||||
if (isRequired) {
|
||||
const tooltipText = p.required
|
||||
const tooltipText = p.required || !depMap[p.name]
|
||||
? "This plugin is required for Vencord to function."
|
||||
: makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled));
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ export default definePlugin({
|
|||
required: true,
|
||||
description: "Helps us provide support to you",
|
||||
authors: [Devs.Ven],
|
||||
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
dependencies: ["UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||
|
||||
settings,
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ interface ActivityButton {
|
|||
}
|
||||
|
||||
interface Activity {
|
||||
state: string;
|
||||
state?: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
|
@ -52,8 +52,8 @@ const enum ActivityFlag {
|
|||
|
||||
export interface TrackData {
|
||||
name: string;
|
||||
album: string;
|
||||
artist: string;
|
||||
album?: string;
|
||||
artist?: string;
|
||||
|
||||
appleMusicLink?: string;
|
||||
songLink?: string;
|
||||
|
@ -61,8 +61,8 @@ export interface TrackData {
|
|||
albumArtwork?: string;
|
||||
artistArtwork?: string;
|
||||
|
||||
playerPosition: number;
|
||||
duration: number;
|
||||
playerPosition?: number;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
const enum AssetImageType {
|
||||
|
@ -155,8 +155,8 @@ const settings = definePluginSettings({
|
|||
function customFormat(formatStr: string, data: TrackData) {
|
||||
return formatStr
|
||||
.replaceAll("{name}", data.name)
|
||||
.replaceAll("{album}", data.album)
|
||||
.replaceAll("{artist}", data.artist);
|
||||
.replaceAll("{album}", data.album ?? "")
|
||||
.replaceAll("{artist}", data.artist ?? "");
|
||||
}
|
||||
|
||||
function getImageAsset(type: AssetImageType, data: TrackData) {
|
||||
|
@ -212,14 +212,16 @@ export default definePlugin({
|
|||
|
||||
const assets: ActivityAssets = {};
|
||||
|
||||
const isRadio = Number.isNaN(trackData.duration) && (trackData.playerPosition === 0);
|
||||
|
||||
if (settings.store.largeImageType !== AssetImageType.Disabled) {
|
||||
assets.large_image = largeImageAsset;
|
||||
assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
if (!isRadio) assets.large_text = customFormat(settings.store.largeTextString, trackData);
|
||||
}
|
||||
|
||||
if (settings.store.smallImageType !== AssetImageType.Disabled) {
|
||||
assets.small_image = smallImageAsset;
|
||||
assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
if (!isRadio) assets.small_text = customFormat(settings.store.smallTextString, trackData);
|
||||
}
|
||||
|
||||
const buttons: ActivityButton[] = [];
|
||||
|
@ -243,17 +245,17 @@ export default definePlugin({
|
|||
|
||||
name: customFormat(settings.store.nameString, trackData),
|
||||
details: customFormat(settings.store.detailsString, trackData),
|
||||
state: customFormat(settings.store.stateString, trackData),
|
||||
state: isRadio ? undefined : customFormat(settings.store.stateString, trackData),
|
||||
|
||||
timestamps: (settings.store.enableTimestamps ? {
|
||||
timestamps: (trackData.playerPosition && trackData.duration && settings.store.enableTimestamps) ? {
|
||||
start: Date.now() - (trackData.playerPosition * 1000),
|
||||
end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
|
||||
} : undefined),
|
||||
} : undefined,
|
||||
|
||||
assets,
|
||||
|
||||
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: { button_urls: buttons.map(v => v.url) || undefined, },
|
||||
buttons: !isRadio && buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
|
||||
|
||||
type: settings.store.activityType,
|
||||
flags: ActivityFlag.INSTANCE,
|
||||
|
|
68
src/plugins/betterSettings/PluginsSubmenu.tsx
Normal file
68
src/plugins/betterSettings/PluginsSubmenu.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||
import { isObjectEmpty } from "@utils/misc";
|
||||
import { Alerts, i18n, Menu, useMemo, useState } from "@webpack/common";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
function onRestartNeeded() {
|
||||
Alerts.show({
|
||||
title: "Restart required",
|
||||
body: <p>You have changed settings that require a restart.</p>,
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: () => location.reload()
|
||||
});
|
||||
}
|
||||
|
||||
export default function PluginsSubmenu() {
|
||||
const sortedPlugins = useMemo(() => Object.values(Plugins)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)), []);
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const search = query.toLowerCase();
|
||||
const include = (p: typeof Plugins[keyof typeof Plugins]) => (
|
||||
Vencord.Plugins.isPluginEnabled(p.name)
|
||||
&& p.options && !isObjectEmpty(p.options)
|
||||
&& (
|
||||
p.name.toLowerCase().includes(search)
|
||||
|| p.description.toLowerCase().includes(search)
|
||||
|| p.tags?.some(t => t.toLowerCase().includes(search))
|
||||
)
|
||||
);
|
||||
|
||||
const plugins = sortedPlugins.filter(include);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu.MenuControlItem
|
||||
id="vc-plugins-search"
|
||||
control={(props, ref) => (
|
||||
<Menu.MenuSearchControl
|
||||
{...props}
|
||||
query={query}
|
||||
onChange={setQuery}
|
||||
ref={ref}
|
||||
placeholder={i18n.Messages.SEARCH}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!!plugins.length && <Menu.MenuSeparator />}
|
||||
|
||||
{plugins.map(p => (
|
||||
<Menu.MenuItem
|
||||
key={p.name}
|
||||
id={p.name}
|
||||
label={p.name}
|
||||
action={() => openPluginModal(p, onRestartNeeded)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -13,6 +13,8 @@ import { waitFor } from "@webpack";
|
|||
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import type { HTMLAttributes, ReactElement } from "react";
|
||||
|
||||
import PluginsSubmenu from "./PluginsSubmenu";
|
||||
|
||||
type SettingsEntry = { section: string, label: string; };
|
||||
|
||||
const cl = classNameFactory("");
|
||||
|
@ -118,13 +120,21 @@ export default definePlugin({
|
|||
},
|
||||
{ // Settings cog context menu
|
||||
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL",
|
||||
replacement: {
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
}
|
||||
}
|
||||
replacement: [
|
||||
{
|
||||
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$1$self.wrapMenu($2)"
|
||||
},
|
||||
{
|
||||
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
||||
replace: "$&case 'VencordPlugins':return $self.PluginsSubmenu();"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
|
||||
PluginsSubmenu,
|
||||
|
||||
// This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
|
||||
// without possibly also catching unrelated errors of children.
|
||||
//
|
||||
|
|
|
@ -126,7 +126,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: '"Handling ping: "',
|
||||
find: '"_handleLocalVideoDisabled: ',
|
||||
predicate: () => settings.store.disableNoisyLoggers,
|
||||
replacement: {
|
||||
match: /new \i\.\i\("RTCConnection\("\.concat.+?\)\)(?=,)/,
|
||||
|
|
|
@ -23,12 +23,13 @@ import { ErrorCard } from "@components/ErrorCard";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { Forms, React } from "@webpack/common";
|
||||
|
||||
import hideBugReport from "./hideBugReport.css?managed";
|
||||
|
||||
const KbdStyles = findByPropsLazy("key", "combo");
|
||||
const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
toolbarDevMenu: {
|
||||
|
@ -78,8 +79,8 @@ export default definePlugin({
|
|||
{
|
||||
find: "toolbar:function",
|
||||
replacement: {
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
match: /hasBugReporterAccess:(\i)/,
|
||||
replace: "_hasBugReporterAccess:$1=true"
|
||||
},
|
||||
predicate: () => settings.store.toolbarDevMenu
|
||||
},
|
||||
|
@ -91,10 +92,18 @@ export default definePlugin({
|
|||
match: /\i\.isDM\(\)\|\|\i\.isThread\(\)/,
|
||||
replace: "false",
|
||||
}
|
||||
},
|
||||
// enable option to always record clips even if you are not streaming
|
||||
{
|
||||
find: "isDecoupledGameClippingEnabled(){",
|
||||
replacement: {
|
||||
match: /\i\.isStaff\(\)/,
|
||||
replace: "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
start: () => enableStyle(hideBugReport),
|
||||
start: () => !BugReporterExperiment.getCurrentConfig().hasBugReporterAccess && enableStyle(hideBugReport),
|
||||
stop: () => disableStyle(hideBugReport),
|
||||
|
||||
settingsAboutComponent: () => {
|
||||
|
|
|
@ -27,7 +27,6 @@ export default definePlugin({
|
|||
name: "FriendInvites",
|
||||
description: "Create and manage friend invite links via slash commands (/create friend invite, /view friend invites, /revoke friend invites).",
|
||||
authors: [Devs.afn, Devs.Dziurwa],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{
|
||||
name: "create friend invite",
|
||||
|
|
|
@ -105,6 +105,11 @@ for (const p of pluginsValues) if (isPluginEnabled(p.name)) {
|
|||
settings[d].enabled = true;
|
||||
dep.isDependency = true;
|
||||
});
|
||||
|
||||
if (p.commands?.length) {
|
||||
Plugins.CommandsAPI.isDependency = true;
|
||||
settings.CommandsAPI.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const p of pluginsValues) {
|
||||
|
|
|
@ -82,7 +82,6 @@ export default definePlugin({
|
|||
default: true
|
||||
}
|
||||
},
|
||||
dependencies: ["CommandsAPI"],
|
||||
|
||||
async start() {
|
||||
for (const tag of await getTags()) createTagCommand(tag);
|
||||
|
|
|
@ -33,7 +33,6 @@ export default definePlugin({
|
|||
name: "MoreCommands",
|
||||
description: "echo, lenny, mock",
|
||||
authors: [Devs.Arjix, Devs.echo, Devs.Samu],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{
|
||||
name: "echo",
|
||||
|
|
|
@ -24,7 +24,6 @@ export default definePlugin({
|
|||
name: "MoreKaomoji",
|
||||
description: "Adds more Kaomoji to discord. ヽ(´▽`)/",
|
||||
authors: [Devs.JacobTm],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{ name: "dissatisfaction", description: " >﹏<" },
|
||||
{ name: "smug", description: "ಠ_ಠ" },
|
||||
|
|
|
@ -88,7 +88,6 @@ export default definePlugin({
|
|||
name: "petpet",
|
||||
description: "Adds a /petpet slash command to create headpet gifs from any image",
|
||||
authors: [Devs.Ven],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
|
|
|
@ -91,7 +91,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".PANEL,isInteractionSource:",
|
||||
find: ".PANEL,interactionType:",
|
||||
replacement: {
|
||||
match: /{profileType:\i\.\i\.PANEL,children:\[/,
|
||||
replace: "$&$self.BiteSizeReviewsButton({user:arguments[0].user}),"
|
||||
|
|
|
@ -88,7 +88,7 @@ export default definePlugin({
|
|||
name: "SilentTyping",
|
||||
authors: [Devs.Ven, Devs.Rini, Devs.ImBanana],
|
||||
description: "Hide that you are typing",
|
||||
dependencies: ["CommandsAPI", "ChatInputButtonAPI"],
|
||||
dependencies: ["ChatInputButtonAPI"],
|
||||
settings,
|
||||
contextMenus: {
|
||||
"textarea-context": ChatBarContextCheckbox
|
||||
|
|
|
@ -76,7 +76,6 @@ export default definePlugin({
|
|||
name: "SpotifyShareCommands",
|
||||
description: "Share your current Spotify track, album or artist via slash command (/track, /album, /artist)",
|
||||
authors: [Devs.katlyn],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{
|
||||
name: "track",
|
||||
|
|
21
src/plugins/webhookManager.desktop/README.MD
Normal file
21
src/plugins/webhookManager.desktop/README.MD
Normal file
|
@ -0,0 +1,21 @@
|
|||
# WebhookManager
|
||||
Manage your webhooks easily; delete, send messages, get detailed info and more.
|
||||
|
||||
## Commands
|
||||
|
||||
- /webhook send - *Sends a message through a webhook.*
|
||||
- /webhook delete - *Deletes any webhook that is specified.*
|
||||
- /webhook info - *Gets advanced details on the webhook such as the name, profile pic, server and channel ID, and additionally, information on person who created it*
|
||||
|
||||
<details>
|
||||
<summary>Full /webhookinfo Output</summary>
|
||||
|
||||
- Webhook Username
|
||||
- Webhook ID
|
||||
- Webhook Token
|
||||
- Channel ID
|
||||
- Server ID
|
||||
- Webhook Profile Picture
|
||||
- Webhook Type
|
||||
- Creator Profile
|
||||
</details>
|
176
src/plugins/webhookManager.desktop/index.tsx
Normal file
176
src/plugins/webhookManager.desktop/index.tsx
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import definePlugin, { PluginNative } from "@utils/types";
|
||||
import { Button, Forms, React, Switch, TextInput, useState } from "@webpack/common";
|
||||
|
||||
const Native = VencordNative.pluginHelpers.WebhookManager as PluginNative<typeof import("./native")>;
|
||||
let url, content, username, avatarUrl = "";
|
||||
let jsonMode = false;
|
||||
|
||||
// TODO: add sending as raw
|
||||
function WebhookMessageModal(props: ModalProps) {
|
||||
const [params, setParams] = useState({ content: "", username: "", avatarUrl: "", url: "", jsonMode: false });
|
||||
|
||||
const onURL = (url: string) => setParams(prev => ({ ...prev, url }));
|
||||
const onContent = (content: string) => setParams(prev => ({ ...prev, content }));
|
||||
const onUsername = (username: string) => setParams(prev => ({ ...prev, username }));
|
||||
const onAvatar = (avatarUrl: string) => setParams(prev => ({ ...prev, avatarUrl }));
|
||||
const onSwitch = (jsonMode: boolean) => setParams(prev => ({ ...prev, jsonMode }));
|
||||
|
||||
|
||||
return <ModalRoot {...props} size={ModalSize.MEDIUM} className={"wm-send-webhook"} >
|
||||
<ModalContent className="wm-send-webhook-content">
|
||||
<Forms.FormTitle className={Margins.top20}>Webhook URL</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder={"Webhook URL"}
|
||||
value={params.url}
|
||||
onChange={onURL}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top20}>Webhook Message</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder={"Content"}
|
||||
value={params.content}
|
||||
onChange={onContent}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top20}>Webhook Username</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder={"Username"}
|
||||
value={params.username}
|
||||
onChange={onUsername}
|
||||
/>
|
||||
<Forms.FormTitle className={Margins.top20}>Webhook Avatar URL</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder={"Image URL"}
|
||||
value={params.avatarUrl}
|
||||
onChange={onAvatar}
|
||||
/>
|
||||
<Switch
|
||||
key="wm-raw"
|
||||
value={jsonMode}
|
||||
onChange={onSwitch}
|
||||
>Send as Raw JSON</Switch>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (jsonMode !== true) {
|
||||
Native.executeWebhook(params.url, {
|
||||
content: params.content,
|
||||
username: params.username,
|
||||
avatar_url: params.avatarUrl
|
||||
});
|
||||
}
|
||||
else {
|
||||
Native.executeWebhook(params.url, {
|
||||
webhookMessage: params.content,
|
||||
username: params.username,
|
||||
avatar_url: params.avatarUrl
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
||||
>Send Webhook</Button>
|
||||
</ModalContent>
|
||||
</ModalRoot >;
|
||||
}
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "WebhookManager",
|
||||
description: "Manage your webhooks easily; delete, send messages, get detailed info and more.",
|
||||
authors: [Devs.Byeoon, Devs.Ven],
|
||||
dependencies: ["CommandsAPI"],
|
||||
commands: [
|
||||
{
|
||||
name: "webhook delete",
|
||||
description: "Delete a webhook.",
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
options: [
|
||||
{
|
||||
name: "url",
|
||||
description: "The URL of the webhook",
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
execute: async (option, ctx) => {
|
||||
try {
|
||||
await fetch(findOption(option, "url", ""), {
|
||||
method: "DELETE"
|
||||
});
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: "The webhook has deleted successfully."
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: "There was an error deleting the webhook. Did you input a valid webhook URL? Error: " + error
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "webhook info",
|
||||
description: "Retrieve information about a webhook.",
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
options: [
|
||||
{
|
||||
name: "url",
|
||||
description: "The URL of the webhook",
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
execute: async (option, ctx) => {
|
||||
const webhookUrl = findOption(option, "url", "");
|
||||
const { user, avatar, name, id, token, type, channel_id, guild_id }
|
||||
= await fetch(webhookUrl).then(res => res.json());
|
||||
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `This webhook was created by ${user?.name}.`,
|
||||
embeds: [
|
||||
{
|
||||
title: "Webhook Information",
|
||||
color: "1323",
|
||||
// @ts-ignore
|
||||
author: {
|
||||
name,
|
||||
url: ""
|
||||
},
|
||||
thumbnail: {
|
||||
url: `https://cdn.discordapp.com/avatars/${id}/${avatar}.png`,
|
||||
proxyURL: `https://cdn.discordapp.com/avatars/${id}/${avatar}.png`,
|
||||
height: 128,
|
||||
width: 128
|
||||
},
|
||||
description: `
|
||||
Webhook ID: ${id}
|
||||
Webhook Token: ${token}
|
||||
Webhook Type: ${type}
|
||||
Channel ID: ${channel_id}
|
||||
Server ID: ${guild_id}
|
||||
`
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "webhook send",
|
||||
description: "Send a message through a webhook.",
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
async execute(_, ctx) {
|
||||
openModal(props => <WebhookMessageModal {...props} />);
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: "Your webhook message has been executed."
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
22
src/plugins/webhookManager.desktop/native.ts
Normal file
22
src/plugins/webhookManager.desktop/native.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { IpcMainInvokeEvent } from "electron";
|
||||
import https from "https";
|
||||
|
||||
const DiscordHosts = new Set(["discord.com", "ptb.discord.com", "canary.discord.com"]);
|
||||
|
||||
export function executeWebhook(_event: IpcMainInvokeEvent, url: string, body: object) {
|
||||
const { hostname, pathname } = new URL(url);
|
||||
|
||||
if (!DiscordHosts.has(hostname) || !pathname.startsWith("/api/webhooks/")) {
|
||||
throw new Error("This URL is not a valid webhook.");
|
||||
}
|
||||
|
||||
const req = https.request(url, { method: "POST", headers: { "Content-Type": "application/json", } });
|
||||
req.write(JSON.stringify(body));
|
||||
req.end();
|
||||
}
|
|
@ -475,7 +475,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "Sqaaakoi",
|
||||
id: 259558259491340288n
|
||||
},
|
||||
Byron: {
|
||||
Byeoon: {
|
||||
name: "byeoon",
|
||||
id: 1167275288036655133n
|
||||
},
|
||||
|
|
|
@ -72,13 +72,13 @@ export interface PluginDef {
|
|||
stop?(): void;
|
||||
patches?: Omit<Patch, "plugin">[];
|
||||
/**
|
||||
* List of commands. If you specify these, you must add CommandsAPI to dependencies
|
||||
* List of commands that your plugin wants to register
|
||||
*/
|
||||
commands?: Command[];
|
||||
/**
|
||||
* A list of other plugins that your plugin depends on.
|
||||
* These will automatically be enabled and loaded before your plugin
|
||||
* Common examples are CommandsAPI, MessageEventsAPI...
|
||||
* Generally these will be API plugins
|
||||
*/
|
||||
dependencies?: string[],
|
||||
/**
|
||||
|
|
|
@ -28,6 +28,8 @@ export let Forms = {} as {
|
|||
FormText: t.FormText,
|
||||
};
|
||||
|
||||
export let Icons = {} as t.Icons;
|
||||
|
||||
export let Card: t.Card;
|
||||
export let Button: t.Button;
|
||||
export let Switch: t.Switch;
|
||||
|
@ -85,4 +87,5 @@ waitFor(["FormItem", "Button"], m => {
|
|||
Heading
|
||||
} = m);
|
||||
Forms = m;
|
||||
Icons = m;
|
||||
});
|
||||
|
|
11
src/webpack/common/types/components.d.ts
vendored
11
src/webpack/common/types/components.d.ts
vendored
|
@ -18,6 +18,8 @@
|
|||
|
||||
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
||||
|
||||
import { IconNames } from "./iconNames";
|
||||
|
||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
|
||||
export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`;
|
||||
|
@ -69,7 +71,7 @@ export type FormText = ComponentType<PropsWithChildren<{
|
|||
}> & TextProps> & { Types: FormTextTypes; };
|
||||
|
||||
export type Tooltip = ComponentType<{
|
||||
text: ReactNode;
|
||||
text: ReactNode | ComponentType;
|
||||
children: FunctionComponent<{
|
||||
onClick(): void;
|
||||
onMouseEnter(): void;
|
||||
|
@ -502,3 +504,10 @@ export type Avatar = ComponentType<PropsWithChildren<{
|
|||
type FocusLock = ComponentType<PropsWithChildren<{
|
||||
containerRef: RefObject<HTMLElement>;
|
||||
}>>;
|
||||
|
||||
export type Icon = ComponentType<JSX.IntrinsicElements["svg"] & {
|
||||
size?: string;
|
||||
colorClass?: string;
|
||||
} & Record<string, any>>;
|
||||
|
||||
export type Icons = Record<IconNames, Icon>;
|
||||
|
|
14
src/webpack/common/types/iconNames.d.ts
vendored
Normal file
14
src/webpack/common/types/iconNames.d.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
5
src/webpack/common/types/menu.d.ts
vendored
5
src/webpack/common/types/menu.d.ts
vendored
|
@ -72,6 +72,11 @@ export interface Menu {
|
|||
onChange(value: number): void,
|
||||
renderValue?(value: number): string,
|
||||
}>;
|
||||
MenuSearchControl: RC<{
|
||||
query: string
|
||||
onChange(query: string): void;
|
||||
placeholder?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ContextMenuApi {
|
||||
|
|
Loading…
Reference in a new issue