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",
"private": "true",
"version": "1.2.4",
"version": "1.2.5",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {

View File

@ -18,6 +18,7 @@
import { generateId } from "@api/Commands";
import { useSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy";
@ -40,6 +41,7 @@ import {
SettingSliderComponent,
SettingTextComponent
} from "./components";
import hideBotTagStyle from "./userPopoutHideBotTag.css?managed";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
@ -50,11 +52,12 @@ interface PluginModalProps extends ModalProps {
onRestartNeeded(): void;
}
/** To stop discord making unwanted requests... */
function makeDummyUser(user: { name: string, id: BigInt; }) {
function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) {
const newUser = new UserRecord({
username: user.name,
id: generateId(),
username: user.username,
id: user.id ?? generateId(),
avatar: user.avatar,
/** To stop discord making unwanted requests... */
bot: true,
});
FluxDispatcher.dispatch({
@ -89,14 +92,27 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
const hasSettings = Boolean(pluginSettings && plugin.options);
React.useEffect(() => {
enableStyle(hideBotTagStyle);
let originalUser: User;
(async () => {
for (const user of plugin.authors.slice(0, 6)) {
const author = user.id
? await UserUtils.fetchUser(`${user.id}`).catch(() => makeDummyUser(user))
: makeDummyUser(user);
? await UserUtils.fetchUser(`${user.id}`)
// 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]);
}
})();
return () => {
disableStyle(hideBotTagStyle);
if (originalUser)
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
};
}, []);
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 { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { isPluginDev } from "@utils/misc";
import { closeModal, Modals, openModal } from "@utils/modal";
import definePlugin from "@utils/types";
import { Forms, Toasts } from "@webpack/common";
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 = {
description: "Vencord Contributor",
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
}
},
shouldShow: ({ user }) => contributorIds.includes(user.id),
shouldShow: ({ user }) => isPluginDev(user.id),
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_STICKERS = 1n << 37n;
enum EmojiIntentions {
const enum EmojiIntentions {
REACTION = 0,
STATUS = 1,
COMMUNITY_CONTENT = 2,
@ -66,6 +66,14 @@ enum EmojiIntentions {
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 {
available: boolean;
description: string;
@ -171,6 +179,10 @@ export default definePlugin({
{
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)))`
},
{
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);
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return true;
@ -623,8 +635,9 @@ export default definePlugin({
},
start() {
const settings = Settings.plugins.FakeNitro;
if (!settings.enableEmojiBypass && !settings.enableStickerBypass) {
const s = settings.store;
if (!s.enableEmojiBypass && !s.enableStickerBypass) {
return;
}
@ -636,39 +649,37 @@ export default definePlugin({
const { guildId } = this;
stickerBypass: {
if (!settings.enableStickerBypass)
if (!s.enableStickerBypass)
break stickerBypass;
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
if (!sticker)
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;
let link = this.getStickerLink(sticker.id);
if (sticker.format_type === 2) {
const canUseStickers = this.canUseStickers && this.hasPermissionToUseExternalStickers(channelId);
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);
return { cancel: true };
} 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;
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) {
if (!emoji.require_colons) continue;
if (emoji.available !== false && canUseEmotes) continue;
if (emoji.guildId === guildId && !emoji.animated) continue;
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
@ -686,23 +697,25 @@ export default definePlugin({
});
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;
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);
if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue;
if (!emoji.require_colons) continue;
if (emoji == null) return emojiStr;
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({
size: Settings.plugins.FakeNitro.emojiSize,
name: encodeURIComponent(emoji.name)
}));
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 + emojiStr.length)}`;
});
});
},

View File

@ -72,7 +72,7 @@ enum ActivityFlag {
INSTANCE = 1 << 0,
}
const applicationId = "1043533871037284423";
const applicationId = "1108588077900898414";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
const logger = new Logger("LastFMRichPresence");
@ -167,6 +167,7 @@ export default definePlugin({
settings,
start() {
this.updatePresence();
this.updateInterval = setInterval(() => { this.updatePresence(); }, 16000);
},
@ -198,7 +199,7 @@ export default definePlugin({
const trackData = json.recenttracks?.track[0];
if (!trackData || !trackData["@attr"]?.nowplaying)
if (!trackData?.["@attr"]?.nowplaying)
return null;
// 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 type { Guild, GuildMember } from "discord-types/general";
import { settings } from "..";
import { PermissionsSortOrder, settings } from "..";
import { cl, getPermissionString, getSortedRoles, sortUserRoles } from "../utils";
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>;
function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) {
const stns = settings.use(["permissionsSortOrder"]);
const [viewPermissions, setViewPermissions] = useState(settings.store.defaultPermissionsDropdownState);
const [rolePermissions, userPermissions] = useMemo(() => {
@ -91,7 +92,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM
userPermissions.sort((a, b) => b.rolePosition - a.rolePosition);
return [rolePermissions, userPermissions];
}, []);
}, [stns.permissionsSortOrder]);
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")}>
<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">
{tooltipProps => (
<button

View File

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

View File

@ -3,21 +3,38 @@
.vc-permviewer-userperms-title-container {
display: flex;
justify-content: space-between;
}
.vc-permviewer-userperms-title {
align-items: center;
margin-top: 10px;
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 {
all: unset;
cursor: pointer;
display: flex;
align-items: center;
}
.vc-permviewer-userperms-toggleperms-btn {
all: unset;
cursor: pointer;
display: flex;
align-items: center;
}
/* RolesAndUsersPermissions Component */

View File

@ -18,11 +18,12 @@
import { classNameFactory } from "@api/Styles";
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 type { ReactNode } from "react";
import { PermissionsSortOrder, settings } from ".";
import { PermissionType } from "./components/RolesAndUsersPermissions";
export const cl = classNameFactory("vc-permviewer-");
@ -82,3 +83,16 @@ export function sortUserRoles(roles: Role[]) {
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 openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
import { sortPermissionOverwrites } from "../../permissionsViewer/utils";
import { settings, VIEW_CHANNEL } from "..";
enum SortOrderTypes {
@ -169,12 +170,12 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
}
if (Settings.plugins.PermissionsViewer.enabled) {
setPermissions(Object.values(permissionOverwrites).map(overwrite => ({
setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({
type: overwrite.type as PermissionType,
id: overwrite.id,
overwriteAllow: overwrite.allow,
overwriteDeny: overwrite.deny
})));
})), guild_id));
}
}, [channelId]);

View File

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

View File

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

View File

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

View File

@ -89,7 +89,11 @@ const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
src={user.getAvatarURL(guildId, 128)} />
</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>
);
}, { noop: true });

View File

@ -17,8 +17,10 @@
*/
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 definePlugin from "@utils/types";
import definePlugin, { OptionType } from "@utils/types";
const endings = [
"rawr x3",
@ -65,6 +67,15 @@ const replacements = [
["meow", "nya~"],
];
const settings = definePluginSettings({
uwuEveryMessage: {
description: "Make every single message uwuified",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: false
}
});
function selectRandomElement(arr) {
// generate a random index based on the length of the array
const randomIndex = Math.floor(Math.random() * arr.length);
@ -94,8 +105,9 @@ function uwuify(message: string): string {
export default definePlugin({
name: "UwUifier",
description: "Simply uwuify commands",
authors: [Devs.echo, Devs.skyevg],
dependencies: ["CommandsAPI"],
authors: [Devs.echo, Devs.skyevg, Devs.PandaNinjas],
dependencies: ["CommandsAPI", "MessageEventsAPI"],
settings,
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 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({
Ven: {
name: "Vendicated",
@ -201,7 +212,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({
},
nick: {
name: "nick",
id: 347884694408265729n
id: 347884694408265729n,
badge: false
},
whqwert: {
name: "whqwert",
@ -287,6 +299,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "carince",
id: 818323528755314698n
},
PandaNinjas: {
name: "PandaNinjas",
id: 455128749071925248n
},
CatNoir: {
name: "CatNoir",
id: 260371016348336128n
@ -295,4 +311,17 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "outfoxxed",
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/>.
*/
import { findLazy } from "@webpack";
import { MessageObject } from "@api/MessageEvents";
import { findByPropsLazy, findLazy } from "@webpack";
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 MessageActions = findByPropsLazy("editMessage", "sendMessage");
export function getCurrentChannel() {
return ChannelStore.getChannel(SelectedChannelStore.getChannelId());
@ -49,3 +51,29 @@ export function insertTextIntoChatInputBox(text: string) {
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 { DevsById } from "./constants";
/**
* Recursively merges defaults into an object and returns the same 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
// "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 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");
type GenericStore = t.FluxStore & Record<string, any>;
export type GenericStore = t.FluxStore & Record<string, any>;
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
getMessages(chanId: string): any;
@ -37,6 +37,7 @@ export let PermissionStore: GenericStore;
export let GuildChannelStore: GenericStore;
export let ReadStateStore: GenericStore;
export let PresenceStore: GenericStore;
export let PoggerModeSettingsStore: GenericStore;
export let GuildStore: Stores.GuildStore & t.FluxStore;
export let UserStore: Stores.UserStore & t.FluxStore;