Compare commits

...

11 Commits

Author SHA1 Message Date
V
a94787a9f3 Bump to 1.2.5 2023-05-23 03:50:21 +02:00
V
368d2bcdbb DiscordUtils: Add sendMessage 2023-05-23 03:47:09 +02:00
UwU
bc46bfa467 New plugin : Party mode 🎉 (#1161)
Co-authored-by: ArjixWasTaken <53124886+ArjixWasTaken@users.noreply.github.com>
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:32:27 +02:00
V
dab48288a8 TypingTweaks: Fix type error 2023-05-23 03:22:48 +02:00
alexia
9aef97c771 fix(TypingTweaks): use global displayName over username (#1165)
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:19:26 +02:00
PandaNinjas
9d62dec6b9 Uwufy: Add option to uwufy all messages (#1036)
Co-authored-by: V <vendicated@riseup.net>
2023-05-23 03:13:21 +02:00
V
6bf6583e7d FakeNitro: Fix unavailable emotes; Discord Stickers are now free (#1184)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-05-23 03:02:48 +02:00
Nuckyz
5219fb700f PermViewer: Add ability to change sort order; Properly center (#1182) 2023-05-23 02:22:25 +02:00
V
184c03b28e PluginModal: Anonymise authors (#1176) 2023-05-23 01:55:39 +02:00
Nuckyz
ec091a7959 Fix SHC broken patches; Sort PermViewer channel overwrites roles (#1166) 2023-05-20 02:24:56 +02:00
V
89a6c575c9 lastfm: Fix discord application 2023-05-18 05:11:06 +02:00
21 changed files with 358 additions and 71 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.2.4", "version": "1.2.5",
"description": "The cutest Discord client mod", "description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme", "homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": { "bugs": {

View File

@ -18,6 +18,7 @@
import { generateId } from "@api/Commands"; import { generateId } from "@api/Commands";
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy"; import { proxyLazy } from "@utils/lazy";
@ -40,6 +41,7 @@ import {
SettingSliderComponent, SettingSliderComponent,
SettingTextComponent SettingTextComponent
} from "./components"; } from "./components";
import hideBotTagStyle from "./userPopoutHideBotTag.css?managed";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
@ -50,11 +52,12 @@ interface PluginModalProps extends ModalProps {
onRestartNeeded(): void; onRestartNeeded(): void;
} }
/** To stop discord making unwanted requests... */ function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) {
function makeDummyUser(user: { name: string, id: BigInt; }) {
const newUser = new UserRecord({ const newUser = new UserRecord({
username: user.name, username: user.username,
id: generateId(), id: user.id ?? generateId(),
avatar: user.avatar,
/** To stop discord making unwanted requests... */
bot: true, bot: true,
}); });
FluxDispatcher.dispatch({ FluxDispatcher.dispatch({
@ -89,14 +92,27 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
const hasSettings = Boolean(pluginSettings && plugin.options); const hasSettings = Boolean(pluginSettings && plugin.options);
React.useEffect(() => { React.useEffect(() => {
enableStyle(hideBotTagStyle);
let originalUser: User;
(async () => { (async () => {
for (const user of plugin.authors.slice(0, 6)) { for (const user of plugin.authors.slice(0, 6)) {
const author = user.id const author = user.id
? await UserUtils.fetchUser(`${user.id}`).catch(() => makeDummyUser(user)) ? await UserUtils.fetchUser(`${user.id}`)
: makeDummyUser(user); // only show name & pfp and no actions so users cannot harass plugin devs for support (send dms, add as friend, etc)
.then(u => (originalUser = u, makeDummyUser(u)))
.catch(() => makeDummyUser({ username: user.name }))
: makeDummyUser({ username: user.name });
setAuthors(a => [...a, author]); setAuthors(a => [...a, author]);
} }
})(); })();
return () => {
disableStyle(hideBotTagStyle);
if (originalUser)
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
};
}, []); }, []);
async function saveAndClose() { async function saveAndClose() {

View File

@ -0,0 +1,3 @@
[class|="userPopoutOuter"] [class*="botTag"] {
display: none;
}

View File

@ -24,15 +24,13 @@ import { Heart } from "@components/Heart";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { isPluginDev } from "@utils/misc";
import { closeModal, Modals, openModal } from "@utils/modal"; import { closeModal, Modals, openModal } from "@utils/modal";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Forms, Toasts } from "@webpack/common"; import { Forms, Toasts } from "@webpack/common";
const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/attachments/1033680203433660458/1092089947126780035/favicon.png"; const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/attachments/1033680203433660458/1092089947126780035/favicon.png";
/** List of vencord contributor IDs */
const contributorIds: string[] = Object.values(Devs).map(d => d.id.toString());
const ContributorBadge: ProfileBadge = { const ContributorBadge: ProfileBadge = {
description: "Vencord Contributor", description: "Vencord Contributor",
image: CONTRIBUTOR_BADGE, image: CONTRIBUTOR_BADGE,
@ -43,7 +41,7 @@ const ContributorBadge: ProfileBadge = {
transform: "scale(0.9)" // The image is a bit too big compared to default badges transform: "scale(0.9)" // The image is a bit too big compared to default badges
} }
}, },
shouldShow: ({ user }) => contributorIds.includes(user.id), shouldShow: ({ user }) => isPluginDev(user.id),
link: "https://github.com/Vendicated/Vencord" link: "https://github.com/Vendicated/Vencord"
}; };

View File

@ -55,7 +55,7 @@ const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSe
const USE_EXTERNAL_EMOJIS = 1n << 18n; const USE_EXTERNAL_EMOJIS = 1n << 18n;
const USE_EXTERNAL_STICKERS = 1n << 37n; const USE_EXTERNAL_STICKERS = 1n << 37n;
enum EmojiIntentions { const enum EmojiIntentions {
REACTION = 0, REACTION = 0,
STATUS = 1, STATUS = 1,
COMMUNITY_CONTENT = 2, COMMUNITY_CONTENT = 2,
@ -66,6 +66,14 @@ enum EmojiIntentions {
SOUNDBOARD = 7 SOUNDBOARD = 7
} }
const enum StickerType {
PNG = 1,
APNG = 2,
LOTTIE = 3,
// don't think you can even have gif stickers but the docs have it
GIF = 4
}
interface BaseSticker { interface BaseSticker {
available: boolean; available: boolean;
description: string; description: string;
@ -171,6 +179,10 @@ export default definePlugin({
{ {
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/, match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))` replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
},
{
match: /if\(!\i\.available/,
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
} }
] ]
}, },
@ -542,7 +554,7 @@ export default definePlugin({
} }
}, },
hasPermissionToUseExternalEmojis(channelId: string) { hasPermissionToUseExternalEmojis(channelId: string): boolean {
const channel = ChannelStore.getChannel(channelId); const channel = ChannelStore.getChannel(channelId);
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return true; if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return true;
@ -623,8 +635,9 @@ export default definePlugin({
}, },
start() { start() {
const settings = Settings.plugins.FakeNitro; const s = settings.store;
if (!settings.enableEmojiBypass && !settings.enableStickerBypass) {
if (!s.enableEmojiBypass && !s.enableStickerBypass) {
return; return;
} }
@ -636,39 +649,37 @@ export default definePlugin({
const { guildId } = this; const { guildId } = this;
stickerBypass: { stickerBypass: {
if (!settings.enableStickerBypass) if (!s.enableStickerBypass)
break stickerBypass; break stickerBypass;
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!); const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
if (!sticker) if (!sticker)
break stickerBypass; break stickerBypass;
if (sticker.available !== false && ((this.canUseStickers && this.hasPermissionToUseExternalStickers(channelId)) || (sticker as GuildSticker)?.guild_id === guildId)) // Discord Stickers are now free yayyy!! :D
if ("pack_id" in sticker)
break stickerBypass; break stickerBypass;
let link = this.getStickerLink(sticker.id); const canUseStickers = this.canUseStickers && this.hasPermissionToUseExternalStickers(channelId);
if (sticker.format_type === 2) { if (sticker.available !== false && (canUseStickers || sticker.guild_id === guildId))
break stickerBypass;
const link = this.getStickerLink(sticker.id);
if (sticker.format_type === StickerType.APNG) {
this.sendAnimatedSticker(link, sticker.id, channelId); this.sendAnimatedSticker(link, sticker.id, channelId);
return { cancel: true }; return { cancel: true };
} else { } else {
if ("pack_id" in sticker) {
const packId = sticker.pack_id === "847199849233514549"
// Discord moved these stickers into a different pack at some point, but
// Distok still uses the old id
? "749043879713701898"
: sticker.pack_id;
link = `https://distok.top/stickers/${packId}/${sticker.id}.gif`;
}
extra.stickers!.length = 0; extra.stickers!.length = 0;
messageObj.content += " " + link + `&name=${encodeURIComponent(sticker.name)}`; messageObj.content += ` ${link}&name=${encodeURIComponent(sticker.name)}`;
} }
} }
if ((!this.canUseEmotes || !this.hasPermissionToUseExternalEmojis(channelId)) && settings.enableEmojiBypass) { if (s.enableEmojiBypass) {
const canUseEmotes = this.canUseEmotes && this.hasPermissionToUseExternalEmojis(channelId);
for (const emoji of messageObj.validNonShortcutEmojis) { for (const emoji of messageObj.validNonShortcutEmojis) {
if (!emoji.require_colons) continue; if (!emoji.require_colons) continue;
if (emoji.available !== false && canUseEmotes) continue;
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}>`;
@ -686,23 +697,25 @@ export default definePlugin({
}); });
this.preEdit = addPreEditListener((channelId, __, messageObj) => { this.preEdit = addPreEditListener((channelId, __, messageObj) => {
if (this.canUseEmotes && this.hasPermissionToUseExternalEmojis(channelId)) return; if (!s.enableEmojiBypass) return;
const canUseEmotes = this.canUseEmotes && this.hasPermissionToUseExternalEmojis(channelId);
const { guildId } = this; const { guildId } = this;
for (const [emojiStr, _, emojiId] of messageObj.content.matchAll(/(?<!\\)<a?:(\w+):(\d+)>/ig)) { messageObj.content = messageObj.content.replace(/(?<!\\)<a?:(?:\w+):(\d+)>/ig, (emojiStr, emojiId, offset, origStr) => {
const emoji = EmojiStore.getCustomEmojiById(emojiId); const emoji = EmojiStore.getCustomEmojiById(emojiId);
if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue; if (emoji == null) return emojiStr;
if (!emoji.require_colons) continue; if (!emoji.require_colons) return emojiStr;
if (emoji.available !== false && canUseEmotes) return emojiStr;
if (emoji.guildId === guildId && !emoji.animated) return emojiStr;
const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({ const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({
size: Settings.plugins.FakeNitro.emojiSize, size: Settings.plugins.FakeNitro.emojiSize,
name: encodeURIComponent(emoji.name) name: encodeURIComponent(emoji.name)
})); }));
messageObj.content = messageObj.content.replace(emojiStr, (match, offset, origStr) => { return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
}); });
}
}); });
}, },

View File

@ -72,7 +72,7 @@ enum ActivityFlag {
INSTANCE = 1 << 0, INSTANCE = 1 << 0,
} }
const applicationId = "1043533871037284423"; const applicationId = "1108588077900898414";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f"; const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
const logger = new Logger("LastFMRichPresence"); const logger = new Logger("LastFMRichPresence");
@ -167,6 +167,7 @@ export default definePlugin({
settings, settings,
start() { start() {
this.updatePresence();
this.updateInterval = setInterval(() => { this.updatePresence(); }, 16000); this.updateInterval = setInterval(() => { this.updatePresence(); }, 16000);
}, },
@ -198,7 +199,7 @@ export default definePlugin({
const trackData = json.recenttracks?.track[0]; const trackData = json.recenttracks?.track[0];
if (!trackData || !trackData["@attr"]?.nowplaying) if (!trackData?.["@attr"]?.nowplaying)
return null; return null;
// why does the json api have xml structure // why does the json api have xml structure

105
src/plugins/partyMode.ts Normal file
View File

@ -0,0 +1,105 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 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 definePlugin, { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack";
import { GenericStore } from "@webpack/common";
const PoggerModeSettingsStore: GenericStore = findStoreLazy("PoggermodeSettingsStore");
const enum Intensity {
Normal,
Better,
ProjectX,
}
const settings = definePluginSettings({
superIntensePartyMode: {
description: "Party intensity",
type: OptionType.SELECT,
options: [
{ label: "Normal", value: Intensity.Normal, default: true },
{ label: "Better", value: Intensity.Better },
{ label: "Project X", value: Intensity.ProjectX },
],
restartNeeded: false,
onChange: setSettings
},
});
export default definePlugin({
name: "Party mode 🎉",
description: "Allows you to use party mode cause the party never ends ✨",
authors: [Devs.UwUDev],
settings,
start() {
setPoggerState(true);
setSettings(settings.store.superIntensePartyMode);
},
stop() {
setPoggerState(false);
},
});
function setPoggerState(state: boolean) {
Object.assign(PoggerModeSettingsStore.__getLocalVars().state, {
enabled: state,
settingsVisible: state
});
}
function setSettings(intensity: Intensity) {
const state = {
screenshakeEnabledLocations: { 0: true, 1: true, 2: true },
shakeIntensity: 1,
confettiSize: 16,
confettiCount: 5,
combosRequiredCount: 1
};
switch (intensity) {
case Intensity.Normal: {
Object.assign(state, {
screenshakeEnabledLocations: { 0: true, 1: false, 2: false },
combosRequiredCount: 5
});
break;
}
case Intensity.Better: {
Object.assign(state, {
confettiSize: 12,
confettiCount: 8,
});
break;
}
case Intensity.ProjectX: {
Object.assign(state, {
shakeIntensity: 20,
confettiSize: 25,
confettiCount: 15,
});
break;
}
}
Object.assign(PoggerModeSettingsStore.__getLocalVars().state, state);
}

View File

@ -23,7 +23,7 @@ import { filters, findBulk } from "@webpack";
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore, useState } from "@webpack/common"; import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore, useState } from "@webpack/common";
import type { Guild, GuildMember } from "discord-types/general"; import type { Guild, GuildMember } from "discord-types/general";
import { settings } from ".."; import { PermissionsSortOrder, settings } from "..";
import { cl, getPermissionString, getSortedRoles, sortUserRoles } from "../utils"; import { cl, getPermissionString, getSortedRoles, sortUserRoles } from "../utils";
import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions"; import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions";
@ -46,6 +46,7 @@ const Classes = proxyLazy(() => {
}) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>; }) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>;
function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) { function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) {
const stns = settings.use(["permissionsSortOrder"]);
const [viewPermissions, setViewPermissions] = useState(settings.store.defaultPermissionsDropdownState); const [viewPermissions, setViewPermissions] = useState(settings.store.defaultPermissionsDropdownState);
const [rolePermissions, userPermissions] = useMemo(() => { const [rolePermissions, userPermissions] = useMemo(() => {
@ -91,7 +92,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM
userPermissions.sort((a, b) => b.rolePosition - a.rolePosition); userPermissions.sort((a, b) => b.rolePosition - a.rolePosition);
return [rolePermissions, userPermissions]; return [rolePermissions, userPermissions];
}, []); }, [stns.permissionsSortOrder]);
const { root, role, roleRemoveButton, roleNameOverflow, roles, rolePill, rolePillBorder, roleCircle, roleName } = Classes; const { root, role, roleRemoveButton, roleNameOverflow, roles, rolePill, rolePillBorder, roleCircle, roleName } = Classes;
@ -100,7 +101,28 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM
<div className={cl("userperms-title-container")}> <div className={cl("userperms-title-container")}>
<Text className={cl("userperms-title")} variant="eyebrow">Permissions</Text> <Text className={cl("userperms-title")} variant="eyebrow">Permissions</Text>
<div> <div className={cl("userperms-btns-container")}>
<Tooltip text={`Sorting by ${stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
{tooltipProps => (
<button
{...tooltipProps}
className={cl("userperms-sortorder-btn")}
onClick={() => {
stns.permissionsSortOrder = stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? PermissionsSortOrder.LowestRole : PermissionsSortOrder.HighestRole;
}}
>
<svg
width="20"
height="20"
viewBox="0 96 960 960"
transform={stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "scale(1 1)" : "scale(1 -1)"}
>
<path fill="var(--text-normal)" d="M440 896V409L216 633l-56-57 320-320 320 320-56 57-224-224v487h-80Z" />
</svg>
</button>
)}
</Tooltip>
<Tooltip text="Role Details"> <Tooltip text="Role Details">
{tooltipProps => ( {tooltipProps => (
<button <button

View File

@ -27,7 +27,7 @@ import type { Guild, GuildMember } from "discord-types/general";
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions";
import UserPermissions from "./components/UserPermissions"; import UserPermissions from "./components/UserPermissions";
import { getSortedRoles } from "./utils"; import { getSortedRoles, sortPermissionOverwrites } from "./utils";
export const enum PermissionsSortOrder { export const enum PermissionsSortOrder {
HighestRole, HighestRole,
@ -94,12 +94,12 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
case MenuItemParentType.Channel: { case MenuItemParentType.Channel: {
const channel = ChannelStore.getChannel(id!); const channel = ChannelStore.getChannel(id!);
permissions = Object.values(channel.permissionOverwrites).map(({ id, allow, deny, type }) => ({ permissions = sortPermissionOverwrites(Object.values(channel.permissionOverwrites).map(({ id, allow, deny, type }) => ({
type: type as PermissionType, type: type as PermissionType,
id, id,
overwriteAllow: allow, overwriteAllow: allow,
overwriteDeny: deny overwriteDeny: deny
})); })), guildId);
header = channel.name; header = channel.name;

View File

@ -3,21 +3,38 @@
.vc-permviewer-userperms-title-container { .vc-permviewer-userperms-title-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} align-items: center;
.vc-permviewer-userperms-title {
margin-top: 10px; margin-top: 10px;
margin-bottom: 6px; margin-bottom: 6px;
} }
.vc-permviewer-userperms-btns-container {
display: flex;
align-items: center;
}
.vc-permviewer-userperms-sortorder-btn {
all: unset;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
}
.vc-permviewer-userperms-permdetails-btn { .vc-permviewer-userperms-permdetails-btn {
all: unset; all: unset;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
} }
.vc-permviewer-userperms-toggleperms-btn { .vc-permviewer-userperms-toggleperms-btn {
all: unset; all: unset;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
} }
/* RolesAndUsersPermissions Component */ /* RolesAndUsersPermissions Component */

View File

@ -18,11 +18,12 @@
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { wordsToTitle } from "@utils/text"; import { wordsToTitle } from "@utils/text";
import { i18n, Parser } from "@webpack/common"; import { GuildStore, i18n, Parser } from "@webpack/common";
import { Guild, GuildMember, Role } from "discord-types/general"; import { Guild, GuildMember, Role } from "discord-types/general";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { PermissionsSortOrder, settings } from "."; import { PermissionsSortOrder, settings } from ".";
import { PermissionType } from "./components/RolesAndUsersPermissions";
export const cl = classNameFactory("vc-permviewer-"); export const cl = classNameFactory("vc-permviewer-");
@ -82,3 +83,16 @@ export function sortUserRoles(roles: Role[]) {
return roles; return roles;
} }
} }
export function sortPermissionOverwrites<T extends { id: string; type: number; }>(overwrites: T[], guildId: string) {
const guild = GuildStore.getGuild(guildId);
return overwrites.sort((a, b) => {
if (a.type !== PermissionType.Role || b.type !== PermissionType.Role) return 0;
const roleA = guild.roles[a.id];
const roleB = guild.roles[b.id];
return roleB.position - roleA.position;
});
}

View File

@ -26,6 +26,7 @@ import type { Channel } from "discord-types/general";
import type { ComponentType } from "react"; import type { ComponentType } from "react";
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
import { sortPermissionOverwrites } from "../../permissionsViewer/utils";
import { settings, VIEW_CHANNEL } from ".."; import { settings, VIEW_CHANNEL } from "..";
enum SortOrderTypes { enum SortOrderTypes {
@ -169,12 +170,12 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
} }
if (Settings.plugins.PermissionsViewer.enabled) { if (Settings.plugins.PermissionsViewer.enabled) {
setPermissions(Object.values(permissionOverwrites).map(overwrite => ({ setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({
type: overwrite.type as PermissionType, type: overwrite.type as PermissionType,
id: overwrite.id, id: overwrite.id,
overwriteAllow: overwrite.allow, overwriteAllow: overwrite.allow,
overwriteDeny: overwrite.deny overwriteDeny: overwrite.deny
}))); })), guild_id));
} }
}, [channelId]); }, [channelId]);

View File

@ -107,13 +107,13 @@ export default definePlugin({
}, },
{ {
// Prevent Discord from trying to connect to hidden channels // Prevent Discord from trying to connect to hidden channels
match: /(?=\|\|\i\.default\.selectVoiceChannel\((\i)\.id\))/, match: /if\(!\i&&!\i(?=.{0,50}?selectVoiceChannel\((\i)\.id\))/,
replace: (_, channel) => `||$self.isHiddenChannel(${channel})` replace: (m, channel) => `${m}&&!$self.isHiddenChannel(${channel})`
}, },
{ {
// Make Discord show inside the channel if clicking on a hidden or locked channel // Make Discord show inside the channel if clicking on a hidden or locked channel
match: /(?<=\|\|\i\.default\.selectVoiceChannel\((\i)\.id\);!__OVERLAY__&&\()/, match: /!__OVERLAY__&&\((?<=selectVoiceChannel\((\i)\.id\).+?)/,
replace: (_, channel) => `$self.isHiddenChannel(${channel},true)||` replace: (m, channel) => `${m}$self.isHiddenChannel(${channel},true)||`
} }
] ]
}, },
@ -195,7 +195,7 @@ export default definePlugin({
replace: (_, pushNotificationButtonExpression, channel) => `if($self.isHiddenChannel(${channel})){${pushNotificationButtonExpression}break;}` replace: (_, pushNotificationButtonExpression, channel) => `if($self.isHiddenChannel(${channel})){${pushNotificationButtonExpression}break;}`
}, },
{ {
match: /(?<=renderHeaderToolbar=function.+?case \i\.\i\.GUILD_FORUM:if\(!\i\){)(?=.+?;(.+?{channel:(\i)},"notifications"\)\)))/, match: /(?<=renderHeaderToolbar=function.+?case \i\.\i\.GUILD_FORUM:.+?if\(!\i\){)(?=.+?;(.+?{channel:(\i)},"notifications"\)\)))/,
replace: (_, pushNotificationButtonExpression, channel) => `if($self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}` replace: (_, pushNotificationButtonExpression, channel) => `if($self.isHiddenChannel(${channel})){${pushNotificationButtonExpression};break;}`
}, },
{ {

View File

@ -18,6 +18,7 @@
import { DataStore } from "@api/index"; import { DataStore } from "@api/index";
import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants"; import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants";
import { isPluginDev } from "@utils/misc";
import { makeCodeblock } from "@utils/text"; import { makeCodeblock } from "@utils/text";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { isOutdated } from "@utils/updater"; import { isOutdated } from "@utils/updater";
@ -74,8 +75,7 @@ ${makeCodeblock(Object.keys(plugins).filter(Vencord.Plugins.isPluginEnabled).joi
async CHANNEL_SELECT({ channelId }) { async CHANNEL_SELECT({ channelId }) {
if (channelId !== SUPPORT_CHANNEL_ID) return; if (channelId !== SUPPORT_CHANNEL_ID) return;
const myId = BigInt(UserStore.getCurrentUser().id); if (isPluginDev(UserStore.getCurrentUser().id)) return;
if (Object.values(Devs).some(d => d.id === myId)) return;
if (isOutdated && gitHash !== await DataStore.get(REMEMBER_DISMISS_KEY)) { if (isOutdated && gitHash !== await DataStore.get(REMEMBER_DISMISS_KEY)) {
const rememberDismiss = () => DataStore.set(REMEMBER_DISMISS_KEY, gitHash); const rememberDismiss = () => DataStore.set(REMEMBER_DISMISS_KEY, gitHash);

View File

@ -89,7 +89,7 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
<Tooltip text={tooltipText!}> <Tooltip text={tooltipText!}>
{({ onMouseLeave, onMouseEnter }) => ( {({ onMouseLeave, onMouseEnter }) => (
<div <div
style={{ marginLeft: 6, zIndex: 0, cursor: "pointer" }} style={{ marginLeft: 6, height: 16, display: "flex", alignItems: "center", zIndex: 0, cursor: "pointer" }}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
> >

View File

@ -89,7 +89,11 @@ const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
src={user.getAvatarURL(guildId, 128)} /> src={user.getAvatarURL(guildId, 128)} />
</div> </div>
)} )}
{GuildMemberStore.getNick(guildId!, user.id) || !guildId && RelationshipStore.getNickname(user.id) || user.username} {GuildMemberStore.getNick(guildId!, user.id)
|| (!guildId && RelationshipStore.getNickname(user.id))
|| (user as any).globalName
|| user.username
}
</strong> </strong>
); );
}, { noop: true }); }, { noop: true });

View File

@ -17,8 +17,10 @@
*/ */
import { findOption, RequiredMessageOption } from "@api/Commands"; import { findOption, RequiredMessageOption } from "@api/Commands";
import { addPreEditListener, addPreSendListener, MessageObject, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
const endings = [ const endings = [
"rawr x3", "rawr x3",
@ -65,6 +67,15 @@ const replacements = [
["meow", "nya~"], ["meow", "nya~"],
]; ];
const settings = definePluginSettings({
uwuEveryMessage: {
description: "Make every single message uwuified",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: false
}
});
function selectRandomElement(arr) { function selectRandomElement(arr) {
// generate a random index based on the length of the array // generate a random index based on the length of the array
const randomIndex = Math.floor(Math.random() * arr.length); const randomIndex = Math.floor(Math.random() * arr.length);
@ -94,8 +105,9 @@ function uwuify(message: string): string {
export default definePlugin({ export default definePlugin({
name: "UwUifier", name: "UwUifier",
description: "Simply uwuify commands", description: "Simply uwuify commands",
authors: [Devs.echo, Devs.skyevg], authors: [Devs.echo, Devs.skyevg, Devs.PandaNinjas],
dependencies: ["CommandsAPI"], dependencies: ["CommandsAPI", "MessageEventsAPI"],
settings,
commands: [ commands: [
{ {
@ -108,4 +120,23 @@ export default definePlugin({
}), }),
}, },
], ],
onSend(msg: MessageObject) {
// Only run when it's enabled
if (settings.store.uwuEveryMessage) {
msg.content = uwuify(msg.content);
}
},
start() {
this.preSend = addPreSendListener((_, msg) => this.onSend(msg));
this.preEdit = addPreEditListener((_cid, _mid, msg) =>
this.onSend(msg)
);
},
stop() {
removePreSendListener(this.preSend);
removePreEditListener(this.preEdit);
},
}); });

View File

@ -29,7 +29,18 @@ export const REACT_GLOBAL = "Vencord.Webpack.Common.React";
export const VENCORD_USER_AGENT = `Vencord/${gitHash}${gitRemote ? ` (https://github.com/${gitRemote})` : ""}`; export const VENCORD_USER_AGENT = `Vencord/${gitHash}${gitRemote ? ` (https://github.com/${gitRemote})` : ""}`;
export const SUPPORT_CHANNEL_ID = "1026515880080842772"; export const SUPPORT_CHANNEL_ID = "1026515880080842772";
// Add yourself here if you made a plugin export interface Dev {
name: string;
id: bigint;
badge?: boolean;
}
/**
* If you made a plugin or substantial contribution, add yourself here.
* This object is used for the plugin author list, as well as to add a contributor badge to your profile.
* If you wish to stay fully anonymous, feel free to set ID to 0n.
* If you are fine with attribution but don't want the badge, add badge: false
*/
export const Devs = /* #__PURE__*/ Object.freeze({ export const Devs = /* #__PURE__*/ Object.freeze({
Ven: { Ven: {
name: "Vendicated", name: "Vendicated",
@ -201,7 +212,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
}, },
nick: { nick: {
name: "nick", name: "nick",
id: 347884694408265729n id: 347884694408265729n,
badge: false
}, },
whqwert: { whqwert: {
name: "whqwert", name: "whqwert",
@ -287,6 +299,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "carince", name: "carince",
id: 818323528755314698n id: 818323528755314698n
}, },
PandaNinjas: {
name: "PandaNinjas",
id: 455128749071925248n
},
CatNoir: { CatNoir: {
name: "CatNoir", name: "CatNoir",
id: 260371016348336128n id: 260371016348336128n
@ -295,4 +311,17 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "outfoxxed", name: "outfoxxed",
id: 837425748435796060n id: 837425748435796060n
}, },
}); UwUDev: {
name: "UwU",
id: 691413039156690994n,
},
} satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly
export const DevsById = /* #__PURE__*/ (() =>
Object.freeze(Object.fromEntries(
Object.entries(Devs)
.filter(d => d[1].id !== 0n)
.map(([_, v]) => [v.id, v] as const)
))
)() as Record<string, Dev>;

View File

@ -16,11 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { findLazy } from "@webpack"; import { MessageObject } from "@api/MessageEvents";
import { findByPropsLazy, findLazy } from "@webpack";
import { ChannelStore, ComponentDispatch, GuildStore, PrivateChannelsStore, SelectedChannelStore } from "@webpack/common"; import { ChannelStore, ComponentDispatch, GuildStore, PrivateChannelsStore, SelectedChannelStore } from "@webpack/common";
import { Guild } from "discord-types/general"; import { Guild, Message } from "discord-types/general";
const PreloadedUserSettings = findLazy(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings")); const PreloadedUserSettings = findLazy(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"));
const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export function getCurrentChannel() { export function getCurrentChannel() {
return ChannelStore.getChannel(SelectedChannelStore.getChannelId()); return ChannelStore.getChannel(SelectedChannelStore.getChannelId());
@ -49,3 +51,29 @@ export function insertTextIntoChatInputBox(text: string) {
plainText: text plainText: text
}); });
} }
interface MessageExtra {
messageReference: Message["messageReference"];
allowedMentions: {
parse: string[];
replied_user: boolean;
};
stickerIds: string[];
}
export function sendMessage(
channelId: string,
data: Partial<MessageObject>,
waitForChannelReady?: boolean,
extra?: Partial<MessageExtra>
) {
const messageData = {
content: "",
invalidEmojis: [],
tts: false,
validNonShortcutEmojis: [],
...data
};
return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra);
}

View File

@ -18,6 +18,8 @@
import { Clipboard, Toasts } from "@webpack/common"; import { Clipboard, Toasts } from "@webpack/common";
import { DevsById } from "./constants";
/** /**
* Recursively merges defaults into an object and returns the same object * Recursively merges defaults into an object and returns the same object
* @param obj Object * @param obj Object
@ -100,3 +102,5 @@ export function identity<T>(value: T): T {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#mobile_tablet_or_desktop // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#mobile_tablet_or_desktop
// "In summary, we recommend looking for the string Mobi anywhere in the User Agent to detect a mobile device." // "In summary, we recommend looking for the string Mobi anywhere in the User Agent to detect a mobile device."
export const isMobile = navigator.userAgent.includes("Mobi"); export const isMobile = navigator.userAgent.includes("Mobi");
export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id);

View File

@ -25,7 +25,7 @@ import * as t from "./types/stores";
export const Flux: t.Flux = findByPropsLazy("connectStores"); export const Flux: t.Flux = findByPropsLazy("connectStores");
type GenericStore = t.FluxStore & Record<string, any>; export type GenericStore = t.FluxStore & Record<string, any>;
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & { export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
getMessages(chanId: string): any; getMessages(chanId: string): any;
@ -37,6 +37,7 @@ export let PermissionStore: GenericStore;
export let GuildChannelStore: GenericStore; export let GuildChannelStore: GenericStore;
export let ReadStateStore: GenericStore; export let ReadStateStore: GenericStore;
export let PresenceStore: GenericStore; export let PresenceStore: GenericStore;
export let PoggerModeSettingsStore: GenericStore;
export let GuildStore: Stores.GuildStore & t.FluxStore; export let GuildStore: Stores.GuildStore & t.FluxStore;
export let UserStore: Stores.UserStore & t.FluxStore; export let UserStore: Stores.UserStore & t.FluxStore;