Boo: Add Plugin

This commit is contained in:
thororen1234 2025-10-31 15:39:03 -04:00
parent b8261aba5e
commit 81a4ea94c9
No known key found for this signature in database
5 changed files with 167 additions and 0 deletions

View file

@ -0,0 +1,60 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Channel, Message } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { MessageStore, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
import { cl } from ".";
import { IconGhost } from "./IconGhost";
const countedChannels = new Set<string>();
export let booCount = 0;
const ChannelWrapperStyles = findByPropsLazy("muted", "wrapper");
export function Boo({ channel }: { channel: Channel; }) {
const { id } = channel;
const currentUserId = useStateFromStores([UserStore], () => UserStore.getCurrentUser()?.id);
const lastMessage: Message = useStateFromStores([MessageStore], () =>
MessageStore.getMessages(id)?.last()
);
const [state, setState] = useState({
isCurrentUser: null as boolean | null,
containsQuestionMark: false,
isDataProcessed: false,
});
useEffect(() => {
if (!lastMessage || !currentUserId) return;
const lastIsCurrentUser = lastMessage.author.id === currentUserId;
const containsQuestionMark = !lastIsCurrentUser && lastMessage.content.includes("?");
setState({
isCurrentUser: lastIsCurrentUser,
containsQuestionMark,
isDataProcessed: true,
});
}, [lastMessage, currentUserId]);
if (!state.isDataProcessed || !currentUserId || !lastMessage || state.isCurrentUser) return null;
if (!countedChannels.has(id)) {
countedChannels.add(id);
booCount++;
}
return (
<div className={cl("icon", ChannelWrapperStyles.wrapper)}>
{state.containsQuestionMark
? <IconGhost fill="#ff8000" />
: <IconGhost fill="currentColor" />}
</div>
);
}

View file

@ -0,0 +1,19 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export function IconGhost({ fill }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.5em"
height="1.5em"
viewBox="0 0 24 24"
>
<path fill={fill} d="M12 4a7 7 0 0 0-7 7v8.62a.4.4 0 0 0 .594.35l.943-.523a4 4 0 0 1 3.927.023l.544.311a2 2 0 0 0 1.984 0l.544-.31a4 4 0 0 1 3.927-.024l.943.523a.4.4 0 0 0 .594-.35V11a7 7 0 0 0-7-7m-9 7a9 9 0 0 1 18 0v8.62c0 1.83-1.966 2.987-3.565 2.099l-.943-.524a2 2 0 0 0-1.964.012l-.543.31a4 4 0 0 1-3.97 0l-.543-.31a2 2 0 0 0-1.964-.012l-.942.524C4.966 22.607 3 21.45 3 19.62zm7.5-.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0M15 12a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3" />
</svg>
);
}

View file

@ -0,0 +1,68 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import { Channel } from "@vencord/discord-types";
import { Tooltip } from "@webpack/common";
import { Boo, booCount } from "./Boo";
import { IconGhost } from "./IconGhost";
export const cl = classNameFactory("vc-boo-");
export default definePlugin({
name: "Boo",
description: "A cute ghost will appear if you don't answer their DMs",
authors: [EquicordDevs.vei, Devs.sadan],
patches: [
{
find: "interactiveSelected]",
replacement: {
match: /interactiveSelected.{0,50}children:\[/,
replace: "$&$self.renderBoo(arguments[0]),"
}
}
],
renderBoo(props: { channel: Channel; }) {
return (
<ErrorBoundary noop>
<Boo {...props} />
</ErrorBoundary>
);
},
renderIndicator() {
return <ErrorBoundary noop>
<div id={cl("container")}>
<Tooltip text={`${booCount} Ghosted Users`} position="right">
{({ onMouseEnter, onMouseLeave }) => (
<div
id={cl("container")}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}>
{booCount} <IconGhost fill="currentColor" />
</div>
)}
</Tooltip>
</div>
</ErrorBoundary>;
},
start() {
addServerListElement(ServerListRenderPosition.Above, this.renderIndicator);
},
stop() {
removeServerListElement(ServerListRenderPosition.Above, this.renderIndicator);
}
});

View file

@ -0,0 +1,16 @@
.vc-boo-icon {
height: 42px;
}
#vc-boo-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: var(--background-primary);
height: 30px;
width: 48px;
margin-left: 8px;
color: var(--text-default);
fill: var(--text-default);
}

View file

@ -1178,6 +1178,10 @@ export const EquicordDevs = Object.freeze({
name: "mja00",
id: 108698087769260032n
},
vei: {
name: "Vei",
id: 239414094799699968n
},
} satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly