Restore Missing Plugins

This commit is contained in:
thororen1234 2025-09-27 16:13:56 -04:00
parent dd9811147b
commit 630fdb0119
No known key found for this signature in database
5 changed files with 421 additions and 211 deletions

View file

@ -0,0 +1,243 @@
/*
* 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 { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { SelectedChannelStore, UserStore } from "@webpack/common";
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"
}
]
}
});
let audioContext: AudioContext | null = null;
// better than my old hardcoded garbage
const highSounds = Array.from(
{ length: 30 },
(_, i) => `sound${String(i + 1).padStart(2, "0")}.wav`
);
const soundBuffers: Record<string, AudioBuffer> = {};
const BASE_URL_HIGH = "https://raw.githubusercontent.com/Equicord/Equibored/main/sounds/animalese";
async function initSoundBuffers() {
if (!audioContext) audioContext = new AudioContext();
const quality = settings.store.soundQuality;
for (const file of highSounds) {
const nameWithoutExt = file.replace(".wav", "");
soundBuffers[nameWithoutExt] = await loadSound(
`${BASE_URL_HIGH}/${quality}/${file}`
);
}
}
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");
const arrayBuffer = await response.arrayBuffer();
return audioContext.decodeAudioData(arrayBuffer);
}
async function generateAnimalese(text: string): Promise<AudioBuffer | null> {
if (!audioContext) audioContext = new AudioContext();
const soundIndices: string[] = [];
const text_lower = text.toLowerCase();
for (let i = 0; i < text_lower.length; i++) {
const char = text_lower[i];
if (char === "s" && text_lower[i + 1] === "h") {
soundIndices.push("sound27");
i++;
} else if (char === "t" && text_lower[i + 1] === "h") {
soundIndices.push("sound28");
i++;
} else if (
char === "h" &&
(text_lower[i - 1] === "s" || text_lower[i - 1] === "t")
) {
continue;
} else if (char === "," || char === "?") {
soundIndices.push("sound30");
} else if (char === text_lower[i - 1]) {
continue;
} else if (char.match(/[a-z]/)) {
const index = char.charCodeAt(0) - 96;
soundIndices.push(`sound${String(index).padStart(2, "0")}`);
}
}
// No valid characters? Just return null
if (soundIndices.length === 0) {
return null;
}
const totalDuration = soundIndices.length * 0.1;
const frameCount = Math.max(1, Math.floor(audioContext.sampleRate * totalDuration));
const outputBuffer = audioContext.createBuffer(
1,
frameCount,
audioContext.sampleRate
);
const outputData = outputBuffer.getChannelData(0);
let offset = 0;
const baseLetterDuration = audioContext.sampleRate * (0.09 / settings.store.speed);
// ~90ms per letter at 1x speed (tweakable!)
for (let i = 0; i < soundIndices.length; i++) {
const buffer = soundBuffers[soundIndices[i]];
if (!buffer) continue;
const variation = 0.15;
let pitchShift = (2.8 * settings.store.pitch) + (Math.random() * variation);
const isQuestion = text_lower.endsWith("?");
if (isQuestion && i >= soundIndices.length * 0.8) {
const progress =
(i - soundIndices.length * 0.8) / (soundIndices.length * 0.2);
pitchShift += progress * 0.1 + 0.1;
}
const inputData = buffer.getChannelData(0);
const inputLength = inputData.length;
const outputLength = Math.floor(inputLength / pitchShift);
// copy sound into the slot
for (let j = 0; j < outputLength; j++) {
const inputIndex = Math.floor(j * pitchShift);
const targetIndex = offset + j;
if (inputIndex < inputLength && targetIndex < outputData.length) {
outputData[targetIndex] = inputData[inputIndex];
}
}
// instead of stacking lengths, jump forward a *fixed slot* per character
offset += Math.floor(baseLetterDuration);
}
return outputBuffer;
}
async function playSound(buffer: AudioBuffer, volume: number) {
if (!audioContext) audioContext = new AudioContext();
const source = audioContext.createBufferSource();
const gainNode = audioContext.createGain();
source.buffer = buffer;
source.playbackRate.value = settings.store.pitch;
gainNode.gain.value = volume;
source.connect(gainNode);
gainNode.connect(audioContext.destination);
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;
}
},
});

View file

@ -0,0 +1,3 @@
# Equissant
Plays crossant every specified amount of clicks

View file

@ -0,0 +1,174 @@
/*
* 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();
}

View file

@ -6,7 +6,7 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings, Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { PaintbrushIcon } from "@components/Icons";
import { EquicordDevs } from "@utils/constants";
@ -133,10 +133,6 @@ 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);
}

View file

@ -1,206 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 exhq
*
* 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 { findOption, RequiredMessageOption } from "@api/Commands";
import { addMessagePreEditListener, addMessagePreSendListener, MessageObject, removeMessagePreEditListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
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)"
];
const replacements = [
["small", "smol"],
["cute", "kawaii"],
["fluff", "floof"],
["love", "luv"],
["stupid", "baka"],
["what", "nani"],
["meow", "nya"],
["hello", "hewwo"],
];
const settings = definePluginSettings({
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
}
});
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];
}
const isOneCharacterString = (str: string): boolean => {
return str.split("").every((char: string) => char === str[0]);
};
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;
}
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;
}
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;
}
// actual command declaration
export default definePlugin({
name: "UwUifier",
description: "Make everything uwu",
authors: [Devs.amy],
dependencies: ["MessageEventsAPI"],
settings,
commands: [
{
name: "uwuify",
description: "uwuifies your messages",
options: [RequiredMessageOption],
execute: opts => ({
content: uwuify(findOption(opts, "message", "")),
}),
},
],
patches: [
{
find: ".isPureReactComponent=!0;",
predicate: () => settings.store.uwuEverything,
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) {
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);
},
});