Fake Nitro Transform Stickers option and other stuff (#683)
Co-authored-by: V <vendicated@riseup.net>
This commit is contained in:
parent
99391a4f0e
commit
12ffb9d642
4 changed files with 269 additions and 76 deletions
|
@ -43,6 +43,7 @@ const settings = definePluginSettings({
|
||||||
|
|
||||||
let crashCount: number = 0;
|
let crashCount: number = 0;
|
||||||
let lastCrashTimestamp: number = 0;
|
let lastCrashTimestamp: number = 0;
|
||||||
|
let shouldAttemptNextHandle = false;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "CrashHandler",
|
name: "CrashHandler",
|
||||||
|
@ -72,6 +73,10 @@ export default definePlugin({
|
||||||
],
|
],
|
||||||
|
|
||||||
handleCrash(_this: ReactElement & { forceUpdate: () => void; }) {
|
handleCrash(_this: ReactElement & { forceUpdate: () => void; }) {
|
||||||
|
if (Date.now() - lastCrashTimestamp <= 1_000 && !shouldAttemptNextHandle) return true;
|
||||||
|
|
||||||
|
shouldAttemptNextHandle = false;
|
||||||
|
|
||||||
if (++crashCount > 5) {
|
if (++crashCount > 5) {
|
||||||
try {
|
try {
|
||||||
showNotification({
|
showNotification({
|
||||||
|
@ -151,6 +156,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
shouldAttemptNextHandle = true;
|
||||||
_this.forceUpdate();
|
_this.forceUpdate();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
CrashHandlerLogger.debug("Failed to update crash handler component.", err);
|
CrashHandlerLogger.debug("Failed to update crash handler component.", err);
|
||||||
|
|
|
@ -17,20 +17,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
||||||
import { migratePluginSettings, Settings } from "@api/settings";
|
import { definePluginSettings, migratePluginSettings, Settings } from "@api/settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
|
import { ApngBlendOp, ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
|
||||||
import { getCurrentGuild } from "@utils/discord";
|
import { getCurrentGuild } from "@utils/discord";
|
||||||
import { proxyLazy } from "@utils/proxyLazy";
|
import { proxyLazy } from "@utils/proxyLazy";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, FluxDispatcher, PermissionStore, UserStore } from "@webpack/common";
|
import { ChannelStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
|
||||||
|
import type { Message } from "discord-types/general";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
||||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||||
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
||||||
const ReaderFactory = findByPropsLazy("readerFactory");
|
const ReaderFactory = findByPropsLazy("readerFactory");
|
||||||
|
const StickerStore = findStoreLazy("StickersStore") as {
|
||||||
|
getPremiumPacks(): StickerPack[];
|
||||||
|
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||||
|
getStickerById(id: string): Sticker | undefined;
|
||||||
|
};
|
||||||
|
const EmojiStore = findStoreLazy("EmojiStore");
|
||||||
|
|
||||||
|
|
||||||
function searchProtoClass(localName: string, parentProtoClass: any) {
|
function searchProtoClass(localName: string, parentProtoClass: any) {
|
||||||
if (!parentProtoClass) return;
|
if (!parentProtoClass) return;
|
||||||
|
@ -86,6 +94,55 @@ interface StickerPack {
|
||||||
stickers: Sticker[];
|
stickers: Sticker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
||||||
|
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
||||||
|
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
enableEmojiBypass: {
|
||||||
|
description: "Allow sending fake emojis",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
emojiSize: {
|
||||||
|
description: "Size of the emojis when sending",
|
||||||
|
type: OptionType.SLIDER,
|
||||||
|
default: 48,
|
||||||
|
markers: [32, 48, 64, 128, 160, 256, 512]
|
||||||
|
},
|
||||||
|
transformEmojis: {
|
||||||
|
description: "Whether to transform fake emojis into real ones",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
enableStickerBypass: {
|
||||||
|
description: "Allow sending fake stickers",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
stickerSize: {
|
||||||
|
description: "Size of the stickers when sending",
|
||||||
|
type: OptionType.SLIDER,
|
||||||
|
default: 160,
|
||||||
|
markers: [32, 64, 128, 160, 256, 512]
|
||||||
|
},
|
||||||
|
transformStickers: {
|
||||||
|
description: "Whether to transform fake stickers into real ones",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
enableStreamQualityBypass: {
|
||||||
|
description: "Allow streaming in nitro quality",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
migratePluginSettings("FakeNitro", "NitroBypass");
|
migratePluginSettings("FakeNitro", "NitroBypass");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
@ -94,10 +151,12 @@ export default definePlugin({
|
||||||
description: "Allows you to stream in nitro quality, send fake emojis/stickers and use client themes.",
|
description: "Allows you to stream in nitro quality, send fake emojis/stickers and use client themes.",
|
||||||
dependencies: ["MessageEventsAPI"],
|
dependencies: ["MessageEventsAPI"],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".PREMIUM_LOCKED;",
|
find: ".PREMIUM_LOCKED;",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableEmojiBypass === true,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=(\i)=\i\.intention)/,
|
match: /(?<=(\i)=\i\.intention)/,
|
||||||
|
@ -115,7 +174,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "canUseAnimatedEmojis:function",
|
find: "canUseAnimatedEmojis:function",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableEmojiBypass === true,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
|
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
|
||||||
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
||||||
|
@ -123,7 +182,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "canUseStickersEverywhere:function",
|
find: "canUseStickersEverywhere:function",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableStickerBypass === true,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /canUseStickersEverywhere:function\(\i\){/,
|
match: /canUseStickersEverywhere:function\(\i\){/,
|
||||||
replace: "$&return true;"
|
replace: "$&return true;"
|
||||||
|
@ -131,7 +190,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "\"SENDABLE\"",
|
find: "\"SENDABLE\"",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableStickerBypass === true,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\w+)\.available\?/,
|
match: /(\w+)\.available\?/,
|
||||||
replace: "true?"
|
replace: "true?"
|
||||||
|
@ -139,7 +198,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "canStreamHighQuality:function",
|
find: "canStreamHighQuality:function",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableStreamQualityBypass === true,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
"canUseHighVideoUploadQuality",
|
"canUseHighVideoUploadQuality",
|
||||||
"canStreamHighQuality",
|
"canStreamHighQuality",
|
||||||
|
@ -153,7 +212,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "STREAM_FPS_OPTION.format",
|
find: "STREAM_FPS_OPTION.format",
|
||||||
predicate: () => Settings.plugins.FakeNitro.enableStreamQualityBypass === true,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g,
|
match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g,
|
||||||
replace: ""
|
replace: ""
|
||||||
|
@ -186,34 +245,61 @@ export default definePlugin({
|
||||||
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
find: 'jumboable?"jumbo":"default"',
|
|
||||||
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
|
||||||
replacement: {
|
|
||||||
match: /jumboable\?"jumbo":"default",emojiId.+?}}\)},(?<=(\i)=function\(\i\){var \i=\i\.node.+?)/,
|
|
||||||
replace: (m, component) => `${m}fakeNitroEmojiComponentExport=($self.EmojiComponent=${component},void 0),`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||||
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
predicate: () => settings.store.transformEmojis,
|
||||||
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
||||||
replace: (m, content) => `${m}||${content}[0].target?.startsWith("https://cdn.discordapp.com/emojis/")`
|
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
||||||
replace: (_, content) => `${content}=$self.patchFakeNitroEmojis(${content},arguments[2]?.formatInline);`
|
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "renderEmbeds=function",
|
find: "renderEmbeds=function",
|
||||||
predicate: () => Settings.plugins.FakeNitro.transformEmojis === true,
|
replacement: [
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
|
match: /(renderEmbeds=function\((\i)\){)(.+?embeds\.map\(\(function\((\i)\){)/,
|
||||||
|
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.transformStickers,
|
||||||
|
match: /renderStickersAccessories=function\((\i)\){var (\i)=\(0,\i\.\i\)\(\i\),/,
|
||||||
|
replace: (m, message, stickers) => `${m}${stickers}=$self.patchFakeNitroStickers(${stickers},${message}),`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.transformStickers,
|
||||||
|
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
|
||||||
|
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".STICKER_IN_MESSAGE_HOVER,",
|
||||||
|
predicate: () => settings.store.transformStickers,
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /var (\i)=\i\.renderableSticker,.{0,50}closePopout.+?channel:\i,closePopout:\i,/,
|
||||||
|
replace: (m, renderableSticker) => `${m}renderableSticker:${renderableSticker},`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /emojiSection.{0,50}description:\i(?<=(\i)\.sticker,.+?)(?=,)/,
|
||||||
|
replace: (m, props) => `${m}+(${props}.renderableSticker?.fake?" This is a Fake Nitro sticker. Only you can see it rendered like a real one, for non Vencord users it will show as a link.":"")`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
||||||
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /renderEmbeds=function\(\i\){.+?embeds\.map\(\(function\((\i)\){/,
|
match: /((\i)=\i\.node,\i=\i\.emojiSourceDiscoverableGuild)(.+?return) (.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
||||||
replace: (m, embed) => `${m}if(${embed}.url?.startsWith("https://cdn.discordapp.com/emojis/"))return null;`
|
replace: (_, rest1, node, rest2, messages) => `${rest1},fakeNitroNode=${node}${rest2}(${messages})+(fakeNitroNode.fake?" This is a Fake Nitro emoji. Only you can see it rendered like a real one, for non Vencord users it will show as a link.":"")`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -331,37 +417,146 @@ export default definePlugin({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
EmojiComponent: null as any,
|
patchFakeNitroEmojisOrRemoveStickersLinks(content: Array<any>, inline: boolean) {
|
||||||
|
if (content.length > 1) return content;
|
||||||
patchFakeNitroEmojis(content: Array<any>, inline: boolean) {
|
|
||||||
if (!this.EmojiComponent) return content;
|
|
||||||
|
|
||||||
const newContent: Array<any> = [];
|
const newContent: Array<any> = [];
|
||||||
|
|
||||||
|
let nextIndex = content.length;
|
||||||
|
|
||||||
for (const element of content) {
|
for (const element of content) {
|
||||||
if (element.props?.trusted == null) {
|
if (element.props?.trusted == null) {
|
||||||
newContent.push(element);
|
newContent.push(element);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fakeNitroMatch = element.props.href.match(/https:\/\/cdn\.discordapp\.com\/emojis\/(\d+?)\.(png|webp|gif).+?(?=\s|$)/);
|
if (settings.store.transformEmojis) {
|
||||||
if (!fakeNitroMatch) {
|
const fakeNitroMatch = element.props.href.match(fakeNitroEmojiRegex);
|
||||||
|
if (fakeNitroMatch) {
|
||||||
|
let url: URL | null = null;
|
||||||
|
try {
|
||||||
|
url = new URL(element.props.href);
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
const emojiName = EmojiStore.getCustomEmojiById(fakeNitroMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroEmoji";
|
||||||
|
|
||||||
|
newContent.push(Parser.defaultRules.customEmoji.react({
|
||||||
|
jumboable: !inline,
|
||||||
|
animated: fakeNitroMatch[2] === "gif",
|
||||||
|
emojiId: fakeNitroMatch[1],
|
||||||
|
name: emojiName,
|
||||||
|
fake: true
|
||||||
|
}, void 0, { key: String(nextIndex++) }));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.store.transformStickers) {
|
||||||
|
if (fakeNitroStickerRegex.test(element.props.href)) continue;
|
||||||
|
|
||||||
|
const gifMatch = element.props.href.match(fakeNitroGifStickerRegex);
|
||||||
|
if (gifMatch) {
|
||||||
|
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||||
|
if (StickerStore.getStickerById(gifMatch[1])) continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newContent.push(element);
|
newContent.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstTextElementIdx = newContent.findIndex(element => typeof element === "string");
|
||||||
|
if (firstTextElementIdx !== -1) newContent[firstTextElementIdx] = newContent[firstTextElementIdx].trimStart();
|
||||||
|
|
||||||
|
return newContent;
|
||||||
|
},
|
||||||
|
|
||||||
|
patchFakeNitroStickers(stickers: Array<any>, message: Message) {
|
||||||
|
const itemsToMaybePush: Array<string> = [];
|
||||||
|
|
||||||
|
const contentItems = message.content.split(/\s/);
|
||||||
|
if (contentItems.length === 1) itemsToMaybePush.push(contentItems[0]);
|
||||||
|
|
||||||
|
itemsToMaybePush.push(...message.attachments.filter(attachment => attachment.content_type === "image/gif").map(attachment => attachment.url));
|
||||||
|
|
||||||
|
for (const item of itemsToMaybePush) {
|
||||||
|
const imgMatch = item.match(fakeNitroStickerRegex);
|
||||||
|
if (imgMatch) {
|
||||||
|
let url: URL | null = null;
|
||||||
|
try {
|
||||||
|
url = new URL(item);
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||||
|
stickers.push({
|
||||||
|
format_type: 1,
|
||||||
|
id: imgMatch[1],
|
||||||
|
name: stickerName,
|
||||||
|
fake: true
|
||||||
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
newContent.push((
|
const gifMatch = item.match(fakeNitroGifStickerRegex);
|
||||||
<this.EmojiComponent node={{
|
if (gifMatch) {
|
||||||
type: "customEmoji",
|
if (!StickerStore.getStickerById(gifMatch[1])) continue;
|
||||||
jumboable: !inline && content.length === 1,
|
|
||||||
animated: fakeNitroMatch[2] === "gif",
|
const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||||
name: ":FakeNitroEmoji:",
|
stickers.push({
|
||||||
emojiId: fakeNitroMatch[1]
|
format_type: 2,
|
||||||
}} />
|
id: gifMatch[1],
|
||||||
));
|
name: stickerName,
|
||||||
|
fake: true
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newContent;
|
return stickers;
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldIgnoreEmbed(embed: Message["embeds"][number], message: Message) {
|
||||||
|
if (message.content.split(/\s/).length > 1) return false;
|
||||||
|
|
||||||
|
switch (embed.type) {
|
||||||
|
case "image": {
|
||||||
|
if (settings.store.transformEmojis) {
|
||||||
|
if (fakeNitroEmojiRegex.test(embed.url!)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.store.transformStickers) {
|
||||||
|
if (fakeNitroStickerRegex.test(embed.url!)) return true;
|
||||||
|
|
||||||
|
const gifMatch = embed.url!.match(fakeNitroGifStickerRegex);
|
||||||
|
if (gifMatch) {
|
||||||
|
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||||
|
if (StickerStore.getStickerById(gifMatch[1])) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
filterAttachments(attachments: Message["attachments"]) {
|
||||||
|
return attachments.filter(attachment => {
|
||||||
|
if (attachment.content_type !== "image/gif") return true;
|
||||||
|
|
||||||
|
const match = attachment.url.match(fakeNitroGifStickerRegex);
|
||||||
|
if (match) {
|
||||||
|
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||||
|
if (StickerStore.getStickerById(match[1])) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldKeepEmojiLink(link: any) {
|
||||||
|
return link.target && fakeNitroEmojiRegex.test(link.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
hasPermissionToUseExternalEmojis(channelId: string) {
|
hasPermissionToUseExternalEmojis(channelId: string) {
|
||||||
|
@ -407,8 +602,17 @@ export default definePlugin({
|
||||||
const scale = resolution / Math.max(width, height);
|
const scale = resolution / Math.max(width, height);
|
||||||
ctx.scale(scale, scale);
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
let lastImg: HTMLImageElement | null = null;
|
let previousFrameData: ImageData;
|
||||||
for (const { left, top, width, height, disposeOp, img, delay } of frames) {
|
|
||||||
|
for (const frame of frames) {
|
||||||
|
const { left, top, width, height, img, delay, blendOp, disposeOp } = frame;
|
||||||
|
|
||||||
|
previousFrameData = ctx.getImageData(left, top, width, height);
|
||||||
|
|
||||||
|
if (blendOp === ApngBlendOp.SOURCE) {
|
||||||
|
ctx.clearRect(left, top, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.drawImage(img, left, top, width, height);
|
ctx.drawImage(img, left, top, width, height);
|
||||||
|
|
||||||
const { data } = ctx.getImageData(0, 0, resolution, resolution);
|
const { data } = ctx.getImageData(0, 0, resolution, resolution);
|
||||||
|
@ -419,19 +623,18 @@ export default definePlugin({
|
||||||
gif.writeFrame(index, resolution, resolution, {
|
gif.writeFrame(index, resolution, resolution, {
|
||||||
transparent: true,
|
transparent: true,
|
||||||
palette,
|
palette,
|
||||||
delay,
|
delay
|
||||||
});
|
});
|
||||||
|
|
||||||
if (disposeOp === ApngDisposeOp.BACKGROUND) {
|
if (disposeOp === ApngDisposeOp.BACKGROUND) {
|
||||||
ctx.clearRect(left, top, width, height);
|
ctx.clearRect(left, top, width, height);
|
||||||
} else if (disposeOp === ApngDisposeOp.PREVIOUS && lastImg) {
|
} else if (disposeOp === ApngDisposeOp.PREVIOUS) {
|
||||||
ctx.drawImage(lastImg, left, top, width, height);
|
ctx.putImageData(previousFrameData, left, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastImg = img;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gif.finish();
|
gif.finish();
|
||||||
|
|
||||||
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
||||||
promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
||||||
},
|
},
|
||||||
|
@ -442,13 +645,6 @@ export default definePlugin({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmojiStore = findByPropsLazy("getCustomEmojiById");
|
|
||||||
const StickerStore = findByPropsLazy("getAllGuildStickers") as {
|
|
||||||
getPremiumPacks(): StickerPack[];
|
|
||||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
|
||||||
getStickerById(id: string): Sticker | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getWordBoundary(origStr: string, offset: number) {
|
function getWordBoundary(origStr: string, offset: number) {
|
||||||
return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " ";
|
return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " ";
|
||||||
}
|
}
|
||||||
|
@ -469,7 +665,7 @@ export default definePlugin({
|
||||||
|
|
||||||
let link = this.getStickerLink(sticker.id);
|
let link = this.getStickerLink(sticker.id);
|
||||||
if (sticker.format_type === 2) {
|
if (sticker.format_type === 2) {
|
||||||
this.sendAnimatedSticker(this.getStickerLink(sticker.id), sticker.id, channelId);
|
this.sendAnimatedSticker(link, sticker.id, channelId);
|
||||||
return { cancel: true };
|
return { cancel: true };
|
||||||
} else {
|
} else {
|
||||||
if ("pack_id" in sticker) {
|
if ("pack_id" in sticker) {
|
||||||
|
@ -483,7 +679,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
|
|
||||||
delete extra.stickerIds;
|
delete extra.stickerIds;
|
||||||
messageObj.content += " " + link;
|
messageObj.content += " " + link + `&name=${sticker.name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,7 +689,10 @@ export default definePlugin({
|
||||||
if (emoji.guildId === guildId && !emoji.animated) continue;
|
if (emoji.guildId === guildId && !emoji.animated) continue;
|
||||||
|
|
||||||
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
|
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
|
||||||
const url = emoji.url.replace(/\?size=\d+/, `?size=${Settings.plugins.FakeNitro.emojiSize}`);
|
const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({
|
||||||
|
size: Settings.plugins.FakeNitro.emojiSize,
|
||||||
|
name: encodeURIComponent(emoji.name)
|
||||||
|
}));
|
||||||
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
|
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
|
||||||
return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
|
return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
|
||||||
});
|
});
|
||||||
|
@ -513,7 +712,10 @@ export default definePlugin({
|
||||||
if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue;
|
if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue;
|
||||||
if (!emoji.require_colons) continue;
|
if (!emoji.require_colons) continue;
|
||||||
|
|
||||||
const url = emoji.url.replace(/\?size=\d+/, `?size=${Settings.plugins.FakeNitro.emojiSize}`);
|
const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({
|
||||||
|
size: Settings.plugins.FakeNitro.emojiSize,
|
||||||
|
name: encodeURIComponent(emoji.name)
|
||||||
|
}));
|
||||||
messageObj.content = messageObj.content.replace(emojiStr, (match, offset, origStr) => {
|
messageObj.content = messageObj.content.replace(emojiStr, (match, offset, origStr) => {
|
||||||
return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
|
return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,13 +77,8 @@ enum ChannelFlags {
|
||||||
REQUIRE_TAG = 1 << 4
|
REQUIRE_TAG = 1 << 4
|
||||||
}
|
}
|
||||||
|
|
||||||
let EmojiComponent: ComponentType<any>;
|
|
||||||
let ChannelBeginHeader: ComponentType<any>;
|
let ChannelBeginHeader: ComponentType<any>;
|
||||||
|
|
||||||
export function setEmojiComponent(component: ComponentType<any>) {
|
|
||||||
EmojiComponent = component;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setChannelBeginHeaderComponent(component: ComponentType<any>) {
|
export function setChannelBeginHeaderComponent(component: ComponentType<any>) {
|
||||||
ChannelBeginHeader = component;
|
ChannelBeginHeader = component;
|
||||||
}
|
}
|
||||||
|
@ -245,11 +240,10 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
{defaultReactionEmoji != null &&
|
{defaultReactionEmoji != null &&
|
||||||
<div className="shc-lock-screen-default-emoji-container">
|
<div className="shc-lock-screen-default-emoji-container">
|
||||||
<Text variant="text-md/normal">Default reaction emoji:</Text>
|
<Text variant="text-md/normal">Default reaction emoji:</Text>
|
||||||
<EmojiComponent node={{
|
{Parser.defaultRules[defaultReactionEmoji.emojiName ? "emoji" : "customEmoji"].react({
|
||||||
type: defaultReactionEmoji.emojiName ? "emoji" : "customEmoji",
|
|
||||||
name: defaultReactionEmoji.emojiName ?? "",
|
name: defaultReactionEmoji.emojiName ?? "",
|
||||||
emojiId: defaultReactionEmoji.emojiId
|
emojiId: defaultReactionEmoji.emojiId
|
||||||
}} />
|
})}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{channel.hasFlag(ChannelFlags.REQUIRE_TAG) &&
|
{channel.hasFlag(ChannelFlags.REQUIRE_TAG) &&
|
||||||
|
|
|
@ -27,7 +27,7 @@ import { findByPropsLazy } from "@webpack";
|
||||||
import { ChannelStore, PermissionStore, Tooltip } from "@webpack/common";
|
import { ChannelStore, PermissionStore, Tooltip } from "@webpack/common";
|
||||||
import { Channel } from "discord-types/general";
|
import { Channel } from "discord-types/general";
|
||||||
|
|
||||||
import HiddenChannelLockScreen, { setChannelBeginHeaderComponent, setEmojiComponent } from "./components/HiddenChannelLockScreen";
|
import HiddenChannelLockScreen, { setChannelBeginHeaderComponent } from "./components/HiddenChannelLockScreen";
|
||||||
|
|
||||||
const ChannelListClasses = findByPropsLazy("channelName", "subtitle", "modeMuted", "iconContainer");
|
const ChannelListClasses = findByPropsLazy("channelName", "subtitle", "modeMuted", "iconContainer");
|
||||||
|
|
||||||
|
@ -234,14 +234,6 @@ export default definePlugin({
|
||||||
replace: ".filter(ch=>!$self.isHiddenChannel(ch))"
|
replace: ".filter(ch=>!$self.isHiddenChannel(ch))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Export the emoji component used on the lock screen
|
|
||||||
{
|
|
||||||
find: 'jumboable?"jumbo":"default"',
|
|
||||||
replacement: {
|
|
||||||
match: /jumboable\?"jumbo":"default",emojiId.+?}}\)},(?<=(\i)=function\(\i\){var \i=\i\.node.+?)/,
|
|
||||||
replace: (m, component) => `${m}shcEmojiComponentExport=($self.setEmojiComponent(${component}),void 0),`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
find: ".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE",
|
find: ".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE",
|
||||||
replacement: [
|
replacement: [
|
||||||
|
@ -403,7 +395,6 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
setEmojiComponent,
|
|
||||||
setChannelBeginHeaderComponent,
|
setChannelBeginHeaderComponent,
|
||||||
|
|
||||||
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {
|
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {
|
||||||
|
|
Loading…
Reference in a new issue