Plugin

GreetStickerPicker

Allows you to use any greet sticker instead of only the random one by right-clicking the 'Wave to say hi!' button

Emotes Customisation
index.tsx
Download

Source

src/plugins/greetStickerPicker/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { definePluginSettings } from "@api/Settings";
8import { Devs } from "@utils/constants";
9import definePlugin, { OptionType } from "@utils/types";
10import { Channel, Message } from "@vencord/discord-types";
11import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common";
12
13enum GreetMode {
14 Greet = "Greet",
15 NormalMessage = "Message"
16}
17
18const settings = definePluginSettings({
19 greetMode: {
20 type: OptionType.SELECT,
21 options: [
22 { label: "Greet (you can only greet 3 times)", value: GreetMode.Greet, default: true },
23 { label: "Normal Message (you can greet spam)", value: GreetMode.NormalMessage }
24 ],
25 description: "Choose the greet mode"
26 }
27}).withPrivateSettings<{
28 multiGreetChoices?: string[];
29 unholyMultiGreetEnabled?: boolean;
30}>();
31
32let WELCOME_STICKERS: any;
33
34function greet(channel: Channel, message: Message, stickers: string[]) {
35 const options = MessageActions.getSendMessageOptionsForReply({
36 channel,
37 message,
38 shouldMention: true,
39 showMentionToggle: true
40 });
41
42 if (settings.store.greetMode === GreetMode.NormalMessage || stickers.length > 1) {
43 options.stickerIds = stickers;
44 const msg = {
45 content: "",
46 tts: false,
47 invalidEmojis: [],
48 validNonShortcutEmojis: []
49 };
50
51 MessageActions._sendMessage(channel.id, msg, options);
52 } else {
53 MessageActions.sendGreetMessage(channel.id, stickers[0], options);
54 }
55}
56
57
58function GreetMenu({ channel, message }: { message: Message, channel: Channel; }) {
59 const s = settings.use(["greetMode", "multiGreetChoices"]);
60 const { greetMode, multiGreetChoices = [] } = s;
61
62 return (
63 <Menu.Menu
64 navId="greet-sticker-picker"
65 onClose={() => FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })}
66 aria-label="Greet Sticker Picker"
67 >
68 <Menu.MenuGroup
69 label="Greet Mode"
70 >
71 {Object.values(GreetMode).map(mode => (
72 <Menu.MenuRadioItem
73 key={mode}
74 group="greet-mode"
75 id={"greet-mode-" + mode}
76 label={mode}
77 checked={mode === greetMode}
78 action={() => s.greetMode = mode}
79 />
80 ))}
81 </Menu.MenuGroup>
82
83 <Menu.MenuSeparator />
84
85 <Menu.MenuGroup
86 label="Greet Stickers"
87 >
88 {WELCOME_STICKERS.map(sticker => (
89 <Menu.MenuItem
90 key={sticker.id}
91 id={"greet-" + sticker.id}
92 label={sticker.description.split(" ")[0]}
93 action={() => greet(channel, message, [sticker.id])}
94 />
95 ))}
96 </Menu.MenuGroup>
97
98 {!settings.store.unholyMultiGreetEnabled ? null : (
99 <>
100 <Menu.MenuSeparator />
101
102 <Menu.MenuItem
103 label="Unholy Multi-Greet"
104 id="unholy-multi-greet"
105 >
106 {WELCOME_STICKERS.map(sticker => {
107 const checked = multiGreetChoices.some(s => s === sticker.id);
108
109 return (
110 <Menu.MenuCheckboxItem
111 key={sticker.id}
112 id={"multi-greet-" + sticker.id}
113 label={sticker.description.split(" ")[0]}
114 checked={checked}
115 disabled={!checked && multiGreetChoices.length >= 3}
116 action={() => {
117 s.multiGreetChoices = checked
118 ? multiGreetChoices.filter(s => s !== sticker.id)
119 : [...multiGreetChoices, sticker.id];
120 }}
121 />
122 );
123 })}
124
125 <Menu.MenuSeparator />
126 <Menu.MenuItem
127 id="multi-greet-submit"
128 label="Send Greets"
129 action={() => greet(channel, message, multiGreetChoices!)}
130 disabled={multiGreetChoices.length === 0}
131 />
132
133 </Menu.MenuItem>
134 </>
135 )}
136 </Menu.Menu>
137 );
138}
139
140export default definePlugin({
141 name: "GreetStickerPicker",
142 description: "Allows you to use any greet sticker instead of only the random one by right-clicking the &#039;Wave to say hi!&#039; button",
143 tags: ["Emotes", "Customisation"],
144 authors: [Devs.Ven],
145
146 settings,
147
148 patches: [
149 {
150 find: "#{intl::WELCOME_CTA_LABEL}",
151 replacement: {
152 match: /className:\i\.\i,(?=.{0,40}?"sticker")(?<={channel:\i,message:\i}=(\i).+?)/,
153 replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1),"
154 }
155 },
156 {
157 find: &#039;"Wumpus waves hello"&#039;,
158 replacement: {
159 match: /(?<==)(?=\[{id:"749054660769218631",format_type:\d,description:"Wumpus waves hello")/,
160 replace: "$self.WELCOME_STICKERS="
161 }
162 }
163 ],
164
165 set WELCOME_STICKERS(value: any) {
166 WELCOME_STICKERS = value;
167 },
168
169 pickSticker(
170 event: React.UIEvent,
171 props: {
172 channel: Channel,
173 message: Message;
174 }
175 ) {
176 if (!(props.message as any).deleted)
177 ContextMenuApi.openContextMenu(event, () => <GreetMenu {...props} />);
178 }
179});
180