From 4d31f05c630be313a0fd3bb8b38c76fef4cbc40a Mon Sep 17 00:00:00 2001
From: Rico040 <93081680+Rico040@users.noreply.github.com>
Date: Sun, 5 May 2024 23:58:02 +1000
Subject: [PATCH] feat(plugin): `Freemoji` fixed for alpha
---
plugins/freemoji/manifest.json | 18 ++++++++
plugins/freemoji/src/Settings.tsx | 45 +++++++++++++++++++
plugins/freemoji/src/def.d.ts | 5 +++
plugins/freemoji/src/index.ts | 17 ++++++++
plugins/freemoji/src/msgProcessor.ts | 48 +++++++++++++++++++++
plugins/freemoji/src/patches/nitroChecks.ts | 9 ++++
plugins/freemoji/src/patches/sendMessage.ts | 11 +++++
plugins/freemoji/src/vpatcher.ts | 28 ++++++++++++
8 files changed, 181 insertions(+)
create mode 100644 plugins/freemoji/manifest.json
create mode 100644 plugins/freemoji/src/Settings.tsx
create mode 100644 plugins/freemoji/src/def.d.ts
create mode 100644 plugins/freemoji/src/index.ts
create mode 100644 plugins/freemoji/src/msgProcessor.ts
create mode 100644 plugins/freemoji/src/patches/nitroChecks.ts
create mode 100644 plugins/freemoji/src/patches/sendMessage.ts
create mode 100644 plugins/freemoji/src/vpatcher.ts
diff --git a/plugins/freemoji/manifest.json b/plugins/freemoji/manifest.json
new file mode 100644
index 0000000..ce411c2
--- /dev/null
+++ b/plugins/freemoji/manifest.json
@@ -0,0 +1,18 @@
+{
+ "name": "Freemoji",
+ "description": "Allows you to use Nitro emoji without Nitro.",
+ "authors": [
+ {
+ "name": "maisy",
+ "id": "257109471589957632"
+ },
+ {
+ "name": "Rico040",
+ "id": "619474349845643275"
+ }
+ ],
+ "main": "src/index.ts",
+ "vendetta": {
+ "icon": "img_nitro_emojis"
+ }
+}
\ No newline at end of file
diff --git a/plugins/freemoji/src/Settings.tsx b/plugins/freemoji/src/Settings.tsx
new file mode 100644
index 0000000..600331b
--- /dev/null
+++ b/plugins/freemoji/src/Settings.tsx
@@ -0,0 +1,45 @@
+import { ReactNative as RN } from "@vendetta/metro/common";
+import { Forms } from "@vendetta/ui/components";
+import { useProxy } from "@vendetta/storage";
+import { storage } from "@vendetta/plugin";
+
+const { FormSection, FormRadioRow } = Forms;
+
+const sizeOptions = {
+ Tiny: 16,
+ Small: 32,
+ Medium: 48,
+ Large: 64,
+ Huge: 96,
+ Jumbo: 128,
+}
+
+const previewUri = "https://cdn.discordapp.com/emojis/1105406110724268075.webp";
+
+export default () => {
+ useProxy(storage);
+
+ return (
+
+
+ {Object.entries(sizeOptions).map(([name, size]) => {
+ storage.emojiSize = size;
+ }}
+ />)}
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/plugins/freemoji/src/def.d.ts b/plugins/freemoji/src/def.d.ts
new file mode 100644
index 0000000..119c978
--- /dev/null
+++ b/plugins/freemoji/src/def.d.ts
@@ -0,0 +1,5 @@
+export interface Message {
+ content: string;
+ // TODO: Get the proper type for this
+ invalidEmojis: any[];
+}
\ No newline at end of file
diff --git a/plugins/freemoji/src/index.ts b/plugins/freemoji/src/index.ts
new file mode 100644
index 0000000..d92977e
--- /dev/null
+++ b/plugins/freemoji/src/index.ts
@@ -0,0 +1,17 @@
+import { storage } from "@vendetta/plugin";
+import nitroChecks from "./patches/nitroChecks";
+import sendMessage from "./patches/sendMessage";
+
+// Default settings
+storage.emojiSize ??= 48;
+
+// Migration code, used to be string containing a number but is now just a number
+if (typeof storage.emojiSize === "string") storage.emojiSize = parseInt(storage.emojiSize);
+
+const patches = [
+ ...nitroChecks,
+ ...sendMessage,
+];
+
+export const onUnload = () => patches.forEach(p => p());
+export { default as settings } from "./Settings";
\ No newline at end of file
diff --git a/plugins/freemoji/src/msgProcessor.ts b/plugins/freemoji/src/msgProcessor.ts
new file mode 100644
index 0000000..15366e5
--- /dev/null
+++ b/plugins/freemoji/src/msgProcessor.ts
@@ -0,0 +1,48 @@
+import { findByStoreName } from "@vendetta/metro";
+import { storage } from "@vendetta/plugin";
+import { Message } from "./def";
+const { getCustomEmojiById } = findByStoreName("EmojiStore");
+const { getGuildId } = findByStoreName("SelectedGuildStore");
+
+// https://github.com/luimu64/nitro-spoof/blob/1bb75a2471c39669d590bfbabeb7b922672929f5/index.js#L25
+const hasEmotesRegex = //i;
+
+function extractUnusableEmojis(messageString: string, size: number) {
+ const emojiStrings = messageString.matchAll(//gi);
+ const emojiUrls = [];
+
+ for (const emojiString of emojiStrings) {
+ // Fetch required info about the emoji
+ const emoji = getCustomEmojiById(emojiString[2]);
+
+ // Check emoji usability
+ if (
+ emoji.guildId != getGuildId() ||
+ emoji.animated
+ ) {
+ // Remove emote from original msg
+ messageString = messageString.replace(emojiString[0], "");
+ // Add to emotes to send
+ emojiUrls.push(`${emoji.url.split("?")[0]}?size=${size}`);
+ }
+ }
+
+ return {
+ newContent: messageString.trim(),
+ extractedEmojis: emojiUrls,
+ };
+}
+
+export default function modifyIfNeeded(msg: Message) {
+ if (!msg.content.match(hasEmotesRegex)) return;
+
+ // Find all emojis from the captured message string and return object with emojiURLS and content
+ const { newContent, extractedEmojis } = extractUnusableEmojis(msg.content, storage.emojiSize);
+
+ msg.content = newContent;
+
+ if (extractedEmojis.length > 0) msg.content += "\n" + extractedEmojis.join("\n");
+
+ // Set invalidEmojis to empty to prevent Discord yelling to you about you not having nitro
+ msg.invalidEmojis = [];
+};
\ No newline at end of file
diff --git a/plugins/freemoji/src/patches/nitroChecks.ts b/plugins/freemoji/src/patches/nitroChecks.ts
new file mode 100644
index 0000000..09139cf
--- /dev/null
+++ b/plugins/freemoji/src/patches/nitroChecks.ts
@@ -0,0 +1,9 @@
+import { findByProps } from "@vendetta/metro";
+import { instead } from "../vpatcher";
+
+const nitroInfo = findByProps("canUseEmojisEverywhere");
+
+export default [
+ instead("canUseEmojisEverywhere", nitroInfo, () => true),
+ instead("canUseAnimatedEmojis", nitroInfo, () => true),
+];
\ No newline at end of file
diff --git a/plugins/freemoji/src/patches/sendMessage.ts b/plugins/freemoji/src/patches/sendMessage.ts
new file mode 100644
index 0000000..add4cd2
--- /dev/null
+++ b/plugins/freemoji/src/patches/sendMessage.ts
@@ -0,0 +1,11 @@
+import { findByProps } from "@vendetta/metro";
+import { before } from "../vpatcher";
+import modifyIfNeeded from "../msgProcessor";
+
+const messageModule = findByProps("sendMessage", "receiveMessage");
+const uploadModule = findByProps("uploadLocalFiles");
+
+export default [
+ before("sendMessage", messageModule, (args) => modifyIfNeeded(args[1])),
+ before("uploadLocalFiles", uploadModule, (args) => modifyIfNeeded(args[0].parsedMessage)),
+];
\ No newline at end of file
diff --git a/plugins/freemoji/src/vpatcher.ts b/plugins/freemoji/src/vpatcher.ts
new file mode 100644
index 0000000..41e68d2
--- /dev/null
+++ b/plugins/freemoji/src/vpatcher.ts
@@ -0,0 +1,28 @@
+// https://github.com/Vendicated/its-called-vendetta-cause-its-owned-by-ven-plugins/blob/main/shared/vendetta-wrappers.ts
+import { after as vAfter, before as vBefore, instead as vInstead } from "@vendetta/patcher";
+
+export const unpatches = [] as Array<() => boolean>;
+
+function wrapCb(type: string, name: string, cb: F): F {
+ return function () {
+ try {
+ return cb.apply(this, arguments);
+ } catch (err) {
+ console.error(`Error while running ${type} callback for ${name}:`, err);
+ }
+ } as any as F;
+}
+
+export const before: (...args: Parameters) => void = (name, obj, cb, once) => {
+ unpatches.push(vBefore(name, obj, wrapCb("before", name, cb), once));
+};
+
+export const after: (...args: Parameters) => void = (name, obj, cb, once) => {
+ unpatches.push(vAfter(name, obj, wrapCb("after", name, cb), once));
+};
+
+export const instead: (...args: Parameters) => void = (name, obj, cb, once) => {
+ unpatches.push(vInstead(name, obj, wrapCb("instead", name, cb), once));
+};
+
+export const unpatchAll = () => unpatches.forEach(u => u());
\ No newline at end of file