Merge remote-tracking branch 'upstream/dev'

This commit is contained in:
thororen1234 2024-05-27 21:15:25 -04:00
commit 9fbfcb019a
10 changed files with 174 additions and 26 deletions

View file

@ -19,10 +19,10 @@
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { relaunch } from "@utils/native"; import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
import definePlugin from "@utils/types"; import definePlugin, { StartAt } from "@utils/types";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { extract, filters, findAll, search } from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack";
import { React, ReactDOM } from "@webpack/common"; import * as Common from "@webpack/common";
import type { ComponentType } from "react"; import type { ComponentType } from "react";
const WEB_ONLY = (f: string) => () => { const WEB_ONLY = (f: string) => () => {
@ -34,7 +34,7 @@ export default definePlugin({
description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.", description: "Adds shorter Aliases for many things on the window. Run `shortcutList` for a list.",
authors: [Devs.Ven], authors: [Devs.Ven],
getShortcuts() { getShortcuts(): Record<PropertyKey, any> {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
const cache = new Map<string, unknown>(); const cache = new Map<string, unknown>();
@ -64,16 +64,17 @@ export default definePlugin({
let fakeRenderWin: WeakRef<Window> | undefined; let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f); const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps); const findByProps = newFindWrapper(filters.byProps);
return { return {
...Vencord.Webpack.Common, ...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Vencord.Webpack, wp: Webpack,
wpc: Webpack.wreq.c, wpc: { getter: () => Webpack.cache },
wreq: Webpack.wreq, wreq: { getter: () => Webpack.wreq },
wpsearch: search, wpsearch: search,
wpex: extract, wpex: extract,
wpexs: (code: string) => extract(Webpack.findModuleId(code)!), wpexs: (code: string) => extract(findModuleId(code)!),
find, find,
findAll, findAll: findAll,
findByProps, findByProps,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
findByCode: newFindWrapper(filters.byCode), findByCode: newFindWrapper(filters.byCode),
@ -82,10 +83,10 @@ export default definePlugin({
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
findStore: newFindWrapper(filters.byStoreName), findStore: newFindWrapper(filters.byStoreName),
PluginsApi: Vencord.Plugins, PluginsApi: { getter: () => Vencord.Plugins },
plugins: Vencord.Plugins.plugins, plugins: { getter: () => Vencord.Plugins.plugins },
Settings: Vencord.Settings, Settings: { getter: () => Vencord.Settings },
Api: Vencord.Api, Api: { getter: () => Vencord.Api },
reload: () => location.reload(), reload: () => location.reload(),
restart: IS_WEB ? WEB_ONLY("restart") : relaunch, restart: IS_WEB ? WEB_ONLY("restart") : relaunch,
canonicalizeMatch, canonicalizeMatch,
@ -115,21 +116,40 @@ export default definePlugin({
}); });
} }
ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
} }
}; };
}, },
startAt: StartAt.Init,
start() { start() {
const shortcuts = this.getShortcuts(); const shortcuts = this.getShortcuts();
window.shortcutList = shortcuts; window.shortcutList = {};
for (const [key, val] of Object.entries(shortcuts))
for (const [key, val] of Object.entries(shortcuts)) {
if (val.getter != null) {
Object.defineProperty(window.shortcutList, key, {
get: val.getter,
configurable: true,
enumerable: true
});
Object.defineProperty(window, key, {
get: () => window.shortcutList[key],
configurable: true,
enumerable: true
});
} else {
window.shortcutList[key] = val;
window[key] = val; window[key] = val;
}
}
}, },
stop() { stop() {
delete window.shortcutList; delete window.shortcutList;
for (const key in this.getShortcuts()) for (const key in this.getShortcuts()) {
delete window[key]; delete window[key];
} }
}
}); });

View file

@ -18,7 +18,7 @@ export default definePlugin({
type: OptionType.SELECT, type: OptionType.SELECT,
options: [ options: [
{ {
label: "Ctrl+Enter (Enter or Shift+Enter for new line)", label: "Ctrl+Enter (Enter or Shift+Enter for new line) (cmd+enter on macOS)",
value: "ctrl+enter" value: "ctrl+enter"
}, },
{ {
@ -54,7 +54,7 @@ export default definePlugin({
result = event.shiftKey; result = event.shiftKey;
break; break;
case "ctrl+enter": case "ctrl+enter":
result = event.ctrlKey; result = navigator.platform.includes("Mac") ? event.metaKey : event.ctrlKey;
break; break;
case "enter": case "enter":
result = !event.shiftKey && !event.ctrlKey; result = !event.shiftKey && !event.ctrlKey;

View file

@ -816,7 +816,7 @@ export default definePlugin({
}, },
canUseEmote(e: Emoji, channelId: string) { canUseEmote(e: Emoji, channelId: string) {
if (e.type === "UNICODE") return true; if (e.type === 0) return true;
if (e.available === false) return false; if (e.available === false) return false;
const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji;

View file

@ -52,6 +52,8 @@ export default definePlugin({
getFriendSince(userId: string) { getFriendSince(userId: string) {
try { try {
if (!RelationshipStore.isFriend(userId)) return null;
return RelationshipStore.getSince(userId); return RelationshipStore.getSince(userId);
} catch (err) { } catch (err) {
new Logger("FriendsSince").error(err); new Logger("FriendsSince").error(err);
@ -60,6 +62,8 @@ export default definePlugin({
}, },
friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => { friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
if (!RelationshipStore.isFriend(userId)) return null;
const friendsSince = RelationshipStore.getSince(userId); const friendsSince = RelationshipStore.getSince(userId);
if (!friendsSince) return null; if (!friendsSince) return null;

View file

@ -97,6 +97,9 @@ export default definePlugin({
// Message wasn't received through gateway // Message wasn't received through gateway
if (!isNonNullish(nonce)) return null; if (!isNonNullish(nonce)) return null;
// Bots basically never send a nonce, and if someone does do it then it's usually not a snowflake
if (message.bot) return null;
let isDiscordKotlin = false; let isDiscordKotlin = false;
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
if (!showMillis) { if (!showMillis) {

View file

@ -37,7 +37,7 @@ const settings = definePluginSettings({
}); });
function search(src: string, engine: string) { function search(src: string, engine: string) {
open(engine + encodeURIComponent(src), "_blank"); open(engine + encodeURIComponent(src.trim()), "_blank");
} }
function makeSearchItem(src: string) { function makeSearchItem(src: string) {

View file

@ -0,0 +1,7 @@
# Summaries
Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations.
Note that this plugin can't fetch old summaries, it can only display ones created while your Discord is running with the plugin enabled.
![](https://github.com/Vendicated/Vencord/assets/45497981/bd931b0c-2e85-4c10-9f7c-8ba01eb55745)

View file

@ -0,0 +1,114 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { DataStore } from "@api/index";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, GuildStore } from "@webpack/common";
const SummaryStore = findByPropsLazy("allSummaries", "findSummary");
const { createSummaryFromServer } = findByPropsLazy("createSummaryFromServer");
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 ChannelSummaries {
type: string;
channel_id: string;
guild_id: string;
summaries: Summary[];
// custom property
time?: number;
}
export default definePlugin({
name: "Summaries",
description: "Enables Discord's experimental Summaries feature on every server, displaying AI generated summaries of conversations",
authors: [Devs.mantikafasi],
settings,
patches: [
{
find: "ChannelTypesSets.SUMMARIZEABLE.has",
replacement: {
match: /\i\.hasFeature\(\i\.GuildFeatures\.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: ChannelSummaries[] = data.summaries.map((summary: any) => ({ ...createSummaryFromServer(summary), 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 => {
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);
// @ts-ignore
return guild.hasFeature("SUMMARIES_ENABLED_GA");
}
});

File diff suppressed because one or more lines are too long

View file

@ -63,7 +63,7 @@ export interface CustomEmoji {
originalName?: string; originalName?: string;
require_colons: boolean; require_colons: boolean;
roles: string[]; roles: string[];
type: "GUILD_EMOJI"; type: 1;
} }
export interface UnicodeEmoji { export interface UnicodeEmoji {
@ -75,7 +75,7 @@ export interface UnicodeEmoji {
}; };
index: number; index: number;
surrogates: string; surrogates: string;
type: "UNICODE"; type: 0;
uniqueName: string; uniqueName: string;
useSpriteSheet: boolean; useSpriteSheet: boolean;
get allNamesString(): string; get allNamesString(): string;