diff --git a/package.json b/package.json
index 70ffb7bdf..14c54b0fe 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "equicord",
"private": "true",
- "version": "1.15.6.0",
+ "version": "1.15.7.0",
"description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": {
diff --git a/src/components/settings/tabs/patchHelper/PatchPreview.tsx b/src/components/settings/tabs/patchHelper/PatchPreview.tsx
index e294280da..9fde3b2a2 100644
--- a/src/components/settings/tabs/patchHelper/PatchPreview.tsx
+++ b/src/components/settings/tabs/patchHelper/PatchPreview.tsx
@@ -120,7 +120,7 @@ export function PatchPreview({ module, match, replacement, setReplacementError }
return (
<>
- Module {id}
+ Module {id}
diff --git a/src/plugins/appleMusic.desktop/README.md b/src/plugins/appleMusic.desktop/README.md
deleted file mode 100644
index 52ab93bfd..000000000
--- a/src/plugins/appleMusic.desktop/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# AppleMusicRichPresence
-
-This plugin enables Discord rich presence for your Apple Music! (This only works on macOS with the Music app.)
-
-
-
-## Configuration
-
-For the customizable activity format strings, you can use several special strings to include track data in activities! `{name}` is replaced with the track name; `{artist}` is replaced with the artist(s)' name(s); and `{album}` is replaced with the album name.
diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx
deleted file mode 100644
index 3153744fb..000000000
--- a/src/plugins/appleMusic.desktop/index.tsx
+++ /dev/null
@@ -1,311 +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 { Paragraph } from "@components/Paragraph";
-import { Devs, IS_MAC } from "@utils/constants";
-import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
-import { Activity, ActivityAssets, ActivityButton } from "@vencord/discord-types";
-import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "@vencord/discord-types/enums";
-import { ApplicationAssetUtils, FluxDispatcher } from "@webpack/common";
-
-const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative;
-
-export interface TrackData {
- name: string;
- album?: string;
- artist?: string;
-
- appleMusicLink?: string;
- appleMusicArtistLink?: string;
- songLink?: string;
-
- albumArtwork?: string;
- artistArtwork?: string;
-
- playerPosition?: number;
- duration?: number;
-}
-
-const enum AssetImageType {
- Album = "Album",
- Artist = "Artist",
- Disabled = "Disabled"
-}
-
-const enum LinkType {
- Album = "Album",
- Artist = "Artist",
- Disabled = "Disabled"
-}
-
-const applicationId = "1239490006054207550";
-
-let updateInterval: NodeJS.Timeout | undefined;
-
-function setActivity(activity: Activity | null) {
- FluxDispatcher.dispatch({
- type: "LOCAL_ACTIVITY_UPDATE",
- activity,
- socketId: "AppleMusic",
- });
-}
-
-const settings = definePluginSettings({
- activityType: {
- type: OptionType.SELECT,
- description: "Which type of activity",
- options: [
- { label: "Playing", value: ActivityType.PLAYING, default: true },
- { label: "Listening", value: ActivityType.LISTENING }
- ],
- },
- statusDisplayType: {
- description: "Show the track / artist name in the member list",
- type: OptionType.SELECT,
- options: [
- {
- label: "Don't show (shows generic listening message)",
- value: "off",
- default: true
- },
- {
- label: "Show artist name",
- value: "artist"
- },
- {
- label: "Show track name",
- value: "track"
- }
- ]
- },
- refreshInterval: {
- type: OptionType.SLIDER,
- description: "The interval between activity refreshes (seconds)",
- markers: [1, 2, 2.5, 3, 5, 10, 15],
- default: 5,
- restartNeeded: true,
- },
- enableTimestamps: {
- type: OptionType.BOOLEAN,
- description: "Whether or not to enable timestamps",
- default: true,
- },
- enableButtons: {
- type: OptionType.BOOLEAN,
- description: "Whether or not to enable buttons",
- default: true,
- },
- nameString: {
- type: OptionType.STRING,
- description: "Activity name format string",
- default: "Apple Music"
- },
- detailsString: {
- type: OptionType.STRING,
- description: "Activity details format string",
- default: "{name}"
- },
- stateString: {
- type: OptionType.STRING,
- description: "Activity state format string",
- default: "{artist} ยท {album}"
- },
- detailsLink: {
- type: OptionType.SELECT,
- description: "Activity details link",
- options: [
- { label: "Album", value: LinkType.Album, default: true },
- { label: "Artist", value: LinkType.Artist },
- { label: "Disabled", value: LinkType.Disabled }
- ],
- },
- stateLink: {
- type: OptionType.SELECT,
- description: "Activity state link",
- options: [
- { label: "Album", value: LinkType.Album },
- { label: "Artist", value: LinkType.Artist, default: true },
- { label: "Disabled", value: LinkType.Disabled }
- ],
- },
- largeImageType: {
- type: OptionType.SELECT,
- description: "Activity assets large image type",
- options: [
- { label: "Album artwork", value: AssetImageType.Album, default: true },
- { label: "Artist artwork", value: AssetImageType.Artist },
- { label: "Disabled", value: AssetImageType.Disabled }
- ],
- },
- largeTextString: {
- type: OptionType.STRING,
- description: "Activity assets large text format string",
- default: "{album}"
- },
- largeImageLink: {
- type: OptionType.SELECT,
- description: "Activity assets large image link",
- options: [
- { label: "Album", value: LinkType.Album, default: true },
- { label: "Artist", value: LinkType.Artist },
- { label: "Disabled", value: LinkType.Disabled }
- ],
- },
- smallImageType: {
- type: OptionType.SELECT,
- description: "Activity assets small image type",
- options: [
- { label: "Album artwork", value: AssetImageType.Album },
- { label: "Artist artwork", value: AssetImageType.Artist, default: true },
- { label: "Disabled", value: AssetImageType.Disabled }
- ],
- },
- smallTextString: {
- type: OptionType.STRING,
- description: "Activity assets small text format string",
- default: "{artist}"
- },
- smallImageLink: {
- type: OptionType.SELECT,
- description: "Activity assets small image link",
- options: [
- { label: "Album", value: LinkType.Album },
- { label: "Artist", value: LinkType.Artist, default: true },
- { label: "Disabled", value: LinkType.Disabled }
- ],
- },
-});
-
-function customFormat(formatStr: string, data: TrackData) {
- return formatStr
- .replaceAll("{name}", data.name)
- .replaceAll("{album}", data.album ?? "")
- .replaceAll("{artist}", data.artist ?? "");
-}
-
-function getLink(type: LinkType, data: TrackData) {
- return type === LinkType.Album
- ? data.appleMusicLink
- : type === LinkType.Artist
- ? data.appleMusicArtistLink
- : undefined;
-}
-
-function getImageAsset(type: AssetImageType, data: TrackData) {
- const source = type === AssetImageType.Album
- ? data.albumArtwork
- : data.artistArtwork;
-
- if (!source) return undefined;
-
- return ApplicationAssetUtils.fetchAssetIds(applicationId, [source]).then(ids => ids[0]);
-}
-
-export default definePlugin({
- name: "AppleMusicRichPresence",
- description: "Discord rich presence for your Apple Music!",
- tags: ["Activity", "Media"],
- authors: [Devs.RyanCaoDev],
- hidden: !IS_MAC,
- reporterTestable: ReporterTestable.None,
-
- settingsAboutComponent() {
- return <>
-
- For the customizable activity format strings, you can use several special strings to include track data in activities!{" "}
- {"{name}"} is replaced with the track name; {"{artist}"} is replaced with the artist(s)' name(s); and {"{album}"} is replaced with the album name.
-
- >;
- },
-
- settings,
-
- start() {
- this.updatePresence();
- updateInterval = setInterval(() => { this.updatePresence(); }, settings.store.refreshInterval * 1000);
- },
-
- stop() {
- clearInterval(updateInterval);
- updateInterval = undefined;
- FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null });
- },
-
- updatePresence() {
- this.getActivity().then(activity => { setActivity(activity); });
- },
-
- async getActivity(): Promise {
- const trackData = await Native.fetchTrackData();
- if (!trackData) return null;
-
- const [largeImageAsset, smallImageAsset] = await Promise.all([
- getImageAsset(settings.store.largeImageType, trackData),
- getImageAsset(settings.store.smallImageType, trackData)
- ]);
-
- const assets: ActivityAssets = {};
-
- const isRadio = Number.isNaN(trackData.duration) && (trackData.playerPosition === 0);
-
- if (settings.store.largeImageType !== AssetImageType.Disabled) {
- assets.large_image = largeImageAsset;
- if (!isRadio) assets.large_text = customFormat(settings.store.largeTextString, trackData);
- assets.large_url = getLink(settings.store.largeImageLink, trackData);
- }
-
- if (settings.store.smallImageType !== AssetImageType.Disabled) {
- assets.small_image = smallImageAsset;
- if (!isRadio) assets.small_text = customFormat(settings.store.smallTextString, trackData);
- assets.small_url = getLink(settings.store.smallImageLink, trackData);
- }
-
- const buttons: ActivityButton[] = [];
-
- if (settings.store.enableButtons) {
- if (trackData.appleMusicLink)
- buttons.push({
- label: "Listen on Apple Music",
- url: trackData.appleMusicLink,
- });
-
- if (trackData.songLink)
- buttons.push({
- label: "View on SongLink",
- url: trackData.songLink,
- });
- }
-
- return {
- application_id: applicationId,
-
- name: customFormat(settings.store.nameString, trackData),
- details: customFormat(settings.store.detailsString, trackData),
- state: isRadio ? undefined : customFormat(settings.store.stateString, trackData),
- details_url: getLink(settings.store.detailsLink, trackData),
- state_url: getLink(settings.store.stateLink, trackData),
-
- timestamps: (trackData.playerPosition && trackData.duration && settings.store.enableTimestamps) ? {
- start: Date.now() - (trackData.playerPosition * 1000),
- end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000),
- } : undefined,
-
- assets,
-
- buttons: !isRadio && buttons.length ? buttons.map(v => v.label) : undefined,
- metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
-
- type: settings.store.activityType,
- status_display_type: {
- "off": ActivityStatusDisplayType.NAME,
- "artist": ActivityStatusDisplayType.STATE,
- "track": ActivityStatusDisplayType.DETAILS
- }[settings.store.statusDisplayType],
- flags: ActivityFlags.INSTANCE,
- };
- }
-});
diff --git a/src/plugins/appleMusic.desktop/native.ts b/src/plugins/appleMusic.desktop/native.ts
deleted file mode 100644
index 8698ad426..000000000
--- a/src/plugins/appleMusic.desktop/native.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Vencord, a Discord client mod
- * Copyright (c) 2024 Vendicated and contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
-import { execFile } from "child_process";
-import { promisify } from "util";
-
-import type { TrackData } from ".";
-
-const exec = promisify(execFile);
-
-async function applescript(cmds: string[]) {
- const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat());
- return stdout;
-}
-
-interface RemoteData {
- appleMusicLink?: string,
- appleMusicArtistLink?: string;
- songLink?: string,
- albumArtwork?: string,
- artistArtwork?: string;
-}
-
-let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
-
-async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
- if (id === cachedRemoteData?.id) {
- if ("data" in cachedRemoteData) return cachedRemoteData.data;
- if ("failures" in cachedRemoteData && cachedRemoteData.failures >= 5) return null;
- }
-
- try {
- const dataUrl = new URL("https://itunes.apple.com/search");
- dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
- dataUrl.searchParams.set("media", "music");
- dataUrl.searchParams.set("entity", "song");
-
- const fetchData = () => fetch(dataUrl, {
- headers: {
- "user-agent": VENCORD_USER_AGENT,
- },
- }).then(r => r.json());
-
- let data = await fetchData();
-
- if (data.resultCount === 0) {
- dataUrl.searchParams.set("term", `${name} ${artist}`);
- data = await fetchData();
- }
-
- const songData = data.results.find(song => song.collectionName === album) || data.results[0];
-
- const artistArtworkURL = await fetch(songData.artistViewUrl)
- .then(r => r.text())
- .then(data => {
- const match = data.match(//);
- return match ? match[1].replace(/[0-9]+x.+/, "220x220bb-60.png") : undefined;
- })
- .catch(() => void 0);
-
- cachedRemoteData = {
- id,
- data: {
- appleMusicLink: songData.trackViewUrl,
- appleMusicArtistLink: songData.artistViewUrl,
- songLink: `https://song.link/i/${new URL(songData.trackViewUrl).searchParams.get("i")}`,
- albumArtwork: (songData.artworkUrl100).replace("100x100", "512x512"),
- artistArtwork: artistArtworkURL
- }
- };
-
- return cachedRemoteData.data;
- } catch (e) {
- console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e);
- cachedRemoteData = {
- id,
- failures: (id === cachedRemoteData?.id && "failures" in cachedRemoteData ? cachedRemoteData.failures : 0) + 1
- };
- return null;
- }
-}
-
-export async function fetchTrackData(): Promise {
- try {
- await exec("pgrep", ["^Music$"]);
- } catch (error) {
- return null;
- }
-
- const playerState = await applescript(['tell application "Music"', "get player state", "end tell"])
- .then(out => out.trim());
- if (playerState !== "playing") return null;
-
- const playerPosition = await applescript(['tell application "Music"', "get player position", "end tell"])
- .then(text => Number.parseFloat(text.trim()));
-
- const stdout = await applescript([
- 'set output to ""',
- 'tell application "Music"',
- "set t_id to database id of current track",
- "set t_name to name of current track",
- "set t_album to album of current track",
- "set t_artist to artist of current track",
- "set t_duration to duration of current track",
- 'set output to "" & t_id & "\\n" & t_name & "\\n" & t_album & "\\n" & t_artist & "\\n" & t_duration',
- "end tell",
- "return output"
- ]);
-
- const [id, name, album, artist, durationStr] = stdout.split("\n").filter(k => !!k);
- const duration = Number.parseFloat(durationStr);
-
- const remoteData = await fetchRemoteData({ id, name, artist, album });
-
- return { name, album, artist, playerPosition, duration, ...remoteData };
-}
diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx
index 3eeabab14..5db3183a0 100644
--- a/src/plugins/fakeNitro/index.tsx
+++ b/src/plugins/fakeNitro/index.tsx
@@ -27,7 +27,7 @@ import definePlugin, { OptionType } from "@utils/types";
import type { Emoji, Message, RenderModalProps, Sticker } from "@vencord/discord-types";
import { StickerFormatType } from "@vencord/discord-types/enums";
import { findByCodeLazy, findByPropsLazy, proxyLazyWebpack } from "@webpack";
-import { ChannelStore, ConfirmModal, DraftType, EmojiStore, FluxDispatcher, GuildMemberStore, IconUtils, lodash, openModal, OverridePremiumTypeStore, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserSettingsProtoStore } from "@webpack/common";
+import { ChannelStore, ConfirmModal, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, openModal, OverridePremiumTypeStore, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserSettingsProtoStore, UserStore } from "@webpack/common";
import { applyPalette, GIFEncoder, quantize } from "gifenc";
import type { ReactElement, ReactNode } from "react";
@@ -41,6 +41,8 @@ function searchProtoClassField(localName: string, protoClass: any) {
return fieldGetter?.();
}
+const logger = new Logger("FakeNitro");
+
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
@@ -334,20 +336,11 @@ export default definePlugin({
},
{
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
- replacement: [
- {
- // Call our function to decide whether the emoji link should be kept or not
- predicate: () => settings.store.transformEmojis,
- match: /(\i)=\(0,\i\.\i\)\((\i)\)(?=&&)(?<=\i=\1,\2=\(\i\?\?\i\)\.embeds.{0,50}?)/,
- replace: (m, content, _embeds) => `${m} && !$self.shouldKeepEmojiLink(${content}[0])`
- },
- {
- // Patch the rendered message content to add fake nitro emojis or remove sticker links
- predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
- match: /(?=return{hasSpoilerEmbeds:\i,hasBailedAst:\i,content:(\i))/,
- replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
- }
- ]
+ predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
+ replacement: {
+ match: /\(\{ast:(\i)(?=,inline:\i)/,
+ replace: "({ast:$self.transformAst($1)"
+ }
},
{
find: "}renderStickersAccessories(",
@@ -525,107 +518,58 @@ export default definePlugin({
if (!Array.isArray(child.props.children)) child.props.children = [child.props.children];
},
- patchFakeNitroEmojisOrRemoveStickersLinks(content: Array, inline: boolean) {
- // If content has more than one child or it's a single ReactElement like a header, list or span
- if ((content.length > 1 || typeof content[0]?.type === "string") && !settings.store.transformCompoundSentence) return content;
+ transformAst(ast: any[]) {
+ try {
+ if (!settings.store.transformCompoundSentence && ast.length > 1) {
+ return ast;
+ }
+ // Filter out sticker links. These are transformed into actual stickers later, so we don't want duplicate links
+ if (settings.store.transformStickers) {
+ ast = ast.filter(node => {
+ const { type, target } = node;
- let nextIndex = content.length;
+ if (type !== "link") return true;
+ if (fakeNitroStickerRegex.test(target)) return false;
+
+ const gifStickerLinkMatch = target.match(fakeNitroGifStickerRegex);
+ const isGifStickerLink = gifStickerLinkMatch && StickersStore.getStickerById(gifStickerLinkMatch[1]);
+
+ return !isGifStickerLink;
+ });
+ }
- const transformLinkChild = (child: ReactElement) => {
if (settings.store.transformEmojis) {
- const fakeNitroMatch = child.props.href.match(fakeNitroEmojiRegex);
- if (fakeNitroMatch) {
+ ast = ast.map(node => {
+ const { type, target } = node;
+
+ if (type !== "link") return node;
+
+ const fakeNitroMatch = target.match(fakeNitroEmojiRegex);
+ if (!fakeNitroMatch) return node;
+
let url: URL | null = null;
try {
- url = new URL(child.props.href);
+ url = new URL(target);
} catch { }
- const emojiName = EmojiStore.getCustomEmojiById(fakeNitroMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroEmoji";
- const isAnimated = fakeNitroMatch[2] === "gif" || url?.searchParams.get("animated") === "true";
+ const emojiId = fakeNitroMatch[1];
+ const emojiName = EmojiStore.getCustomEmojiById(emojiId)?.name ?? url?.searchParams.get("name") ?? "FakeNitroEmoji";
+ const animated = fakeNitroMatch[2] === "gif" || url?.searchParams.get("animated") === "true";
- return Parser.defaultRules.customEmoji.react({
- jumboable: !inline && content.length === 1 && typeof content[0].type !== "string",
- animated: isAnimated,
- emojiId: fakeNitroMatch[1],
- name: emojiName,
+ return {
+ animated,
+ emojiId,
+ name: `:${emojiName}:`,
+ type: "customEmoji",
fake: true
- }, void 0, { key: String(nextIndex++) });
- }
+ };
+ });
}
-
- if (settings.store.transformStickers) {
- if (fakeNitroStickerRegex.test(child.props.href)) return null;
-
- const gifMatch = child.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 StickersStore contains the id of the fake sticker
- if (StickersStore.getStickerById(gifMatch[1])) return null;
- }
- }
-
- return child;
- };
-
- const transformChild = (child: ReactElement) => {
- if (child?.props?.trusted != null) return transformLinkChild(child);
- if (child?.props?.children != null) {
- if (!Array.isArray(child.props.children)) {
- child.props.children = modifyChild(child.props.children);
- return child;
- }
-
- child.props.children = modifyChildren(child.props.children);
- if (child.props.children.length === 0) return null;
- return child;
- }
-
- return child;
- };
-
- const modifyChild = (child: ReactElement) => {
- const newChild = transformChild(child);
-
- if (newChild?.type === "ul" || newChild?.type === "ol") {
- this.ensureChildrenIsArray(newChild);
- if (newChild.props.children.length === 0) return null;
-
- let listHasAnItem = false;
- for (const [index, child] of newChild.props.children.entries()) {
- if (child == null) {
- delete newChild.props.children[index];
- continue;
- }
-
- this.ensureChildrenIsArray(child);
- if (child.props.children.length > 0) listHasAnItem = true;
- else delete newChild.props.children[index];
- }
-
- if (!listHasAnItem) return null;
-
- newChild.props.children = this.clearEmptyArrayItems(newChild.props.children);
- }
-
- return newChild;
- };
-
- const modifyChildren = (children: Array>) => {
- for (const [index, child] of children.entries()) children[index] = modifyChild(child);
-
- children = this.clearEmptyArrayItems(children);
-
- return children;
- };
-
- try {
- const newContent = modifyChildren(lodash.cloneDeep(content));
- this.trimContent(newContent);
-
- return newContent;
- } catch (err) {
- new Logger("FakeNitro").error(err);
- return content;
+ } catch (e) {
+ logger.error("Error transforming AST:", e);
}
+
+ return ast;
},
patchFakeNitroStickers(stickers: Array, message: Message) {
@@ -727,10 +671,6 @@ export default definePlugin({
});
},
- shouldKeepEmojiLink(link: any) {
- return link.target && fakeNitroEmojiRegex.test(link.target);
- },
-
addFakeNotice(type: FakeNoticeType, node: Array, fake: boolean) {
if (!fake) return node;
diff --git a/src/plugins/seeSummaries/README.md b/src/plugins/seeSummaries/README.md
deleted file mode 100644
index 1c8b6c793..000000000
--- a/src/plugins/seeSummaries/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Summaries
-
-Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations.
-
-Read more about summaries in the [official Discord help article](https://support.discord.com/hc/en-us/articles/12926016807575-In-Channel-Conversation-Summaries)!
-
-Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled.
-
-
diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx
deleted file mode 100644
index 1b5ff70c5..000000000
--- a/src/plugins/seeSummaries/index.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Vencord, a Discord client mod
- * Copyright (c) 2024 Vendicated and contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-import * as DataStore from "@api/DataStore";
-import { definePluginSettings } from "@api/Settings";
-import { Devs } from "@utils/constants";
-import { hasGuildFeature } from "@utils/discord";
-import definePlugin, { OptionType } from "@utils/types";
-import { findByPropsLazy } from "@webpack";
-import { ChannelStore, GuildStore } from "@webpack/common";
-
-const SummaryStore = findByPropsLazy("allSummaries", "findSummary");
-
-const settings = definePluginSettings({
- summaryExpiryThresholdDays: {
- type: OptionType.SLIDER,
- description: "The time in days before a summary is removed. Note that only up to 50 summaries are kept per channel",
- markers: [1, 3, 5, 7, 10, 15, 20, 25, 30],
- stickToMarkers: false,
- default: 3,
- }
-});
-
-interface Summary {
- count: number;
- end_id: string;
- id: string;
- message_ids: string[];
- people: string[];
- source: number;
- start_id: string;
- summ_short: string;
- topic: string;
- type: number;
- unsafe: boolean;
-}
-
-interface ChannelSummary {
- type: string;
- channel_id: string;
- guild_id: string;
- summaries: Summary[];
-
- // custom property
- time?: number;
-}
-// TODO: these types are wrong and evil and incorrect
-function createChannelSummaryFromServer(s: Summary, channelId: string): ChannelSummary {
- return {
- id: s.id,
- topic: s.topic,
- summShort: s.summ_short,
- people: Array.from(new Set(s.people)),
- startId: s.start_id,
- endId: s.end_id,
- count: s.count,
- channelId,
- source: s.source,
- type: s.type as any,
- } as any as ChannelSummary;
-}
-
-export default definePlugin({
- name: "Summaries",
- description: "Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations",
- tags: ["Chat", "Fun"],
- authors: [Devs.mantikafasi],
- settings,
- patches: [
- {
- find: "SUMMARIZEABLE.has",
- replacement: {
- match: /\i\.features\.has\(\i\.\i\.SUMMARIES_ENABLED\w+?\)/g,
- replace: "true"
- }
- },
- {
- find: "RECEIVE_CHANNEL_SUMMARY(",
- replacement: {
- match: /shouldFetch\((\i),\i\){/,
- replace: "$& if(!$self.shouldFetch($1)) return false;"
- }
- }
- ],
-
- flux: {
- CONVERSATION_SUMMARY_UPDATE(data) {
- const incomingSummaries: ChannelSummary[] = data.summaries.map((summary: any) => ({
- ...createChannelSummaryFromServer(summary, undefined!),
- time: Date.now()
- }));
-
- // idk if this is good for performance but it doesnt seem to be a problem in my experience
- DataStore.update("summaries-data", summaries => {
- summaries ??= {};
- summaries[data.channel_id] ? summaries[data.channel_id].unshift(...incomingSummaries) : (summaries[data.channel_id] = incomingSummaries);
- if (summaries[data.channel_id].length > 50)
- summaries[data.channel_id] = summaries[data.channel_id].slice(0, 50);
-
- return summaries;
- });
- }
- },
-
- async start() {
- await DataStore.update("summaries-data", summaries => {
- summaries ??= {};
- for (const key of Object.keys(summaries)) {
- for (let i = summaries[key].length - 1; i >= 0; i--) {
- if (summaries[key][i].time < Date.now() - 1000 * 60 * 60 * 24 * settings.store.summaryExpiryThresholdDays) {
- summaries[key].splice(i, 1);
- }
- }
-
- if (summaries[key].length === 0) {
- delete summaries[key];
- }
- }
-
- Object.assign(SummaryStore.allSummaries(), summaries);
- return summaries;
- });
- },
-
- shouldFetch(channelId: string) {
- const channel = ChannelStore.getChannel(channelId);
- // SUMMARIES_ENABLED feature is not in discord-types
- const guild = GuildStore.getGuild(channel.guild_id);
-
- return hasGuildFeature(guild, "SUMMARIES_ENABLED_GA");
- }
-});