Some Merges
This commit is contained in:
parent
bb071250f6
commit
4411fabdd5
29 changed files with 813 additions and 847 deletions
|
|
@ -1,21 +0,0 @@
|
|||
## vencord-animalese
|
||||
animalese yapping in your discord client
|
||||
|
||||
> [!WARNING]
|
||||
> don't go to Vencord support if you can't install this.
|
||||
|
||||
### demo
|
||||
|
||||
https://github.com/user-attachments/assets/3f1fa925-bbd2-451d-b63e-ea14dbe4be44
|
||||
|
||||
### settings
|
||||
|
||||
##### settings used in the video
|
||||

|
||||
|
||||
|
||||
you can change the settings in the settings but otherwise, enjoy the yap
|
||||
|
||||
### credits
|
||||
|
||||
- [sounds: animalese-generator by equalo-official](https://github.com/equalo-official/animalese-generator/tree/master)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# Demonstration
|
||||
|
||||
Helps for taking theme screenshots
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Button, Forms, Switch, Text } from "@webpack/common";
|
||||
|
||||
// definitely not stolen from glide :P
|
||||
async function injectCSS() {
|
||||
var elementToRemove = document.getElementById("DemonstrationStyle");
|
||||
if (elementToRemove) {
|
||||
elementToRemove.remove();
|
||||
}
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.id = "DemonstrationStyle";
|
||||
const content = await fetch("https://minidiscordthemes.github.io/Demonstration/Demonstration.theme.css").then(e => e.text());
|
||||
styleElement.textContent = content;
|
||||
document.documentElement.appendChild(styleElement);
|
||||
}
|
||||
|
||||
const validKeycodes = [
|
||||
"Backspace", "Tab", "Enter", "ShiftLeft", "ShiftRight", "ControlLeft", "ControlRight", "AltLeft", "AltRight", "Pause", "CapsLock",
|
||||
"Escape", "Space", "PageUp", "PageDown", "End", "Home", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "PrintScreen", "Insert",
|
||||
"Delete", "Digit0", "Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9", "KeyA", "KeyB", "KeyC",
|
||||
"KeyD", "KeyE", "KeyF", "KeyG", "KeyH", "KeyI", "KeyJ", "KeyK", "KeyL", "KeyM", "KeyN", "KeyO", "KeyP", "KeyQ", "KeyR", "KeyS", "KeyT",
|
||||
"KeyU", "KeyV", "KeyW", "KeyX", "KeyY", "KeyZ", "MetaLeft", "MetaRight", "ContextMenu", "Numpad0", "Numpad1", "Numpad2", "Numpad3",
|
||||
"Numpad4", "Numpad5", "Numpad6", "Numpad7", "Numpad8", "Numpad9", "NumpadMultiply", "NumpadAdd", "NumpadSubtract", "NumpadDecimal",
|
||||
"NumpadDivide", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "NumLock", "ScrollLock"
|
||||
];
|
||||
|
||||
const settings = definePluginSettings({
|
||||
keyBind: {
|
||||
description: "The key to toggle the theme when pressed",
|
||||
type: OptionType.STRING,
|
||||
default: "F6",
|
||||
isValid: (value: string) => {
|
||||
if (validKeycodes.includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
soundVolume: {
|
||||
description: "How loud the toggle sound is (0 to disable)",
|
||||
type: OptionType.SLIDER,
|
||||
default: 0.5,
|
||||
markers: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
|
||||
},
|
||||
showConfirmationModal: {
|
||||
description: "Show modal to remind shortcut",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
}
|
||||
});
|
||||
|
||||
function ToggleModal() {
|
||||
const value = !settings.use(["showConfirmationModal"]).showConfirmationModal;
|
||||
return (
|
||||
<Switch
|
||||
note="You can re-enable this setting later"
|
||||
value={value}
|
||||
onChange={v => { settings.store.showConfirmationModal = !v; }}>
|
||||
Disable modal?
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
function handleToggle() {
|
||||
const style = document.getElementById("DemonstrationStyle");
|
||||
if (style != null) {
|
||||
style.remove();
|
||||
playSound("https://github.com/Equicord/Equibored/raw/main/sounds/demonstration/wp5rpz.wav");
|
||||
}
|
||||
else {
|
||||
if (settings.store.showConfirmationModal) {
|
||||
const cl = classNameFactory("vc-demonstration-modal");
|
||||
|
||||
const key = openModal(props => (
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader className={cl("header")}>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }} >Demonstration</Text>
|
||||
<ModalCloseButton onClick={() => closeModal(key)}></ModalCloseButton>
|
||||
</ModalHeader>
|
||||
<ModalContent className={cl("content")}>
|
||||
<Forms.FormText>
|
||||
This will censor all text! To disable this, remember the shortcut:
|
||||
</Forms.FormText>
|
||||
<Text variant="heading-xl/bold" style={{ textAlign: "center", width: "100%", paddingTop: "20px", paddingBottom: "20px" }}>
|
||||
{settings.store.keyBind}
|
||||
</Text>
|
||||
<ToggleModal />
|
||||
</ModalContent>
|
||||
<Button
|
||||
onClick={() => {
|
||||
closeModal(key);
|
||||
injectCSS();
|
||||
playSound("https://github.com/Equicord/Equibored/raw/main/sounds/demonstration/ckz46t.wav");
|
||||
}}
|
||||
>Okay!</Button>
|
||||
</ModalRoot>
|
||||
));
|
||||
} else {
|
||||
injectCSS();
|
||||
playSound("https://github.com/Equicord/Equibored/raw/main/sounds/demonstration/ckz46t.wav");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.code !== settings.store.keyBind) { return; }
|
||||
handleToggle();
|
||||
}
|
||||
|
||||
async function playSound(url: string) {
|
||||
const audio = new Audio(url);
|
||||
audio.volume = settings.store.soundVolume;
|
||||
await audio.play().catch(error => {
|
||||
console.error("Error playing sound:", error);
|
||||
});
|
||||
audio.remove();
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "Demonstration",
|
||||
description: "Plugin for taking theme screenshots - censors identifying images and text.",
|
||||
authors: [Devs.Samwich, EquicordDevs.Panniku],
|
||||
settings,
|
||||
toolboxActions: {
|
||||
"Toggle Demonstration": (() => handleToggle())
|
||||
},
|
||||
settingsAboutComponent: () => {
|
||||
return (
|
||||
<>
|
||||
<Text>To change your keycode, check out <a href="https://www.toptal.com/developers/keycode" target="_blank" rel="noreferrer noopener">this tool</a>!</Text>
|
||||
</>
|
||||
);
|
||||
},
|
||||
start() {
|
||||
document.addEventListener("keydown", handleKeydown);
|
||||
},
|
||||
stop() {
|
||||
document.removeEventListener("keydown", handleKeydown);
|
||||
}
|
||||
});
|
||||
|
|
@ -18,19 +18,27 @@
|
|||
|
||||
import "@equicordplugins/_misc/styles.css";
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { Devs, EQUICORD_HELPERS, EquicordDevs, GUILD_ID } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin from "@utils/types";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Button, ChannelStore, Flex, Forms, GuildMemberStore, showToast, Toasts } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { toggleEnabled } from "./utils";
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
noMirroredCamera: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Prevents the camera from being mirrored on your screen",
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "EquicordHelper",
|
||||
description: "Fixes some misc issues with discord",
|
||||
authors: [Devs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu],
|
||||
authors: [Devs.thororen, Devs.nyx, EquicordDevs.Naibuu],
|
||||
settingsAboutComponent: () => <>
|
||||
<Forms.FormText className="plugin-warning" style={{ textAlign: "left" }}>
|
||||
This plugin was created to allow us as the Equicord Team & Contributors
|
||||
|
|
@ -58,8 +66,41 @@ export default definePlugin({
|
|||
replace: "return $1;"
|
||||
}
|
||||
]
|
||||
},
|
||||
// When focused on voice channel or group chat voice call
|
||||
{
|
||||
find: /\i\?\i.\i.SELF_VIDEO/,
|
||||
replacement: {
|
||||
match: /mirror:\i/,
|
||||
replace: "mirror:!1"
|
||||
},
|
||||
predicate: () => settings.store.noMirroredCamera
|
||||
},
|
||||
// Popout camera when not focused on voice channel
|
||||
{
|
||||
find: ".mirror]:",
|
||||
replacement: {
|
||||
match: /\[(\i).mirror]:\i/,
|
||||
replace: "[$1.mirror]:!1"
|
||||
},
|
||||
predicate: () => settings.store.noMirroredCamera
|
||||
},
|
||||
// Overriding css on Preview Camera/Change Video Background popup
|
||||
{
|
||||
find: ".cameraPreview,",
|
||||
replacement: {
|
||||
match: /className:\i.camera,/,
|
||||
replace: "$&style:{transform: \"scalex(1)\"},"
|
||||
},
|
||||
predicate: () => settings.store.noMirroredCamera
|
||||
}
|
||||
],
|
||||
start() {
|
||||
if (Settings.plugins?.NoMirroredCamera?.enabled) {
|
||||
Settings.plugins.NoMirroredCamera.enabled = false;
|
||||
settings.store.noMirroredCamera = true;
|
||||
}
|
||||
},
|
||||
renderMessageAccessory(props) {
|
||||
const buttons = [] as JSX.Element[];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
# Equissant
|
||||
|
||||
Plays crossant every 10 clicks
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and Megumin
|
||||
*
|
||||
* 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 { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
let clickCount = 0;
|
||||
|
||||
function play() {
|
||||
clickCount++;
|
||||
|
||||
if (clickCount % 10 === 0) {
|
||||
const audio = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/equissant/croissant.mp3");
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "Equissant",
|
||||
description: "Crossant every 10 clicks :trolley:",
|
||||
authors: [EquicordDevs.SomeAspy, Devs.thororen],
|
||||
start() {
|
||||
document.addEventListener("click", play);
|
||||
},
|
||||
stop() {
|
||||
document.removeEventListener("click", play);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# ExportContacts
|
||||
|
||||
Exports your friends list onto your clipboard
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2024 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 "./styles.css";
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Toasts } from "@webpack/common";
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
discriminator: string;
|
||||
publicFlags: number;
|
||||
avatarDecorationData?: any;
|
||||
globalName: string;
|
||||
}
|
||||
|
||||
interface ContactsList {
|
||||
id: string;
|
||||
type: number;
|
||||
nickname?: any;
|
||||
user: User;
|
||||
since: string;
|
||||
}
|
||||
|
||||
// for type parameter, it takes in a number that determines the type of the contact
|
||||
// 1 is friends added
|
||||
// 2 is blocked users
|
||||
// 3 is incoming friend requests
|
||||
// 4 is outgoing friend requests
|
||||
function getUsernames(contacts: ContactsList[], type: number): string[] {
|
||||
return contacts
|
||||
// only select contacts that are the specified type
|
||||
.filter(e => e.type === type)
|
||||
// return the username, and discriminator if necessary
|
||||
.map(e => e.user.discriminator === "0" ? e.user.username : e.user.username + "#" + e.user.discriminator);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "ExportContacts",
|
||||
description: "Export a list of friends to your clipboard. Adds a new button to the menu bar for the friends tab.",
|
||||
authors: [EquicordDevs.dat_insanity],
|
||||
patches: [
|
||||
{
|
||||
find: "fetchRelationships(){",
|
||||
replacement: {
|
||||
match: /(\.then\(\i)=>(\i\.\i\.dispatch\({type:"LOAD_RELATIONSHIPS_SUCCESS",relationships:(\i\.body)}\))/,
|
||||
replace: "$1=>{$2; $self.getContacts($3)}"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "[role=\"tab\"][aria-disabled=\"false\"]",
|
||||
replacement: {
|
||||
match: /("aria-label":(\i).{0,25})(\i)\.Children\.map\((\i),this\.renderChildren\)/,
|
||||
replace:
|
||||
"$1($3 && $3.Children" +
|
||||
"? ($2 === 'Friends'" +
|
||||
"? [...$3.Children.map($4, this.renderChildren), $self.addExportButton()]" +
|
||||
": [...$3.Children.map($4, this.renderChildren)])" +
|
||||
": $3.map($4, this.renderChildren))"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
getContacts(contacts: ContactsList[]) {
|
||||
this.contactList = {
|
||||
friendsAdded: [...getUsernames(contacts, 1)],
|
||||
blockedUsers: [...getUsernames(contacts, 2)],
|
||||
incomingFriendRequests: [...getUsernames(contacts, 3)],
|
||||
outgoingFriendRequests: [...getUsernames(contacts, 4)]
|
||||
};
|
||||
},
|
||||
|
||||
addExportButton() {
|
||||
return <ErrorBoundary noop key=".2">
|
||||
<button className="export-contacts-button" onClick={() => { this.copyContactToClipboard(); console.log("clicked"); }}>Export</button>
|
||||
</ErrorBoundary>;
|
||||
},
|
||||
|
||||
copyContactToClipboard() {
|
||||
if (this.contactList) {
|
||||
copyToClipboard(JSON.stringify(this.contactList));
|
||||
Toasts.show({
|
||||
message: "Contacts copied to clipboard successfully.",
|
||||
type: Toasts.Type.SUCCESS,
|
||||
id: Toasts.genId(),
|
||||
options: {
|
||||
duration: 3000,
|
||||
position: Toasts.Position.BOTTOM
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// reason why you need to click the all tab is because the data is extracted during
|
||||
// the request itself when you fetch all your friends. this is done to avoid sending a
|
||||
// manual request to discord, which may raise suspicion and might even get you terminated.
|
||||
Toasts.show({
|
||||
message: "Contact list is undefined. Click on the \"All\" tab before exporting.",
|
||||
type: Toasts.Type.FAILURE,
|
||||
id: Toasts.genId(),
|
||||
options: {
|
||||
duration: 3000,
|
||||
position: Toasts.Position.BOTTOM
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -4,20 +4,31 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { saveFile } from "@utils/web";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { Menu } from "@webpack/common";
|
||||
import { Menu, Toasts } from "@webpack/common";
|
||||
|
||||
import { ContactsList } from "./types";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
openFileAfterExport: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Open the exported file in the default file handler after export",
|
||||
default: true
|
||||
},
|
||||
exportContacts: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Export a list of friends to your clipboard. Adds a new button to the menu bar for the friends tab.",
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -102,12 +113,92 @@ const messageContextMenuPatch = (children: Array<React.ReactElement<any> | null>
|
|||
);
|
||||
};
|
||||
|
||||
// for type parameter, it takes in a number that determines the type of the contact
|
||||
// 1 is friends added
|
||||
// 2 is blocked users
|
||||
// 3 is incoming friend requests
|
||||
// 4 is outgoing friend requests
|
||||
function getUsernames(contacts: ContactsList[], type: number): string[] {
|
||||
return contacts
|
||||
// only select contacts that are the specified type
|
||||
.filter(e => e.type === type)
|
||||
// return the username, and discriminator if necessary
|
||||
.map(e => e.user.discriminator === "0" ? e.user.username : e.user.username + "#" + e.user.discriminator);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "ExportMessages",
|
||||
description: "Allows you to export any message to a file",
|
||||
authors: [EquicordDevs.veygax],
|
||||
authors: [EquicordDevs.veygax, EquicordDevs.dat_insanity],
|
||||
settings,
|
||||
contextMenus: {
|
||||
"message": messageContextMenuPatch
|
||||
},
|
||||
patches: [
|
||||
{
|
||||
find: "fetchRelationships(){",
|
||||
replacement: {
|
||||
match: /(\.then\(\i)=>(\i\.\i\.dispatch\({type:"LOAD_RELATIONSHIPS_SUCCESS",relationships:(\i\.body)}\))/,
|
||||
replace: "$1=>{$2; $self.getContacts($3)}"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "[role=\"tab\"][aria-disabled=\"false\"]",
|
||||
replacement: {
|
||||
match: /("aria-label":(\i).{0,25})(\i)\.Children\.map\((\i),this\.renderChildren\)/,
|
||||
replace:
|
||||
"$1($3 && $3.Children" +
|
||||
"? ($2 === 'Friends'" +
|
||||
"? [...$3.Children.map($4, this.renderChildren), $self.addExportButton()]" +
|
||||
": [...$3.Children.map($4, this.renderChildren)])" +
|
||||
": $3.map($4, this.renderChildren))"
|
||||
}
|
||||
}
|
||||
],
|
||||
start() {
|
||||
if (Settings.plugins?.ExportContacts?.enabled) {
|
||||
Settings.plugins.ExportContacts.enabled = false;
|
||||
settings.store.exportContacts = true;
|
||||
}
|
||||
},
|
||||
getContacts(contacts: ContactsList[]) {
|
||||
this.contactList = {
|
||||
friendsAdded: [...getUsernames(contacts, 1)],
|
||||
blockedUsers: [...getUsernames(contacts, 2)],
|
||||
incomingFriendRequests: [...getUsernames(contacts, 3)],
|
||||
outgoingFriendRequests: [...getUsernames(contacts, 4)]
|
||||
};
|
||||
},
|
||||
addExportButton() {
|
||||
return <ErrorBoundary noop key=".2">
|
||||
<button className="export-contacts-button" onClick={() => { this.copyContactToClipboard(); console.log("clicked"); }}>Export</button>
|
||||
</ErrorBoundary>;
|
||||
},
|
||||
copyContactToClipboard() {
|
||||
if (this.contactList) {
|
||||
copyToClipboard(JSON.stringify(this.contactList));
|
||||
Toasts.show({
|
||||
message: "Contacts copied to clipboard successfully.",
|
||||
type: Toasts.Type.SUCCESS,
|
||||
id: Toasts.genId(),
|
||||
options: {
|
||||
duration: 3000,
|
||||
position: Toasts.Position.BOTTOM
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// reason why you need to click the all tab is because the data is extracted during
|
||||
// the request itself when you fetch all your friends. this is done to avoid sending a
|
||||
// manual request to discord, which may raise suspicion and might even get you terminated.
|
||||
Toasts.show({
|
||||
message: "Contact list is undefined. Click on the \"All\" tab before exporting.",
|
||||
type: Toasts.Type.FAILURE,
|
||||
id: Toasts.genId(),
|
||||
options: {
|
||||
duration: 3000,
|
||||
position: Toasts.Position.BOTTOM
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@
|
|||
|
||||
.export-contacts-button:hover {
|
||||
background-color: #454fbf;
|
||||
}
|
||||
}
|
||||
23
src/equicordplugins/exportMessages/types.ts
Normal file
23
src/equicordplugins/exportMessages/types.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
discriminator: string;
|
||||
publicFlags: number;
|
||||
avatarDecorationData?: any;
|
||||
globalName: string;
|
||||
}
|
||||
|
||||
export interface ContactsList {
|
||||
id: string;
|
||||
type: number;
|
||||
nickname?: any;
|
||||
user: User;
|
||||
since: string;
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
let click1, click2, click3, backspace;
|
||||
let sounds = {
|
||||
click1,
|
||||
click2,
|
||||
click3,
|
||||
backspace
|
||||
};
|
||||
|
||||
const ignoredKeys = ["CapsLock", "ShiftLeft", "ShiftRight", "ControlLeft", "ControlRight", "AltLeft", "AltRight", "MetaLeft", "MetaRight", "ArrowUp", "ArrowRight", "ArrowLeft", "ArrowDown", "MediaPlayPause", "MediaStop", "MediaTrackNext", "MediaTrackPrevious", "MediaSelect", "MediaEject", "MediaVolumeUp", "MediaVolumeDown", "AudioVolumeUp", "AudioVolumeDown"];
|
||||
|
||||
const keydown = (e: KeyboardEvent) => {
|
||||
if (ignoredKeys.includes(e.code)) return;
|
||||
for (const sound of Object.values(sounds)) sound.pause();
|
||||
if (e.code === "Backspace") {
|
||||
sounds.backspace.currentTime = 0;
|
||||
sounds.backspace.play();
|
||||
} else {
|
||||
const click = sounds[`click${Math.floor(Math.random() * 3) + 1}`];
|
||||
click.currentTime = 0;
|
||||
click.play();
|
||||
}
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "KeyboardSounds",
|
||||
description: "Adds the Opera GX Keyboard Sounds to Discord",
|
||||
authors: [Devs.HypedDomi],
|
||||
start: () => {
|
||||
click1 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click1.wav");
|
||||
click2 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click2.wav");
|
||||
click3 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click3.wav");
|
||||
backspace = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/backspace.wav");
|
||||
sounds = {
|
||||
click1,
|
||||
click2,
|
||||
click3,
|
||||
backspace,
|
||||
};
|
||||
document.addEventListener("keydown", keydown);
|
||||
},
|
||||
stop: () => document.removeEventListener("keydown", keydown),
|
||||
options: {
|
||||
volume: {
|
||||
description: "Volume",
|
||||
type: OptionType.SLIDER,
|
||||
markers: [0, 100],
|
||||
stickToMarkers: false,
|
||||
default: 100,
|
||||
onChange: value => { for (const sound of Object.values(sounds)) sound.volume = value / 100; }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, OptionalMessageOption, RequiredMessageOption, sendBotMessage } from "@api/Commands";
|
||||
import { sendMessage } from "@utils/discord";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
||||
import { fromMorse, getCuteAnimeBoys, getCuteNeko, getCutePats, isMorse, makeFreaky, mock, toMorse } from "../utils";
|
||||
import { sendMessage } from "@utils/discord";
|
||||
import { fromMorse, getCuteAnimeBoys, getCuteNeko, getCutePats, isMorse, makeFreaky, mock, toMorse, uwuify } from "../utils";
|
||||
|
||||
export default [
|
||||
{
|
||||
|
|
@ -130,5 +130,14 @@ export default [
|
|||
content: `${output}`
|
||||
};
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "uwuify",
|
||||
description: "uwuifies your messages",
|
||||
options: [RequiredMessageOption],
|
||||
|
||||
execute: opts => ({
|
||||
content: uwuify(findOption(opts, "message", "")),
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { addMessagePreEditListener, addMessagePreSendListener, MessageObject, removeMessagePreEditListener, removeMessagePreSendListener } from "@api/MessageEvents";
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
|
@ -25,13 +26,13 @@ import misc from "./commands/misc";
|
|||
import system from "./commands/system";
|
||||
import text from "./commands/text";
|
||||
import time from "./commands/time";
|
||||
import { settings } from "./utils";
|
||||
import { settings, uwuify, uwuifyArray } from "./utils";
|
||||
|
||||
migratePluginSettings("MoreCommands", "CuteAnimeBoys", "CuteNekos", "CutePats", "Slap");
|
||||
export default definePlugin({
|
||||
name: "MoreCommands",
|
||||
description: "Adds various fun and useful commands",
|
||||
authors: [Devs.Arjix, Devs.amy, Devs.Samu, EquicordDevs.zyqunix, EquicordDevs.ShadyGoat, Devs.thororen, Devs.Korbo],
|
||||
authors: [Devs.Arjix, Devs.amy, Devs.Samu, EquicordDevs.zyqunix, EquicordDevs.ShadyGoat, Devs.thororen, Devs.Korbo, Devs.nyx, Devs.amy],
|
||||
settings,
|
||||
commands: [
|
||||
...choice,
|
||||
|
|
@ -39,5 +40,40 @@ export default definePlugin({
|
|||
...text,
|
||||
...time,
|
||||
...misc,
|
||||
]
|
||||
],
|
||||
patches: [
|
||||
{
|
||||
find: ".isPureReactComponent=!0;",
|
||||
predicate: () => settings.store.uwuEverything && settings.store.uwuify,
|
||||
replacement: {
|
||||
match: /(?<=.defaultProps\)void 0.{0,60})(\i)\)/,
|
||||
replace: "$self.uwuifyProps($1))"
|
||||
}
|
||||
}
|
||||
],
|
||||
uwuifyProps(props: any) {
|
||||
if (!props.children) return props;
|
||||
if (typeof props.children === "string") props.children = uwuify(props.children);
|
||||
else if (Array.isArray(props.children)) props.children = uwuifyArray(props.children);
|
||||
return props;
|
||||
},
|
||||
|
||||
onSend(msg: MessageObject) {
|
||||
// Only run when it's enabled
|
||||
if (settings.store.uwuEveryMessage && settings.store.uwuify) {
|
||||
msg.content = uwuify(msg.content);
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
this.preSend = addMessagePreSendListener((_, msg) => this.onSend(msg));
|
||||
this.preEdit = addMessagePreEditListener((_cid, _mid, msg) =>
|
||||
this.onSend(msg)
|
||||
);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removeMessagePreSendListener(this.preSend);
|
||||
removeMessagePreEditListener(this.preEdit);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,18 @@ export const settings = definePluginSettings({
|
|||
type: OptionType.BOOLEAN,
|
||||
description: "Add 👅 or ❤️ at the end",
|
||||
default: true
|
||||
},
|
||||
uwuEveryMessage: {
|
||||
description: "Make every single message uwuified",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
restartNeeded: false
|
||||
},
|
||||
uwuEverything: {
|
||||
description: "Makes *all* text uwuified - really bad idea",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
restartNeeded: true
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -87,3 +99,112 @@ export const fromMorse = (text: string) => {
|
|||
};
|
||||
|
||||
export const isMorse = (text: string) => /^[.\-/ ]+$/.test(text);
|
||||
|
||||
export const endings = [
|
||||
"rawr x3",
|
||||
"OwO",
|
||||
"UwU",
|
||||
"o.O",
|
||||
"-.-",
|
||||
">w<",
|
||||
"(⑅˘꒳˘)",
|
||||
"(ꈍᴗꈍ)",
|
||||
"(˘ω˘)",
|
||||
"(U ᵕ U❁)",
|
||||
"σωσ",
|
||||
"òωó",
|
||||
"(///ˬ///✿)",
|
||||
"(U ﹏ U)",
|
||||
"( ͡o ω ͡o )",
|
||||
"ʘwʘ",
|
||||
":3",
|
||||
":3", // important enough to have twice
|
||||
":3", // important enough to have thrice
|
||||
"XD",
|
||||
"nyaa~~",
|
||||
"mya",
|
||||
">_<",
|
||||
"😳",
|
||||
"🥺",
|
||||
"😳😳😳",
|
||||
"rawr",
|
||||
"^^",
|
||||
"^^;;",
|
||||
"(ˆ ﻌ ˆ)♡",
|
||||
"^•ﻌ•^",
|
||||
"/(^•ω•^)",
|
||||
"(✿oωo)"
|
||||
];
|
||||
|
||||
export const replacements = [
|
||||
["small", "smol"],
|
||||
["cute", "kawaii"],
|
||||
["fluff", "floof"],
|
||||
["love", "luv"],
|
||||
["stupid", "baka"],
|
||||
["what", "nani"],
|
||||
["meow", "nya"],
|
||||
["hello", "hewwo"],
|
||||
];
|
||||
|
||||
export function selectRandomElement(arr) {
|
||||
// generate a random index based on the length of the array
|
||||
const randomIndex = Math.floor(Math.random() * arr.length);
|
||||
|
||||
// return the element at the randomly generated index
|
||||
return arr[randomIndex];
|
||||
}
|
||||
export const isOneCharacterString = (str: string): boolean => {
|
||||
return str.split("").every((char: string) => char === str[0]);
|
||||
};
|
||||
|
||||
export function replaceString(inputString) {
|
||||
let replaced = false;
|
||||
for (const replacement of replacements) {
|
||||
const regex = new RegExp(`\\b${replacement[0]}\\b`, "gi");
|
||||
if (regex.test(inputString)) {
|
||||
inputString = inputString.replace(regex, replacement[1]);
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
return replaced ? inputString : false;
|
||||
}
|
||||
|
||||
export function uwuify(message: string): string {
|
||||
const rule = /\S+|\s+/g;
|
||||
const words: string[] | null = message.match(rule);
|
||||
let answer = "";
|
||||
|
||||
if (words === null) return "";
|
||||
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
if (isOneCharacterString(words[i]) || words[i].startsWith("https://")) {
|
||||
answer += words[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!replaceString(words[i])) {
|
||||
answer += words[i]
|
||||
.replace(/n(?=[aeo])/g, "ny")
|
||||
.replace(/l|r/g, "w");
|
||||
} else answer += replaceString(words[i]);
|
||||
|
||||
}
|
||||
|
||||
answer += " " + selectRandomElement(endings);
|
||||
return answer;
|
||||
}
|
||||
|
||||
export function uwuifyArray(arr) {
|
||||
const newArr = [...arr];
|
||||
|
||||
newArr.forEach((item, index) => {
|
||||
if (Array.isArray(item)) {
|
||||
newArr[index] = uwuifyArray(item);
|
||||
} else if (typeof item === "string") {
|
||||
newArr[index] = uwuify(item);
|
||||
}
|
||||
});
|
||||
|
||||
return newArr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { sleep } from "@utils/misc";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { Message, ReactionEmoji } from "@vencord/discord-types";
|
||||
import { RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
|
||||
interface IMessageCreate {
|
||||
type: "MESSAGE_CREATE";
|
||||
optimistic: boolean;
|
||||
isPushNotification: boolean;
|
||||
channelId: string;
|
||||
message: Message;
|
||||
}
|
||||
|
||||
interface IReactionAdd {
|
||||
type: "MESSAGE_REACTION_ADD";
|
||||
optimistic: boolean;
|
||||
channelId: string;
|
||||
messageId: string;
|
||||
messageAuthorId: string;
|
||||
userId: "195136840355807232";
|
||||
emoji: ReactionEmoji;
|
||||
}
|
||||
|
||||
interface IVoiceChannelEffectSendEvent {
|
||||
type: string;
|
||||
emoji?: ReactionEmoji; // Just in case...
|
||||
channelId: string;
|
||||
userId: string;
|
||||
animationType: number;
|
||||
animationId: number;
|
||||
}
|
||||
|
||||
const MOYAI = "🗿";
|
||||
const MOYAI_URL = "https://github.com/Equicord/Equibored/raw/main/sounds/moyai/moyai.mp3";
|
||||
const MOYAI_URL_HD = "https://github.com/Equicord/Equibored/raw/main/sounds/moyai/moyai.wav";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
volume: {
|
||||
description: "Volume of the 🗿🗿🗿",
|
||||
type: OptionType.SLIDER,
|
||||
markers: makeRange(0, 1, 0.1),
|
||||
default: 0.5,
|
||||
stickToMarkers: false
|
||||
},
|
||||
quality: {
|
||||
description: "Quality of the 🗿🗿🗿",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{ label: "Normal", value: "Normal", default: true },
|
||||
{ label: "HD", value: "HD" }
|
||||
],
|
||||
},
|
||||
triggerWhenUnfocused: {
|
||||
description: "Trigger the 🗿 even when the window is unfocused",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBots: {
|
||||
description: "Ignore bots",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBlocked: {
|
||||
description: "Ignore blocked users",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "Moyai",
|
||||
authors: [Devs.Megu, Devs.Nuckyz],
|
||||
description: "🗿🗿🗿🗿🗿🗿🗿🗿",
|
||||
settings,
|
||||
|
||||
flux: {
|
||||
async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) {
|
||||
if (optimistic || type !== "MESSAGE_CREATE") return;
|
||||
if (message.state === "SENDING") return;
|
||||
if (settings.store.ignoreBots && message.author?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(message.author?.id)) return;
|
||||
if (!message.content) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
const moyaiCount = getMoyaiCount(message.content);
|
||||
|
||||
for (let i = 0; i < moyaiCount; i++) {
|
||||
boom();
|
||||
await sleep(300);
|
||||
}
|
||||
},
|
||||
|
||||
MESSAGE_REACTION_ADD({ optimistic, type, channelId, userId, messageAuthorId, emoji }: IReactionAdd) {
|
||||
if (optimistic || type !== "MESSAGE_REACTION_ADD") return;
|
||||
if (settings.store.ignoreBots && UserStore.getUser(userId)?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(messageAuthorId)) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
},
|
||||
|
||||
VOICE_CHANNEL_EFFECT_SEND({ emoji }: IVoiceChannelEffectSendEvent) {
|
||||
if (!emoji?.name) return;
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function countOccurrences(sourceString: string, subString: string) {
|
||||
let i = 0;
|
||||
let lastIdx = 0;
|
||||
while ((lastIdx = sourceString.indexOf(subString, lastIdx) + 1) !== 0)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
function countMatches(sourceString: string, pattern: RegExp) {
|
||||
if (!pattern.global)
|
||||
throw new Error("pattern must be global");
|
||||
|
||||
let i = 0;
|
||||
while (pattern.test(sourceString))
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
const customMoyaiRe = /<a?:\w*moy?ai\w*:\d{17,20}>/gi;
|
||||
|
||||
function getMoyaiCount(message: string) {
|
||||
const count = countOccurrences(message, MOYAI)
|
||||
+ countMatches(message, customMoyaiRe);
|
||||
|
||||
return Math.min(count, 10);
|
||||
}
|
||||
|
||||
function boom() {
|
||||
if (!settings.store.triggerWhenUnfocused && !document.hasFocus()) return;
|
||||
const audioElement = document.createElement("audio");
|
||||
|
||||
audioElement.src = settings.store.quality === "HD"
|
||||
? MOYAI_URL_HD
|
||||
: MOYAI_URL;
|
||||
|
||||
audioElement.volume = settings.store.volume;
|
||||
audioElement.play();
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, GuildStore } from "@webpack/common";
|
||||
const DeleteGuild = findByPropsLazy("deleteGuild", "sendTransferOwnershipPincode").deleteGuild;
|
||||
|
||||
function GetPropsAndDeleteGuild(id) {
|
||||
const GotGuild = GuildStore.getGuild(id);
|
||||
if (!GotGuild) return;
|
||||
|
||||
DeleteGuild(id, GotGuild.name);
|
||||
}
|
||||
|
||||
const settings = definePluginSettings(
|
||||
{
|
||||
confirmModal: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Should a \"are you sure you want to delete\" modal be shown?",
|
||||
default: true
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "NoDeleteSafety",
|
||||
description: "Removes the \"enter server name\" requirement when deleting a server",
|
||||
authors: [Devs.Samwich],
|
||||
settings,
|
||||
async HandleGuildDeleteModal(server) {
|
||||
if (settings.store.confirmModal) {
|
||||
return Alerts.show({ title: "Delete server?", body: <p>It's permanent, if that wasn't obvious.</p>, confirmColor: Button.Colors.RED, confirmText: "Delete", onConfirm: () => GetPropsAndDeleteGuild(server.id), cancelText: "Cancel" });
|
||||
} else {
|
||||
return GetPropsAndDeleteGuild(server.id);
|
||||
}
|
||||
},
|
||||
patches: [
|
||||
{
|
||||
find: ".DELETE,onClick(){let",
|
||||
replacement: {
|
||||
match: /let\{name:\i\}=\i\.guild/,
|
||||
replace: "$self.HandleGuildDeleteModal($1);$&"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "NoMirroredCamera",
|
||||
description: "Prevents the camera from being mirrored on your screen",
|
||||
authors: [Devs.nyx],
|
||||
|
||||
patches: [
|
||||
// When focused on voice channel or group chat voice call
|
||||
{
|
||||
find: /\i\?\i.\i.SELF_VIDEO/,
|
||||
replacement: {
|
||||
match: /mirror:\i/,
|
||||
replace: "mirror:!1"
|
||||
}
|
||||
},
|
||||
// Popout camera when not focused on voice channel
|
||||
{
|
||||
find: ".mirror]:",
|
||||
replacement: {
|
||||
match: /\[(\i).mirror]:\i/,
|
||||
replace: "[$1.mirror]:!1"
|
||||
}
|
||||
},
|
||||
// Overriding css on Preview Camera/Change Video Background popup
|
||||
{
|
||||
find: ".cameraPreview,",
|
||||
replacement: {
|
||||
match: /className:\i.camera,/,
|
||||
replace: "$&style:{transform: \"scalex(1)\"},"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import { PaintbrushIcon } from "@components/Icons";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
|
|
@ -106,12 +107,20 @@ const settings = definePluginSettings({
|
|||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
},
|
||||
remixMe: {
|
||||
description: "Turns every single message with attachment to have remix tag",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
}
|
||||
});
|
||||
|
||||
const handleMessage: MessageSendListener = (_, __, ex) => ex.uploads && ex.uploads.forEach(att => att.isRemix = true);
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "Remix",
|
||||
description: "Adds Remix to Desktop",
|
||||
authors: [EquicordDevs.MrDiamond],
|
||||
authors: [EquicordDevs.MrDiamond, EquicordDevs.meowabyte],
|
||||
settings,
|
||||
contextMenus: {
|
||||
"channel-attach": UploadContextMenuPatch,
|
||||
|
|
@ -124,9 +133,19 @@ export default definePlugin({
|
|||
await requireSettingsMenu();
|
||||
|
||||
enableStyle(css);
|
||||
if (Settings.plugins?.RemixMe?.enabled) {
|
||||
Settings.plugins.RemixMe.enabled = false;
|
||||
settings.store.remixMe = true;
|
||||
}
|
||||
if (settings.store.remixMe) {
|
||||
addMessagePreSendListener(handleMessage);
|
||||
}
|
||||
},
|
||||
|
||||
stop() {
|
||||
disableStyle(css);
|
||||
if (settings.store.remixMe) {
|
||||
removeMessagePreSendListener(handleMessage);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
const handleMessage: MessageSendListener = (_, __, ex) => ex.uploads && ex.uploads.forEach(att => att.isRemix = true);
|
||||
|
||||
export default definePlugin({
|
||||
name: "RemixMe",
|
||||
description: "Turns every single message with attachment to have remix tag",
|
||||
authors: [EquicordDevs.meowabyte],
|
||||
start() {
|
||||
addMessagePreSendListener(handleMessage);
|
||||
},
|
||||
stop() {
|
||||
removeMessagePreSendListener(handleMessage);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,80 +1,38 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { settings } from "./settings";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
volume: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Volume of the animalese sound",
|
||||
default: 0.5,
|
||||
markers: [0, 0.1, 0.25, 0.5, 0.6, 0.75, 1],
|
||||
},
|
||||
speed: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Speed of the animalese sound",
|
||||
default: 1,
|
||||
markers: [0.5, 0.75, 1, 1.25, 1.5],
|
||||
},
|
||||
pitch: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Pitch multiplier",
|
||||
default: 1,
|
||||
markers: [0.75, 0.8, 0.85, 1, 1.15, 1.25, 1.35, 1.5],
|
||||
},
|
||||
messageLengthLimit: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Maximum length of message to process",
|
||||
default: 50,
|
||||
},
|
||||
processOwnMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Enable to yap your own messages too",
|
||||
default: true,
|
||||
},
|
||||
soundQuality: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Quality of sound to use",
|
||||
options: [
|
||||
{
|
||||
label: "High",
|
||||
value: "high",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Medium",
|
||||
value: "med"
|
||||
},
|
||||
{
|
||||
label: "Low",
|
||||
value: "low"
|
||||
},
|
||||
{
|
||||
label: "Lowest",
|
||||
value: "low"
|
||||
}
|
||||
]
|
||||
export let audioContext: AudioContext | null = null;
|
||||
|
||||
export async function init() {
|
||||
if (!audioContext) {
|
||||
audioContext = new AudioContext();
|
||||
await initSoundBuffers();
|
||||
}
|
||||
});
|
||||
init();
|
||||
}
|
||||
|
||||
let audioContext: AudioContext | null = null;
|
||||
export async function kill() {
|
||||
if (audioContext) {
|
||||
audioContext.close();
|
||||
audioContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
// better than my old hardcoded garbage
|
||||
const highSounds = Array.from(
|
||||
export const highSounds = Array.from(
|
||||
{ length: 30 },
|
||||
(_, i) => `sound${String(i + 1).padStart(2, "0")}.wav`
|
||||
);
|
||||
const soundBuffers: Record<string, AudioBuffer> = {};
|
||||
export const soundBuffers: Record<string, AudioBuffer> = {};
|
||||
|
||||
const BASE_URL_HIGH = "https://raw.githubusercontent.com/Equicord/Equibored/main/sounds/animalese";
|
||||
export const BASE_URL_HIGH = "https://raw.githubusercontent.com/Equicord/Equibored/main/sounds/animalese";
|
||||
|
||||
async function initSoundBuffers() {
|
||||
export async function initSoundBuffers() {
|
||||
if (!audioContext) audioContext = new AudioContext();
|
||||
const quality = settings.store.soundQuality;
|
||||
for (const file of highSounds) {
|
||||
|
|
@ -85,7 +43,7 @@ async function initSoundBuffers() {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadSound(url: string): Promise<AudioBuffer> {
|
||||
export async function loadSound(url: string): Promise<AudioBuffer> {
|
||||
if (!audioContext) audioContext = new AudioContext();
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) throw new Error("Network response was not OK");
|
||||
|
|
@ -93,7 +51,7 @@ async function loadSound(url: string): Promise<AudioBuffer> {
|
|||
return audioContext.decodeAudioData(arrayBuffer);
|
||||
}
|
||||
|
||||
async function generateAnimalese(text: string): Promise<AudioBuffer | null> {
|
||||
export async function generateAnimalese(text: string): Promise<AudioBuffer | null> {
|
||||
if (!audioContext) audioContext = new AudioContext();
|
||||
|
||||
const soundIndices: string[] = [];
|
||||
|
|
@ -175,7 +133,7 @@ async function generateAnimalese(text: string): Promise<AudioBuffer | null> {
|
|||
return outputBuffer;
|
||||
}
|
||||
|
||||
async function playSound(buffer: AudioBuffer, volume: number) {
|
||||
export async function playSound(buffer: AudioBuffer, volume: number) {
|
||||
if (!audioContext) audioContext = new AudioContext();
|
||||
const source = audioContext.createBufferSource();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
|
@ -189,55 +147,3 @@ async function playSound(buffer: AudioBuffer, volume: number) {
|
|||
|
||||
source.start();
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "Animalese",
|
||||
description: "Plays animal crossing animalese for every message sent (they yap a lot)",
|
||||
authors: [EquicordDevs.ryanamay, EquicordDevs.Mocha],
|
||||
settings,
|
||||
|
||||
flux: {
|
||||
async MESSAGE_CREATE({ optimistic, type, message, channelId }) {
|
||||
if (optimistic || type !== "MESSAGE_CREATE") return;
|
||||
if (message.state === "SENDING") return;
|
||||
if (!message.content || message.author?.bot || channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
const urlPattern = /https?:\/\/[^\s]+/;
|
||||
const maxLength = settings.store.messageLengthLimit || 100;
|
||||
const processOwnMessages = settings.store.processOwnMessages ?? true;
|
||||
|
||||
if (
|
||||
urlPattern.test(message.content)
|
||||
|| message.content.length > maxLength
|
||||
|| !processOwnMessages
|
||||
&& String(message.author.id) === String(UserStore.getCurrentUser().id)
|
||||
) return;
|
||||
|
||||
try {
|
||||
const buffer = await generateAnimalese(message.content);
|
||||
if (buffer) await playSound(buffer, settings.store.volume);
|
||||
} catch (err) {
|
||||
console.error("[Animalese]", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
// Only subscribe once!
|
||||
const init = async () => {
|
||||
if (!audioContext) {
|
||||
audioContext = new AudioContext();
|
||||
await initSoundBuffers();
|
||||
}
|
||||
init();
|
||||
};
|
||||
init();
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (audioContext) {
|
||||
audioContext.close();
|
||||
audioContext = null;
|
||||
}
|
||||
},
|
||||
});
|
||||
16
src/equicordplugins/soundTroll/equissant.ts
Normal file
16
src/equicordplugins/soundTroll/equissant.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
let clickCount = 0;
|
||||
|
||||
export function croissant() {
|
||||
clickCount++;
|
||||
|
||||
if (clickCount % 10 === 0) {
|
||||
const audio = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/equissant/croissant.mp3");
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
147
src/equicordplugins/soundTroll/index.ts
Normal file
147
src/equicordplugins/soundTroll/index.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { sleep } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
|
||||
import { croissant } from "./equissant";
|
||||
import { boom, getMoyaiCount, MOYAI } from "./moyai";
|
||||
import { settings } from "./settings";
|
||||
import { IMessageCreate, IReactionAdd, IVoiceChannelEffectSendEvent } from "./types";
|
||||
import { generateAnimalese, playSound, init, kill } from "./animalese";
|
||||
import { keydown } from "./keyboardSounds";
|
||||
import { Settings } from "@api/Settings";
|
||||
|
||||
export let click1, click2, click3, backspace;
|
||||
export let sounds = {
|
||||
click1,
|
||||
click2,
|
||||
click3,
|
||||
backspace
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "SoundTroll",
|
||||
description: "Random sound plugin",
|
||||
authors: [EquicordDevs.SomeAspy, Devs.thororen, Devs.Megu, Devs.Nuckyz, EquicordDevs.ryanamay, EquicordDevs.Mocha, Devs.HypedDomi],
|
||||
settings,
|
||||
start() {
|
||||
if (settings.store.equissant) {
|
||||
document.addEventListener("click", croissant);
|
||||
}
|
||||
if (settings.store.animalese) {
|
||||
init();
|
||||
}
|
||||
if (settings.store.keyboardSounds) {
|
||||
click1 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click1.wav");
|
||||
click2 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click2.wav");
|
||||
click3 = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/click3.wav");
|
||||
backspace = new Audio("https://github.com/Equicord/Equibored/raw/main/sounds/keyboard/backspace.wav");
|
||||
sounds = {
|
||||
click1,
|
||||
click2,
|
||||
click3,
|
||||
backspace,
|
||||
};
|
||||
document.addEventListener("keydown", keydown);
|
||||
}
|
||||
if (Settings.plugins?.Animalese?.enabled) {
|
||||
Settings.plugins.Animalese.enabled = false;
|
||||
Settings.plugins.SoundTroll.enabled = true;
|
||||
settings.store.animalese = true;
|
||||
}
|
||||
if (Settings.plugins?.Moyai?.enabled) {
|
||||
Settings.plugins.Moyai.enabled = false;
|
||||
Settings.plugins.SoundTroll.enabled = true;
|
||||
settings.store.moyai = true;
|
||||
}
|
||||
if (Settings.plugins?.Equissant?.enabled) {
|
||||
Settings.plugins.Equissant.enabled = false;
|
||||
Settings.plugins.SoundTroll.enabled = true;
|
||||
settings.store.equissant = true;
|
||||
}
|
||||
if (Settings.plugins?.KeyboardSounds?.enabled) {
|
||||
Settings.plugins.KeyboardSounds.enabled = false;
|
||||
Settings.plugins.SoundTroll.enabled = true;
|
||||
settings.store.keyboardSounds = true;
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
if (settings.store.equissant) {
|
||||
document.removeEventListener("click", croissant);
|
||||
}
|
||||
if (settings.store.animalese) {
|
||||
kill();
|
||||
}
|
||||
if (settings.store.keyboardSounds) {
|
||||
document.removeEventListener("keydown", keydown);
|
||||
}
|
||||
},
|
||||
flux: {
|
||||
async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) {
|
||||
if (optimistic || type !== "MESSAGE_CREATE") return;
|
||||
if (message.state === "SENDING") return;
|
||||
if (settings.store.ignoreBots && message.author?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(message.author?.id)) return;
|
||||
if (!message.content) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
if (settings.store.moyai) {
|
||||
const moyaiCount = getMoyaiCount(message.content);
|
||||
|
||||
for (let i = 0; i < moyaiCount; i++) {
|
||||
boom();
|
||||
await sleep(300);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.store.animalese) {
|
||||
const urlPattern = /https?:\/\/[^\s]+/;
|
||||
const maxLength = settings.store.messageLengthLimit || 100;
|
||||
const processOwnMessages = settings.store.processOwnMessages ?? true;
|
||||
if (
|
||||
urlPattern.test(message.content)
|
||||
|| message.content.length > maxLength
|
||||
|| !processOwnMessages
|
||||
&& String(message.author.id) === String(UserStore.getCurrentUser().id)
|
||||
) return;
|
||||
|
||||
try {
|
||||
const buffer = await generateAnimalese(message.content);
|
||||
if (buffer) await playSound(buffer, settings.store.volume);
|
||||
} catch (err) {
|
||||
console.error("[Animalese]", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
MESSAGE_REACTION_ADD({ optimistic, type, channelId, userId, messageAuthorId, emoji }: IReactionAdd) {
|
||||
if (optimistic || type !== "MESSAGE_REACTION_ADD") return;
|
||||
if (settings.store.ignoreBots && UserStore.getUser(userId)?.bot) return;
|
||||
if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(messageAuthorId)) return;
|
||||
if (channelId !== SelectedChannelStore.getChannelId()) return;
|
||||
|
||||
if (settings.store.moyai) {
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
}
|
||||
},
|
||||
|
||||
VOICE_CHANNEL_EFFECT_SEND({ emoji }: IVoiceChannelEffectSendEvent) {
|
||||
if (settings.store.moyai) {
|
||||
if (!emoji?.name) return;
|
||||
const name = emoji.name.toLowerCase();
|
||||
if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return;
|
||||
|
||||
boom();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
22
src/equicordplugins/soundTroll/keyboardSounds.ts
Normal file
22
src/equicordplugins/soundTroll/keyboardSounds.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { sounds } from ".";
|
||||
|
||||
export const ignoredKeys = ["CapsLock", "ShiftLeft", "ShiftRight", "ControlLeft", "ControlRight", "AltLeft", "AltRight", "MetaLeft", "MetaRight", "ArrowUp", "ArrowRight", "ArrowLeft", "ArrowDown", "MediaPlayPause", "MediaStop", "MediaTrackNext", "MediaTrackPrevious", "MediaSelect", "MediaEject", "MediaVolumeUp", "MediaVolumeDown", "AudioVolumeUp", "AudioVolumeDown"];
|
||||
|
||||
export const keydown = (e: KeyboardEvent) => {
|
||||
if (ignoredKeys.includes(e.code)) return;
|
||||
for (const sound of Object.values(sounds)) sound.pause();
|
||||
if (e.code === "Backspace") {
|
||||
sounds.backspace.currentTime = 0;
|
||||
sounds.backspace.play();
|
||||
} else {
|
||||
const click = sounds[`click${Math.floor(Math.random() * 3) + 1}`];
|
||||
click.currentTime = 0;
|
||||
click.play();
|
||||
}
|
||||
};
|
||||
52
src/equicordplugins/soundTroll/moyai.ts
Normal file
52
src/equicordplugins/soundTroll/moyai.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { settings } from "./settings";
|
||||
|
||||
export const MOYAI = "🗿";
|
||||
export const MOYAI_URL = "https://github.com/Equicord/Equibored/raw/main/sounds/moyai/moyai.mp3";
|
||||
export const MOYAI_URL_HD = "https://github.com/Equicord/Equibored/raw/main/sounds/moyai/moyai.wav";
|
||||
|
||||
export function countOccurrences(sourceString: string, subString: string) {
|
||||
let i = 0;
|
||||
let lastIdx = 0;
|
||||
while ((lastIdx = sourceString.indexOf(subString, lastIdx) + 1) !== 0)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
export function countMatches(sourceString: string, pattern: RegExp) {
|
||||
if (!pattern.global)
|
||||
throw new Error("pattern must be global");
|
||||
|
||||
let i = 0;
|
||||
while (pattern.test(sourceString))
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
export const customMoyaiRe = /<a?:\w*moy?ai\w*:\d{17,20}>/gi;
|
||||
|
||||
export function getMoyaiCount(message: string) {
|
||||
const count = countOccurrences(message, MOYAI)
|
||||
+ countMatches(message, customMoyaiRe);
|
||||
|
||||
return Math.min(count, 10);
|
||||
}
|
||||
|
||||
export function boom() {
|
||||
if (!settings.store.triggerWhenUnfocused && !document.hasFocus()) return;
|
||||
const audioElement = document.createElement("audio");
|
||||
|
||||
audioElement.src = settings.store.soundQuality !== "low"
|
||||
? MOYAI_URL_HD
|
||||
: MOYAI_URL;
|
||||
|
||||
audioElement.volume = settings.store.volume;
|
||||
audioElement.play();
|
||||
}
|
||||
110
src/equicordplugins/soundTroll/settings.tsx
Normal file
110
src/equicordplugins/soundTroll/settings.tsx
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange, OptionType } from "@utils/types";
|
||||
|
||||
import { sounds } from ".";
|
||||
import { croissant } from "./equissant";
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
equissant: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Crossant every 10 clicks :trolley:",
|
||||
default: false,
|
||||
onChange: v => {
|
||||
if (v) {
|
||||
document.addEventListener("click", croissant);
|
||||
} else {
|
||||
document.removeEventListener("click", croissant);
|
||||
}
|
||||
}
|
||||
},
|
||||
moyai: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "🗿🗿🗿🗿🗿🗿🗿🗿",
|
||||
default: false,
|
||||
},
|
||||
animalese: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Plays animal crossing animalese for every message sent (they yap a lot)",
|
||||
default: false,
|
||||
},
|
||||
keyboardSounds: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Adds the Opera GX Keyboard Sounds to Discord",
|
||||
default: false,
|
||||
},
|
||||
speed: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Speed of the animalese sound",
|
||||
default: 1,
|
||||
markers: [0.5, 0.75, 1, 1.25, 1.5],
|
||||
},
|
||||
pitch: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "Pitch multiplier",
|
||||
default: 1,
|
||||
markers: [0.75, 0.8, 0.85, 1, 1.15, 1.25, 1.35, 1.5],
|
||||
},
|
||||
volume: {
|
||||
description: "Volume of the 🗿🗿🗿",
|
||||
type: OptionType.SLIDER,
|
||||
markers: makeRange(0, 1, 0.1),
|
||||
default: 0.5,
|
||||
stickToMarkers: false,
|
||||
onChange: value => { for (const sound of Object.values(sounds)) sound.volume = value / 100; }
|
||||
|
||||
},
|
||||
soundQuality: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Quality of sound to use",
|
||||
options: [
|
||||
{
|
||||
label: "High",
|
||||
value: "high",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Medium",
|
||||
value: "med"
|
||||
},
|
||||
{
|
||||
label: "Low",
|
||||
value: "low"
|
||||
},
|
||||
{
|
||||
label: "Lowest",
|
||||
value: "low"
|
||||
}
|
||||
]
|
||||
},
|
||||
triggerWhenUnfocused: {
|
||||
description: "Trigger the 🗿 even when the window is unfocused",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBots: {
|
||||
description: "Ignore bots",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
ignoreBlocked: {
|
||||
description: "Ignore blocked users",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
},
|
||||
messageLengthLimit: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Maximum length of message to process",
|
||||
default: 50,
|
||||
},
|
||||
processOwnMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Enable to yap your own messages too",
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
34
src/equicordplugins/soundTroll/types.ts
Normal file
34
src/equicordplugins/soundTroll/types.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Message, ReactionEmoji } from "@vencord/discord-types";
|
||||
|
||||
export interface IMessageCreate {
|
||||
type: "MESSAGE_CREATE";
|
||||
optimistic: boolean;
|
||||
isPushNotification: boolean;
|
||||
channelId: string;
|
||||
message: Message;
|
||||
}
|
||||
|
||||
export interface IReactionAdd {
|
||||
type: "MESSAGE_REACTION_ADD";
|
||||
optimistic: boolean;
|
||||
channelId: string;
|
||||
messageId: string;
|
||||
messageAuthorId: string;
|
||||
userId: "195136840355807232";
|
||||
emoji: ReactionEmoji;
|
||||
}
|
||||
|
||||
export interface IVoiceChannelEffectSendEvent {
|
||||
type: string;
|
||||
emoji?: ReactionEmoji; // Just in case...
|
||||
channelId: string;
|
||||
userId: string;
|
||||
animationType: number;
|
||||
animationId: number;
|
||||
}
|
||||
|
|
@ -123,8 +123,8 @@ async function generateDebugInfoMessage() {
|
|||
}
|
||||
|
||||
const potentiallyProblematicPlugins = ([
|
||||
"NoRPC", "NoProfileThemes", "NoMosaic", "NoRoleHeaders", "NoSystemBadge", "NoDeleteSafety",
|
||||
"Moyai", "AlwaysAnimate", "ClientTheme", "Equissant", "Ingtoninator", "KeyboardSounds", "NeverPausePreviews",
|
||||
"NoRPC", "NoProfileThemes", "NoMosaic", "NoRoleHeaders", "NoSystemBadge",
|
||||
"AlwaysAnimate", "ClientTheme", "SoundTroll", "Ingtoninator", "NeverPausePreviews",
|
||||
].filter(Vencord.Plugins.isPluginEnabled) ?? []).sort();
|
||||
|
||||
if (Vencord.Plugins.isPluginEnabled("CustomIdle") && Vencord.Settings.plugins.CustomIdle.idleTimeout === 0) {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,20 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, GuildStore } from "@webpack/common";
|
||||
|
||||
const DeleteGuild = findByPropsLazy("deleteGuild", "sendTransferOwnershipPincode").deleteGuild;
|
||||
|
||||
function GetPropsAndDeleteGuild(id) {
|
||||
const GotGuild = GuildStore.getGuild(id);
|
||||
if (!GotGuild) return;
|
||||
|
||||
DeleteGuild(id, GotGuild.name);
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
domain: {
|
||||
|
|
@ -32,13 +43,25 @@ const settings = definePluginSettings({
|
|||
default: true,
|
||||
description: "Remove the 'Potentially Dangerous Download' popup when opening links",
|
||||
restartNeeded: true
|
||||
}
|
||||
},
|
||||
noDeleteSafety: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Remove the 'Potentially Dangerous Download' popup when opening links",
|
||||
restartNeeded: true
|
||||
},
|
||||
confirmModal: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Should a \"are you sure you want to delete\" modal be shown?",
|
||||
default: true
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "AlwaysTrust",
|
||||
description: "Removes the annoying untrusted domain and suspicious file popup",
|
||||
authors: [Devs.zt, Devs.Trwy],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
find: '="MaskedLinkStore",',
|
||||
|
|
@ -55,7 +78,34 @@ export default definePlugin({
|
|||
replace: "$&return null;"
|
||||
},
|
||||
predicate: () => settings.store.file
|
||||
},
|
||||
{
|
||||
find: ".DELETE,onClick(){let",
|
||||
replacement: {
|
||||
match: /let\{name:\i\}=\i\.guild/,
|
||||
replace: "$self.HandleGuildDeleteModal($1);$&"
|
||||
},
|
||||
predicate: () => settings.store.noDeleteSafety
|
||||
}
|
||||
],
|
||||
settings
|
||||
start() {
|
||||
if (Settings.plugins?.NoDeleteSafety?.enabled) {
|
||||
Settings.plugins.NoDeleteSafety.enabled = false;
|
||||
settings.store.noDeleteSafety = true;
|
||||
}
|
||||
},
|
||||
async HandleGuildDeleteModal(server) {
|
||||
if (settings.store.confirmModal) {
|
||||
return Alerts.show({
|
||||
title: "Delete server?",
|
||||
body: <p>It's permanent, if that wasn't obvious.</p>,
|
||||
confirmColor: Button.Colors.RED,
|
||||
confirmText: "Delete",
|
||||
onConfirm: () => GetPropsAndDeleteGuild(server.id),
|
||||
cancelText: "Cancel"
|
||||
});
|
||||
} else {
|
||||
return GetPropsAndDeleteGuild(server.id);
|
||||
}
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue