Rewrite MessageLinkEmbeds to improve Code Quality
This commit is contained in:
parent
727297ec4e
commit
d6a3edefd9
@ -17,11 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { addAccessory } from "@api/MessageAccessories";
|
import { addAccessory } from "@api/MessageAccessories";
|
||||||
import { Settings } from "@api/settings";
|
import { definePluginSettings } from "@api/settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants.js";
|
import { Devs } from "@utils/constants.js";
|
||||||
|
import { classes, LazyComponent } from "@utils/misc";
|
||||||
import { Queue } from "@utils/Queue";
|
import { Queue } from "@utils/Queue";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByPropsLazy, waitFor } from "@webpack";
|
import { find, findByCode, findByPropsLazy } from "@webpack";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ChannelStore,
|
ChannelStore,
|
||||||
@ -36,41 +38,20 @@ import {
|
|||||||
} from "@webpack/common";
|
} from "@webpack/common";
|
||||||
import { Channel, Guild, Message } from "discord-types/general";
|
import { Channel, Guild, Message } from "discord-types/general";
|
||||||
|
|
||||||
let messageCache: { [id: string]: { message?: Message, fetched: boolean; }; } = {};
|
const messageCache = new Map<string, {
|
||||||
|
message?: Message;
|
||||||
|
fetched: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
let AutomodEmbed: React.ComponentType<any>,
|
const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed"));
|
||||||
Embed: React.ComponentType<any>,
|
const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes('["message","compact","className",')));
|
||||||
ChannelMessage: React.ComponentType<any>,
|
|
||||||
Endpoints: Record<string, any>;
|
|
||||||
|
|
||||||
waitFor(["mle_AutomodEmbed"], m => (AutomodEmbed = m.mle_AutomodEmbed));
|
|
||||||
waitFor(filters.byCode(".inlineMediaEmbed"), m => Embed = m);
|
|
||||||
waitFor(m => m.type?.toString()?.includes('["message","compact","className",'), m => ChannelMessage = m);
|
|
||||||
waitFor(["MESSAGE_CREATE_ATTACHMENT_UPLOAD"], _ => Endpoints = _);
|
|
||||||
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
||||||
|
|
||||||
const messageFetchQueue = new Queue();
|
let AutoModEmbed: React.ComponentType<any> = () => null;
|
||||||
async function fetchMessage(channelID: string, messageID: string): Promise<Message | void> {
|
|
||||||
if (messageID in messageCache && !messageCache[messageID].fetched) return Promise.resolve();
|
|
||||||
if (messageCache[messageID]?.fetched) return Promise.resolve(messageCache[messageID].message);
|
|
||||||
|
|
||||||
messageCache[messageID] = { fetched: false };
|
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
||||||
const res = await RestAPI.get({
|
const tenorRegex = /https:\/\/(?:www.)?tenor\.com/;
|
||||||
url: Endpoints.MESSAGES(channelID),
|
|
||||||
query: {
|
|
||||||
limit: 1,
|
|
||||||
around: messageID
|
|
||||||
},
|
|
||||||
retries: 2
|
|
||||||
}).catch(() => { });
|
|
||||||
const apiMessage = res.body?.[0];
|
|
||||||
const message: Message = MessageStore.getMessages(apiMessage.channel_id).receiveMessage(apiMessage).get(apiMessage.id);
|
|
||||||
messageCache[message.id] = {
|
|
||||||
message: message,
|
|
||||||
fetched: true
|
|
||||||
};
|
|
||||||
return Promise.resolve(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Attachment {
|
interface Attachment {
|
||||||
height: number;
|
height: number;
|
||||||
@ -79,66 +60,133 @@ interface Attachment {
|
|||||||
proxyURL?: string;
|
proxyURL?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTenorGif = /https:\/\/(?:www.)?tenor\.com/;
|
|
||||||
function getImages(message: Message): Attachment[] {
|
|
||||||
const attachments: Attachment[] = [];
|
|
||||||
message.attachments?.forEach(a => {
|
|
||||||
if (a.content_type!.startsWith("image/")) attachments.push({
|
|
||||||
height: a.height!,
|
|
||||||
width: a.width!,
|
|
||||||
url: a.url,
|
|
||||||
proxyURL: a.proxy_url!
|
|
||||||
});
|
|
||||||
});
|
|
||||||
message.embeds?.forEach(e => {
|
|
||||||
if (e.type === "image") attachments.push(
|
|
||||||
e.image ? { ...e.image } : { ...e.thumbnail! }
|
|
||||||
);
|
|
||||||
if (e.type === "gifv" && !isTenorGif.test(e.url!)) {
|
|
||||||
attachments.push({
|
|
||||||
height: e.thumbnail!.height,
|
|
||||||
width: e.thumbnail!.width,
|
|
||||||
url: e.url!
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noContent = (attachments: number, embeds: number): string => {
|
|
||||||
if (!attachments && !embeds) return "";
|
|
||||||
if (!attachments) return `[no content, ${embeds} embed${embeds !== 1 ? "s" : ""}]`;
|
|
||||||
if (!embeds) return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""}]`;
|
|
||||||
return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""} and ${embeds} embed${embeds !== 1 ? "s" : ""}]`;
|
|
||||||
};
|
|
||||||
|
|
||||||
function requiresRichEmbed(message: Message) {
|
|
||||||
if (message.attachments.every(a => a.content_type?.startsWith("image/"))
|
|
||||||
&& message.embeds.every(e => e.type === "image" || (e.type === "gifv" && !isTenorGif.test(e.url!)))
|
|
||||||
&& !message.components.length
|
|
||||||
) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const computeWidthAndHeight = (width: number, height: number) => {
|
|
||||||
const maxWidth = 400, maxHeight = 300;
|
|
||||||
let newWidth: number, newHeight: number;
|
|
||||||
if (width > height) {
|
|
||||||
newWidth = Math.min(width, maxWidth);
|
|
||||||
newHeight = Math.round(height / (width / newWidth));
|
|
||||||
} else {
|
|
||||||
newHeight = Math.min(height, maxHeight);
|
|
||||||
newWidth = Math.round(width / (height / newHeight));
|
|
||||||
}
|
|
||||||
return { width: newWidth, height: newHeight };
|
|
||||||
};
|
|
||||||
|
|
||||||
interface MessageEmbedProps {
|
interface MessageEmbedProps {
|
||||||
message: Message;
|
message: Message;
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
guildID: string;
|
guildID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const messageFetchQueue = new Queue();
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
messageBackgroundColor: {
|
||||||
|
description: "Background color for messages in rich embeds",
|
||||||
|
type: OptionType.BOOLEAN
|
||||||
|
},
|
||||||
|
automodEmbeds: {
|
||||||
|
description: "Use automod embeds instead of rich embeds (smaller but less info)",
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Always use automod embeds",
|
||||||
|
value: "always"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Prefer automod embeds, but use rich embeds if some content can't be shown",
|
||||||
|
value: "prefer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Never use automod embeds",
|
||||||
|
value: "never",
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
clearMessageCache: {
|
||||||
|
type: OptionType.COMPONENT,
|
||||||
|
description: "Clear the linked message cache",
|
||||||
|
component: () =>
|
||||||
|
<Button onClick={() => messageCache.clear()}>
|
||||||
|
Clear the linked message cache
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
async function fetchMessage(channelID: string, messageID: string) {
|
||||||
|
const cached = messageCache.get(messageID);
|
||||||
|
if (cached) return cached.message;
|
||||||
|
|
||||||
|
messageCache.set(messageID, { fetched: false });
|
||||||
|
|
||||||
|
const res = await RestAPI.get({
|
||||||
|
url: `/channels/${channelID}/messages`,
|
||||||
|
query: {
|
||||||
|
limit: 1,
|
||||||
|
around: messageID
|
||||||
|
},
|
||||||
|
retries: 2
|
||||||
|
}).catch(() => null);
|
||||||
|
|
||||||
|
const msg = res?.body?.[0];
|
||||||
|
if (!msg) return;
|
||||||
|
|
||||||
|
const message: Message = MessageStore.getMessages(msg.channel_id).receiveMessage(msg).get(msg.id);
|
||||||
|
|
||||||
|
messageCache.set(message.id, {
|
||||||
|
message,
|
||||||
|
fetched: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getImages(message: Message): Attachment[] {
|
||||||
|
const attachments: Attachment[] = [];
|
||||||
|
|
||||||
|
for (const { content_type, height, width, url, proxy_url } of message.attachments ?? []) {
|
||||||
|
if (content_type?.startsWith("image/"))
|
||||||
|
attachments.push({
|
||||||
|
height: height!,
|
||||||
|
width: width!,
|
||||||
|
url: url,
|
||||||
|
proxyURL: proxy_url!
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { type, image, thumbnail, url } of message.embeds ?? []) {
|
||||||
|
if (type === "image")
|
||||||
|
attachments.push({ ...(image ?? thumbnail!) });
|
||||||
|
else if (url && type === "gifv" && !tenorRegex.test(url))
|
||||||
|
attachments.push({
|
||||||
|
height: thumbnail!.height,
|
||||||
|
width: thumbnail!.width,
|
||||||
|
url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
function noContent(attachments: number, embeds: number) {
|
||||||
|
if (!attachments && !embeds) return "";
|
||||||
|
if (!attachments) return `[no content, ${embeds} embed${embeds !== 1 ? "s" : ""}]`;
|
||||||
|
if (!embeds) return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""}]`;
|
||||||
|
return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""} and ${embeds} embed${embeds !== 1 ? "s" : ""}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requiresRichEmbed(message: Message) {
|
||||||
|
if (message.components.length) return true;
|
||||||
|
if (message.attachments.some(a => a.content_type?.startsWith("image/"))) return true;
|
||||||
|
if (message.embeds.some(e => e.type === "image" || (e.type === "gifv" && !tenorRegex.test(e.url!)))) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeWidthAndHeight(width: number, height: number) {
|
||||||
|
const maxWidth = 400;
|
||||||
|
const maxHeight = 300;
|
||||||
|
|
||||||
|
if (width > height) {
|
||||||
|
const adjustedWidth = Math.min(width, maxWidth);
|
||||||
|
return { width: adjustedWidth, height: Math.round(height / (width / adjustedWidth)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
const adjustedHeight = Math.min(height, maxHeight);
|
||||||
|
return { width: Math.round(width / (height / adjustedHeight)), height: adjustedHeight };
|
||||||
|
}
|
||||||
|
|
||||||
function withEmbeddedBy(message: Message, embeddedBy: string[]) {
|
function withEmbeddedBy(message: Message, embeddedBy: string[]) {
|
||||||
return new Proxy(message, {
|
return new Proxy(message, {
|
||||||
get(_, prop) {
|
get(_, prop) {
|
||||||
@ -149,181 +197,172 @@ function withEmbeddedBy(message: Message, embeddedBy: string[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function MessageEmbedAccessory({ message }: { message: Message; }) {
|
||||||
|
// @ts-ignore
|
||||||
|
const embeddedBy: string[] = message.vencordEmbeddedBy ?? [];
|
||||||
|
|
||||||
|
const accessories = [] as (JSX.Element | null)[];
|
||||||
|
|
||||||
|
let match = null as RegExpMatchArray | null;
|
||||||
|
while ((match = messageLinkRegex.exec(message.content!)) !== null) {
|
||||||
|
const [_, guildID, channelID, messageID] = match;
|
||||||
|
if (embeddedBy.includes(messageID)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkedChannel = ChannelStore.getChannel(channelID);
|
||||||
|
if (!linkedChannel || (guildID !== "@me" && !PermissionStore.can(1024n /* view channel */, linkedChannel))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let linkedMessage = messageCache.get(messageID)?.message;
|
||||||
|
if (!linkedMessage) {
|
||||||
|
linkedMessage ??= MessageStore.getMessage(channelID, messageID);
|
||||||
|
if (linkedMessage) {
|
||||||
|
messageCache.set(messageID, { message: linkedMessage, fetched: true });
|
||||||
|
} else {
|
||||||
|
const msg = { ...message } as any;
|
||||||
|
delete msg.embeds;
|
||||||
|
messageFetchQueue.push(() => fetchMessage(channelID, messageID)
|
||||||
|
.then(m => m && FluxDispatcher.dispatch({
|
||||||
|
type: "MESSAGE_UPDATE",
|
||||||
|
message: msg
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageProps: MessageEmbedProps = {
|
||||||
|
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
|
||||||
|
channel: linkedChannel,
|
||||||
|
guildID
|
||||||
|
};
|
||||||
|
|
||||||
|
const type = settings.store.automodEmbeds;
|
||||||
|
accessories.push(
|
||||||
|
type === "always" || (type === "prefer" && !requiresRichEmbed(linkedMessage))
|
||||||
|
? <AutomodEmbedAccessory {...messageProps} />
|
||||||
|
: <ChannelMessageEmbedAccessory {...messageProps} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessories.length ? <>{accessories}</> : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbedProps): JSX.Element | null {
|
||||||
|
const isDM = guildID === "@me";
|
||||||
|
|
||||||
|
const guild = !isDM && GuildStore.getGuild(channel.guild_id);
|
||||||
|
const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]);
|
||||||
|
|
||||||
|
|
||||||
|
return <Embed
|
||||||
|
embed={{
|
||||||
|
rawDescription: "",
|
||||||
|
color: "var(--background-secondary)",
|
||||||
|
author: {
|
||||||
|
name: <Text variant="text-xs/medium" tag="span">
|
||||||
|
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>,
|
||||||
|
{isDM
|
||||||
|
? Parser.parse(`<@${dmReceiver.id}>`)
|
||||||
|
: Parser.parse(`<#${channel.id}>`)
|
||||||
|
}
|
||||||
|
</Text>,
|
||||||
|
iconProxyURL: guild
|
||||||
|
? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png`
|
||||||
|
: `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}`
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
renderDescription={() => (
|
||||||
|
<div key={message.id} className={classes(SearchResultClasses.message, settings.store.messageBackgroundColor && SearchResultClasses.searchResult)}>
|
||||||
|
<ChannelMessage
|
||||||
|
id={`message-link-embeds-${message.id}`}
|
||||||
|
message={message}
|
||||||
|
channel={channel}
|
||||||
|
subscribeToComponentDispatch={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
||||||
|
const { message, channel, guildID } = props;
|
||||||
|
|
||||||
|
const isDM = guildID === "@me";
|
||||||
|
const images = getImages(message);
|
||||||
|
const { parse } = Parser;
|
||||||
|
|
||||||
|
return <AutoModEmbed
|
||||||
|
channel={channel}
|
||||||
|
childrenAccessories={
|
||||||
|
<Text color="text-muted" variant="text-xs/medium" tag="span">
|
||||||
|
{isDM
|
||||||
|
? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
|
||||||
|
: parse(`<#${channel.id}>`)
|
||||||
|
},
|
||||||
|
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
compact={false}
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
{message.content || message.attachments.length <= images.length
|
||||||
|
? parse(message.content)
|
||||||
|
: [noContent(message.attachments.length, message.embeds.length)]
|
||||||
|
}
|
||||||
|
{images.map(a => {
|
||||||
|
const { width, height } = computeWidthAndHeight(a.width, a.height);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<img src={a.url} width={width} height={height} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
hideTimestamp={false}
|
||||||
|
message={message}
|
||||||
|
_messageEmbed="automod"
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MessageLinkEmbeds",
|
name: "MessageLinkEmbeds",
|
||||||
description: "Adds a preview to messages that link another message",
|
description: "Adds a preview to messages that link another message",
|
||||||
authors: [Devs.TheSun],
|
authors: [Devs.TheSun, Devs.Ven],
|
||||||
dependencies: ["MessageAccessoriesAPI"],
|
dependencies: ["MessageAccessoriesAPI"],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".embedCard",
|
find: ".embedCard",
|
||||||
replacement: [{
|
replacement: [{
|
||||||
match: /{"use strict";(.{0,10})\(\)=>(\i)}\);/,
|
|
||||||
replace: '{"use strict";$1()=>$2,me:()=>typeof messageEmbed !== "undefined" ? messageEmbed : null});'
|
|
||||||
}, {
|
|
||||||
match: /function (\i)\(\i\){var \i=\i\.message,\i=\i\.channel.{0,200}\.hideTimestamp/,
|
match: /function (\i)\(\i\){var \i=\i\.message,\i=\i\.channel.{0,200}\.hideTimestamp/,
|
||||||
replace: "var messageEmbed={mle_AutomodEmbed:$1};$&"
|
replace: "$self.AutoModEmbed=$1;$&"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
options: {
|
|
||||||
messageBackgroundColor: {
|
set AutoModEmbed(e: any) {
|
||||||
description: "Background color for messages in rich embeds",
|
AutoModEmbed = e;
|
||||||
type: OptionType.BOOLEAN
|
|
||||||
},
|
|
||||||
automodEmbeds: {
|
|
||||||
description: "Use automod embeds instead of rich embeds (smaller but less info)",
|
|
||||||
type: OptionType.SELECT,
|
|
||||||
options: [{
|
|
||||||
label: "Always use automod embeds",
|
|
||||||
value: "always"
|
|
||||||
}, {
|
|
||||||
label: "Prefer automod embeds, but use rich embeds if some content can't be shown",
|
|
||||||
value: "prefer"
|
|
||||||
}, {
|
|
||||||
label: "Never use automod embeds",
|
|
||||||
value: "never",
|
|
||||||
default: true
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
clearMessageCache: {
|
|
||||||
type: OptionType.COMPONENT,
|
|
||||||
description: "Clear the linked message cache",
|
|
||||||
component: () =>
|
|
||||||
<Button onClick={() => messageCache = {}}>
|
|
||||||
Clear the linked message cache
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
addAccessory("messageLinkEmbed", props => this.messageEmbedAccessory(props), 4 /* just above rich embeds*/);
|
addAccessory("messageLinkEmbed", props => {
|
||||||
},
|
if (!messageLinkRegex.test(props.message.content))
|
||||||
|
return null;
|
||||||
|
|
||||||
messageLinkRegex: /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(\d{17,19}|@me)\/(\d{17,19})\/(\d{17,19})/g,
|
// need to reset the regex because it's global
|
||||||
|
messageLinkRegex.lastIndex = 0;
|
||||||
|
|
||||||
messageEmbedAccessory(props) {
|
return (
|
||||||
const { message }: { message: Message; } = props;
|
<ErrorBoundary>
|
||||||
// @ts-ignore
|
<MessageEmbedAccessory message={props.message} />
|
||||||
const embeddedBy: string[] = message.vencordEmbeddedBy ?? [];
|
</ErrorBoundary>
|
||||||
|
|
||||||
const accessories = [] as (JSX.Element | null)[];
|
|
||||||
|
|
||||||
let match = null as RegExpMatchArray | null;
|
|
||||||
while ((match = this.messageLinkRegex.exec(message.content!)) !== null) {
|
|
||||||
const [_, guildID, channelID, messageID] = match;
|
|
||||||
if (embeddedBy.includes(messageID)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const linkedChannel = ChannelStore.getChannel(channelID);
|
|
||||||
if (!linkedChannel || (guildID !== "@me" && !PermissionStore.can(1024n /* view channel */, linkedChannel))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let linkedMessage = messageCache[messageID]?.message;
|
|
||||||
if (!linkedMessage) {
|
|
||||||
linkedMessage ??= MessageStore.getMessage(channelID, messageID);
|
|
||||||
if (linkedMessage) messageCache[messageID] = { message: linkedMessage, fetched: true };
|
|
||||||
else {
|
|
||||||
const msg = { ...message } as any;
|
|
||||||
delete msg.embeds;
|
|
||||||
messageFetchQueue.push(() => fetchMessage(channelID, messageID)
|
|
||||||
.then(m => m && FluxDispatcher.dispatch({
|
|
||||||
type: "MESSAGE_UPDATE",
|
|
||||||
message: msg
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const messageProps: MessageEmbedProps = {
|
|
||||||
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
|
|
||||||
channel: linkedChannel,
|
|
||||||
guildID
|
|
||||||
};
|
|
||||||
|
|
||||||
const type = Settings.plugins[this.name].automodEmbeds;
|
|
||||||
accessories.push(
|
|
||||||
type === "always" || (type === "prefer" && !requiresRichEmbed(linkedMessage))
|
|
||||||
? this.automodEmbedAccessory(messageProps)
|
|
||||||
: this.channelMessageEmbedAccessory(messageProps)
|
|
||||||
);
|
);
|
||||||
}
|
}, 4 /* just above rich embeds */);
|
||||||
return accessories;
|
|
||||||
},
|
|
||||||
|
|
||||||
channelMessageEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
|
||||||
const { message, channel, guildID } = props;
|
|
||||||
|
|
||||||
const isDM = guildID === "@me";
|
|
||||||
const guild = !isDM && GuildStore.getGuild(channel.guild_id);
|
|
||||||
const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]);
|
|
||||||
const classNames = [SearchResultClasses.message];
|
|
||||||
if (Settings.plugins[this.name].messageBackgroundColor) classNames.push(SearchResultClasses.searchResult);
|
|
||||||
|
|
||||||
return <Embed
|
|
||||||
embed={{
|
|
||||||
rawDescription: "",
|
|
||||||
color: "var(--background-secondary)",
|
|
||||||
author: {
|
|
||||||
name: <Text variant="text-xs/medium" tag="span">
|
|
||||||
{[
|
|
||||||
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>,
|
|
||||||
...(isDM
|
|
||||||
? Parser.parse(`<@${dmReceiver.id}>`)
|
|
||||||
: Parser.parse(`<#${channel.id}>`)
|
|
||||||
)
|
|
||||||
]}
|
|
||||||
</Text>,
|
|
||||||
iconProxyURL: guild
|
|
||||||
? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png`
|
|
||||||
: `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}`
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
renderDescription={() => {
|
|
||||||
return <div key={message.id} className={classNames.join(" ")}>
|
|
||||||
<ChannelMessage
|
|
||||||
id={`message-link-embeds-${message.id}`}
|
|
||||||
message={message}
|
|
||||||
channel={channel}
|
|
||||||
subscribeToComponentDispatch={false}
|
|
||||||
/>
|
|
||||||
</div >;
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
},
|
|
||||||
|
|
||||||
automodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
|
||||||
const { message, channel, guildID } = props;
|
|
||||||
|
|
||||||
const isDM = guildID === "@me";
|
|
||||||
const images = getImages(message);
|
|
||||||
const { parse } = Parser;
|
|
||||||
|
|
||||||
return <AutomodEmbed
|
|
||||||
channel={channel}
|
|
||||||
childrenAccessories={<Text color="text-muted" variant="text-xs/medium" tag="span">
|
|
||||||
{[
|
|
||||||
...(isDM ? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`) : parse(`<#${channel.id}>`)),
|
|
||||||
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
|
||||||
]}
|
|
||||||
</Text>}
|
|
||||||
compact={false}
|
|
||||||
content={[
|
|
||||||
...(message.content || !(message.attachments.length > images.length)
|
|
||||||
? parse(message.content)
|
|
||||||
: [noContent(message.attachments.length, message.embeds.length)]
|
|
||||||
),
|
|
||||||
...(images.map<JSX.Element>(a => {
|
|
||||||
const { width, height } = computeWidthAndHeight(a.width, a.height);
|
|
||||||
return <div><img src={a.url} width={width} height={height} /></div>;
|
|
||||||
}
|
|
||||||
))
|
|
||||||
]}
|
|
||||||
hideTimestamp={false}
|
|
||||||
message={message}
|
|
||||||
_messageEmbed="automod"
|
|
||||||
/>;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user