Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 68 additions & 39 deletions src/commands/context-menu/editMsg.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable complexity */
import Constants, { ConnectionMode, emojis } from '#main/config/Constants.js';
import BaseCommand from '#main/core/BaseCommand.js';
import { RegisterInteractionHandler } from '#main/decorators/Interaction.js';
Expand All @@ -7,9 +8,9 @@
import { fetchHub } from '#main/utils/hub/utils.js';
import {
findOriginalMessage,
getBroadcast,
getBroadcasts,
getOriginalMessage,
OriginalMessage,
} from '#main/utils/network/messageUtils.js';
import { CustomID } from '#utils/CustomID.js';
import db from '#utils/Db.js';
Expand Down Expand Up @@ -103,104 +104,132 @@

@RegisterInteractionHandler('editMsg')
override async handleModals(interaction: ModalSubmitInteraction): Promise<void> {
// Defer the reply to give the user feedback
await interaction.deferReply({ ephemeral: true });

// Parse the custom ID to get the message ID
const customId = CustomID.parseCustomId(interaction.customId);
const [messageId] = customId.args;
const { userManager } = interaction.client;
const locale = await userManager.getUserLocale(interaction.user.id);

// Fetch the original message
const target = await interaction.channel?.messages.fetch(messageId).catch(() => null);
if (!target) {
await interaction.editReply(t('errors.unknownNetworkMessage', locale, { emoji: emojis.no }));
await this.replyEmbed(interaction, 'errors.unknownNetworkMessage', {
t: { emoji: emojis.no },
});
return;
}

const targetMsgData =
// Get the original message data
const originalMsgData =
(await getOriginalMessage(target.id)) ?? (await findOriginalMessage(target.id));

const unknownMsgErr = t('errors.unknownNetworkMessage', locale, { emoji: emojis.no });
if (!targetMsgData?.hubId) {
await interaction.editReply(unknownMsgErr);
if (!originalMsgData?.hubId) {
await this.replyEmbed(interaction, 'errors.unknownNetworkMessage', {
t: { emoji: emojis.no },
});
return;
}
const hub = await fetchHub(targetMsgData.hubId);

// Fetch the hub information
const hub = await fetchHub(originalMsgData.hubId);
if (!hub) {
await interaction.editReply(unknownMsgErr);
await interaction.editReply(
t('errors.unknownNetworkMessage', await this.getLocale(interaction), {
emoji: emojis.no,
}),
);
return;
}

// get the new message input by user
// Get the new message input from the user
const userInput = interaction.fields.getTextInputValue('newMessage');
const settingsManager = new HubSettingsManager(targetMsgData.hubId, hub.settings);
const settingsManager = new HubSettingsManager(originalMsgData.hubId, hub.settings);
const messageToEdit = this.sanitizeMessage(userInput, settingsManager.getAllSettings());

// Check if the message contains invite links
if (settingsManager.getSetting('BlockInvites') && containsInviteLinks(messageToEdit)) {
await interaction.editReply(t('errors.inviteLinks', locale, { emoji: emojis.no }));
await interaction.editReply(
t('errors.inviteLinks', await this.getLocale(interaction), { emoji: emojis.no }),
);
return;
}

const imageURLs = await this.getImageURLs(target, targetMsgData.mode, messageToEdit);
const mode =
target.id === originalMsgData.messageId
? ConnectionMode.Compact
: ((
await getBroadcast(originalMsgData?.messageId, originalMsgData?.hubId, {
channelId: target.channelId,
})
)?.mode ?? ConnectionMode.Compact);

// Prepare the new message contents and embeds
const imageURLs = await this.getImageURLs(target, mode, messageToEdit);
const newContents = this.getCompactContents(messageToEdit, imageURLs);
const newEmbeds = await this.buildEmbeds(target, targetMsgData, messageToEdit, {
guildId: targetMsgData.guildId,
const newEmbeds = await this.buildEmbeds(target, mode, messageToEdit, {
guildId: originalMsgData.guildId,
user: interaction.user,
imageURLs,
});

// find all the messages through the network
const broadcastedMsgs = Object.values(await getBroadcasts(target.id, targetMsgData.hubId));
// Find all the messages that need to be edited
const broadcastedMsgs = Object.values(await getBroadcasts(target.id, originalMsgData.hubId));
const channelSettingsArr = await db.connectedList.findMany({
where: { channelId: { in: broadcastedMsgs.map((c) => c.channelId) } },
});

const results = broadcastedMsgs.map(async (msg) => {
let counter = 0;
for (const msg of broadcastedMsgs) {
const connection = channelSettingsArr.find((c) => c.channelId === msg.channelId);
if (!connection) return false;
if (!connection) continue;

const webhookURL = connection.webhookURL.split('/');
const webhook = await interaction.client
.fetchWebhook(webhookURL[webhookURL.length - 2])
?.catch(() => null);
.fetchWebhook(connection.webhookURL.split('/')[connection.webhookURL.split('/').length - 2])
.catch(() => null);

if (webhook?.owner?.id !== interaction.client.user.id) return false;
if (webhook?.owner?.id !== interaction.client.user.id) continue;
console.log(msg.mode, msg.mode === ConnectionMode.Embed);

Check warning on line 192 in src/commands/context-menu/editMsg.ts

View workflow job for this annotation

GitHub Actions / Lint Check (20)

Unexpected console statement

let content;
let embeds;

if (msg.mode === ConnectionMode.Embed) {
embeds = connection.profFilter ? [newEmbeds.censored] : [newEmbeds.normal];
}
else {
content = connection.profFilter ? newContents.censored : newContents.normal;
}

// finally, edit the message
return await webhook
// Edit the message
const edited = await webhook
.editMessage(msg.messageId, {
content,
embeds,
threadId: connection.parentId ? connection.channelId : undefined,
})
.then(() => true)
.catch(() => false);
});
.catch(() => null);

const resultsArray = await Promise.all(results);
const edited = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0).toString();
if (edited) counter++;
}

// Update the reply with the edit results
await interaction
.editReply(
t('network.editSuccess', locale, {
edited,
total: resultsArray.length.toString(),
t('network.editSuccess', await this.getLocale(interaction), {
edited: counter.toString(),
total: broadcastedMsgs.length.toString(),
emoji: emojis.yes,
user: userMention(targetMsgData.authorId),
user: userMention(originalMsgData.authorId),
}),
)
.catch(handleError);

const voteLimiter = new VoteBasedLimiter('editMsg', interaction.user.id, userManager);
// Decrement the vote limiter
const voteLimiter = new VoteBasedLimiter(
'editMsg',
interaction.user.id,
interaction.client.userManager,
);
await voteLimiter.decrementUses();
}

Expand All @@ -221,7 +250,7 @@

private async buildEmbeds(
target: Message,
targetMsgData: OriginalMessage,
mode: ConnectionMode,
messageToEdit: string,
opts: { user: User; guildId: string; imageURLs?: ImageUrls },
) {
Expand All @@ -241,7 +270,7 @@

let embed: EmbedBuilder;

if (targetMsgData.mode === ConnectionMode.Embed) {
if (mode === ConnectionMode.Embed) {
// utilize the embed directly from the message
embed = EmbedBuilder.from(target.embeds[0]).setDescription(embedContent).setImage(embedImage);
}
Expand Down
15 changes: 5 additions & 10 deletions src/events/messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,7 @@ export default class MessageCreate extends BaseEventListener<'messageCreate'> {
embedColor: connection.embedColor as HexColorString,
});

await storeMessageData(
message,
sendResult,
connection.hubId,
connection.compact ? ConnectionMode.Compact : ConnectionMode.Embed,
referredMsgData.dbReferrence,
);
await storeMessageData(message, sendResult, connection.hubId, referredMsgData.dbReferrence);
}

private async fetchReferredMessage(message: Message<true>): Promise<Message | null> {
Expand Down Expand Up @@ -277,10 +271,11 @@ export default class MessageCreate extends BaseEventListener<'messageCreate'> {

private getReferredContent(data: ReferredMsgData) {
if (data.referredMessage && data.dbReferrence) {
const messagesRepliedTo =
data.dbReferrence.broadcastMsgs.get(data.referredMessage.channelId) ?? data.dbReferrence;
const mode =
data.dbReferrence.broadcastMsgs.get(data.referredMessage.channelId)?.mode ??
ConnectionMode.Compact; // message is an OriginalMessage (user message)

return getReferredContent(data.referredMessage, messagesRepliedTo.mode);
return getReferredContent(data.referredMessage, mode);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/tasks/storeMsgTimestamps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default async () => {
const timestampsObj = await getRedis().hgetall(`${RedisKeys.msgTimestamp}`);

Object.entries(timestampsObj).forEach(async ([channelId, timestamp]) => {
// FIXME: Errors happens that says "record to update not found"
await updateConnection({ channelId }, { lastActive: new Date(parseInt(timestamp)) });
Logger.debug(`Stored message timestamps for channel ${channelId} from cache to db.`);
await getRedis().hdel(`${RedisKeys.msgTimestamp}`, channelId);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/network/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const getReferredMsgData = async (
// fetch the acttual user ("referredMessage" is a webhook message)
const referredAuthor = await client.users.fetch(dbReferrenceRaw.authorId).catch(() => null);
const dbReferredAuthor = await client.userManager.getUser(dbReferrenceRaw.authorId);
const broadcastedMessages = await getBroadcasts(referredMessage.id, dbReferrenceRaw.hubId);
const broadcastedMessages = await getBroadcasts(dbReferrenceRaw.messageId, dbReferrenceRaw.hubId);

const dbReferrence = {
...dbReferrenceRaw,
Expand Down
7 changes: 1 addition & 6 deletions src/utils/network/messageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { Message, Snowflake } from 'discord.js';
import isEmpty from 'lodash/isEmpty.js';

export interface OriginalMessage {
mode: number;
hubId: string;
messageId: string;
guildId: string;
Expand Down Expand Up @@ -38,11 +37,7 @@ export const getOriginalMessage = async (originalMsgId: string) => {

if (isEmpty(res)) return null;

return {
...res,
mode: parseInt(res.mode),
timestamp: parseInt(res.timestamp),
} as OriginalMessage;
return { ...res, timestamp: parseInt(res.timestamp) } as OriginalMessage;
};

export const addBroadcasts = async (
Expand Down
4 changes: 1 addition & 3 deletions src/utils/network/storeMessageData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ export default async (
message: Message,
broadcastResults: NetworkWebhookSendResult[],
hubId: string,
mode: ConnectionMode,
dbReference?: OriginalMessage | null,
) => {
if (!message.inGuild()) return;

await storeMessage(message.id, {
mode,
hubId,
messageId: message.id,
authorId: message.author.id,
Expand All @@ -63,7 +61,7 @@ export default async (
return;
}
validBroadcasts.push({
mode,
mode: res.mode,
messageId: res.messageRes.id,
channelId: res.messageRes.channel_id,
originalMsgId: message.id,
Expand Down