Make Messageclickactions Great Again! (#695)

forgive me father for i have sinned, and thank you prism for fixing my awful code
This commit is contained in:
Key 2026-01-27 22:49:28 +00:00 • committed by GitHub
parent b13b351dfb
commit bfc9ecb5e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,9 +11,9 @@ import { Devs, EquicordDevs } from "@utils/constants";
import { copyWithToast, insertTextIntoChatInputBox } from "@utils/discord";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import type { Message } from "@vencord/discord-types";
import type { Channel, Message } from "@vencord/discord-types";
import { ApplicationIntegrationType, MessageFlags } from "@vencord/discord-types/enums";
import { AuthenticationStore, Constants, EditMessageStore, FluxDispatcher, MessageActions, MessageTypeSets, PermissionsBits, PermissionStore, PinActions, RestAPI, showToast, Toasts, WindowStore } from "@webpack/common";
import { AuthenticationStore, Constants, EditMessageStore, FluxDispatcher, MessageActions, MessageTypeSets, PermissionsBits, PermissionStore, PinActions, RestAPI, Toasts, WindowStore } from "@webpack/common";
type Modifier = "NONE" | "SHIFT" | "CTRL" | "ALT" | "BACKSPACE" | "DELETE";
type ClickAction = "NONE" | "DELETE" | "COPY_LINK" | "COPY_ID" | "COPY_CONTENT" | "COPY_USER_ID" | "EDIT" | "REPLY" | "REACT" | "OPEN_THREAD" | "OPEN_TAB" | "EDIT_REPLY" | "QUOTE" | "PIN";
@ -201,11 +201,20 @@ function showWarning(message: string) {
});
}
function isMessageReplyable(msg: Message) {
return MessageTypeSets.REPLYABLE.has(msg.type) && !msg.hasFlag(MessageFlags.EPHEMERAL);
}
const canSend = (channel: Channel) =>
!channel.guild_id || PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel);
async function toggleReaction(channelId: string, messageId: string, emoji: string, channel: { id: string; guild_id?: string | null; }, msg: Message) {
const canDelete = (msg: Message, channel: Channel) => {
const myId = AuthenticationStore.getId();
return msg.author.id === myId ||
PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) ||
msg.interactionMetadata?.authorizing_integration_owners?.[ApplicationIntegrationType.USER_INSTALL] === myId;
};
const canReply = (msg: Message) =>
MessageTypeSets.REPLYABLE.has(msg.type) && !msg.hasFlag(MessageFlags.EPHEMERAL);
async function toggleReaction(channelId: string, messageId: string, emoji: string, channel: Channel, msg: Message) {
const trimmed = emoji.trim();
if (!trimmed) return;
@ -241,37 +250,11 @@ async function toggleReaction(channelId: string, messageId: string, emoji: strin
}
}
async function copyMessageLink(msg: Message, channel: { id: string; guild_id?: string | null; }) {
const guildId = channel.guild_id ?? "@me";
const link = `https://discord.com/channels/${guildId}/${channel.id}/${msg.id}`;
try {
await navigator.clipboard.writeText(link);
showToast("Message link copied", Toasts.Type.SUCCESS);
} catch (e) {
new Logger("MessageClickActions").error("Failed to copy link:", e);
}
function copyLink(msg: Message, channel: Channel) {
copyWithToast(`https://discord.com/channels/${channel.guild_id ?? "@me"}/${channel.id}/${msg.id}`, "Link copied!");
}
async function copyMessageId(msg: Message) {
try {
await navigator.clipboard.writeText(msg.id);
showToast("Message ID copied", Toasts.Type.SUCCESS);
} catch (e) {
new Logger("MessageClickActions").error("Failed to copy message ID:", e);
}
}
async function copyUserId(msg: Message) {
try {
await navigator.clipboard.writeText(msg.author.id);
showToast("User ID copied", Toasts.Type.SUCCESS);
} catch (e) {
new Logger("MessageClickActions").error("Failed to copy user ID:", e);
}
}
function togglePin(channel: { id: string; }, msg: Message) {
function togglePin(channel: Channel, msg: Message) {
if (!PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel)) {
showWarning("Cannot pin: Missing permissions");
return;
@ -284,8 +267,8 @@ function togglePin(channel: { id: string; }, msg: Message) {
}
}
function quoteMessage(channel: { id: string; guild_id?: string | null; isPrivate?: () => boolean; }, msg: Message) {
if (!isMessageReplyable(msg)) {
function quoteMessage(channel: Channel, msg: Message) {
if (!canReply(msg)) {
showWarning("Cannot quote this message type");
return;
}
@ -314,13 +297,13 @@ function quoteMessage(channel: { id: string; guild_id?: string | null; isPrivate
}
}
function openInNewTab(msg: Message, channel: { id: string; guild_id?: string | null; }) {
function openInNewTab(msg: Message, channel: Channel) {
const guildId = channel.guild_id ?? "@me";
const link = `https://discord.com/channels/${guildId}/${channel.id}/${msg.id}`;
VencordNative.native.openExternal(link);
}
function openInThread(msg: Message, channel: { id: string; }) {
function openInThread(msg: Message, channel: Channel) {
FluxDispatcher.dispatch({
type: "OPEN_THREAD_FLOW_MODAL",
channelId: channel.id,
@ -331,16 +314,15 @@ function openInThread(msg: Message, channel: { id: string; }) {
async function executeAction(
action: ClickAction,
msg: Message,
channel: { id: string; guild_id?: string | null; isDM?: () => boolean; isSystemDM?: () => boolean; isPrivate?: () => boolean; },
channel: Channel,
event: MouseEvent
) {
const myId = AuthenticationStore.getId();
const isMe = msg.author.id === myId;
const isSelfInvokedUserApp = msg.interactionMetadata?.authorizing_integration_owners?.[ApplicationIntegrationType.USER_INSTALL] === myId;
switch (action) {
case "DELETE":
if (!(isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) || isSelfInvokedUserApp)) return;
if (!canDelete(msg, channel)) return;
if (msg.deleted) {
FluxDispatcher.dispatch({
@ -356,12 +338,12 @@ async function executeAction(
break;
case "COPY_LINK":
await copyMessageLink(msg, channel);
copyLink(msg, channel);
event.preventDefault();
break;
case "COPY_ID":
await copyMessageId(msg);
copyWithToast(msg.id, "Message ID copied!");
event.preventDefault();
break;
@ -371,7 +353,7 @@ async function executeAction(
break;
case "COPY_USER_ID":
await copyUserId(msg);
copyWithToast(msg.author.id, "User ID copied!");
event.preventDefault();
break;
@ -383,8 +365,8 @@ async function executeAction(
break;
case "REPLY":
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
if (!canReply(msg)) return;
if (!canSend(channel)) return;
const isShiftPress = event.shiftKey;
const shouldMention = isPluginEnabled(NoReplyMentionPlugin.name)
@ -406,8 +388,8 @@ async function executeAction(
if (EditMessageStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return;
MessageActions.startEditMessage(channel.id, msg.id, msg.content);
} else {
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
if (!canReply(msg)) return;
if (!canSend(channel)) return;
const shouldMentionReply = isPluginEnabled(NoReplyMentionPlugin.name)
? NoReplyMentionPlugin.shouldMention(msg, false)
@ -490,10 +472,6 @@ export default definePlugin({
let target = event.target as HTMLElement;
if (target.nodeType === Node.TEXT_NODE) target = target.parentElement as HTMLElement;
// dont look, please just dont
if (target.closest('a, button, input, img, video, audio, [role="button"], [role="link"], [role="menuitem"], [class*="repliedTextPreview"], [class*="threadMessageAccessory"], [class*="spoilerText"], [class*="mention"]')) return;
if (!target.closest('[class*="message"]')) return;
const myId = AuthenticationStore.getId();
const isMe = msg.author.id === myId;
const isDM = channel.isDM();
@ -545,7 +523,7 @@ export default definePlugin({
}
const executeDoubleClick = () => {
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
if (!canSend(channel)) return;
if (msg.deleted === true) return;
if (canDoubleClick) {
executeAction(doubleClickAction, msg, channel, event);