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
127 changes: 82 additions & 45 deletions src/commands/context-menu/editMsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
CacheType,
ModalSubmitInteraction,
userMention,
Message,
} from 'discord.js';
import db from '../../utils/Db.js';
import BaseCommand from '../../core/BaseCommand.js';
Expand Down Expand Up @@ -132,7 +133,8 @@ export default class EditMessage extends BaseCommand {
const userInput = interaction.fields.getTextInputValue('newMessage');
const hubSettings = new HubSettingsBitField(messageInDb.originalMsg.hub.settings);
const newMessage = hubSettings.has('HideLinks') ? replaceLinks(userInput) : userInput;
const networkManager = interaction.client.networkManager;
const { newEmbed, censoredEmbed, compactMsg, censoredCmpctMsg } =
await EditMessage.fabricateNewMsg(target, newMessage, messageInDb.originalMsg.serverId);

if (
hubSettings.has('BlockInvites') &&
Expand All @@ -145,50 +147,6 @@ export default class EditMessage extends BaseCommand {
);
return;
}
// get image from embed
// get image from content
const oldImageUrl = target.content
? await networkManager.getAttachmentURL(target.content)
: target.embeds[0]?.image?.url;
const newImageUrl = await networkManager.getAttachmentURL(newMessage);
const guild = await interaction.client.fetchGuild(messageInDb.originalMsg.serverId);
const embedContent = newMessage.replace(oldImageUrl ?? '', '').replace(newImageUrl ?? '', '');

// if the message being edited is in compact mode
// then we create a new embed with the new message and old reply
// else we just use the old embed and replace the description
const newEmbed = target.content
? new EmbedBuilder()
.setAuthor({ name: target.author.username, iconURL: target.author.displayAvatarURL() })
.setDescription(embedContent || null)
.setColor(target.member?.displayHexColor ?? 'Random')
.setImage(newImageUrl || oldImageUrl || null)
.addFields(
target.embeds[0]?.fields[0]
? [{ name: 'Replying-to', value: `${target.embeds[0].description}` }]
: [],
)
.setFooter({ text: `Server: ${guild?.name}` })
: EmbedBuilder.from(target.embeds[0])
.setDescription(embedContent || null)
.setImage(newImageUrl || oldImageUrl || null);

const censoredEmbed = EmbedBuilder.from(newEmbed).setDescription(
censor(newEmbed.data.description ?? '') || null,
);
let compactMsg = newMessage;

if (oldImageUrl) {
if (newImageUrl) {
compactMsg = compactMsg.replace(oldImageUrl, newImageUrl);
}
else if (!newMessage.includes(oldImageUrl)) {
newEmbed.setImage(null);
censoredEmbed.setImage(null);
}
}

const censoredCmpctMsg = censor(compactMsg);

// find all the messages through the network
const channelSettingsArr = await db.connectedList.findMany({
Expand Down Expand Up @@ -237,4 +195,83 @@ export default class EditMessage extends BaseCommand {
),
);
}

static async getImageUrls(target: Message, newMessage: string) {
const { networkManager } = target.client;
// get image from embed
// get image from content
const oldImageUrl = target.content
? await networkManager.getAttachmentURL(target.content)
: target.embeds[0]?.image?.url;
const newImageUrl = await networkManager.getAttachmentURL(newMessage);
return { oldImageUrl, newImageUrl };
}

static async buildNewEmbed(
target: Message,
newMessage: string,
serverId: string,
oldImageUrl?: string | null,
newImageUrl?: string | null,
) {
let newEmbed = new EmbedBuilder();
const embedContent =
newMessage.replace(oldImageUrl ?? '', '').replace(newImageUrl ?? '', '') || null;

if (target.content) {
const guild = await target.client.fetchGuild(serverId);

// create a new embed if the message being edited is in compact mode
newEmbed
.setAuthor({ name: target.author.username, iconURL: target.author.displayAvatarURL() })
.setDescription(embedContent)
.setColor(target.member?.displayHexColor ?? 'Random')
.setImage(newImageUrl ?? oldImageUrl ?? null)
.addFields(
target.embeds.at(0)?.fields.at(0)
? [{ name: 'Replying-to', value: `${target.embeds[0].description}` }]
: [],
)
.setFooter({ text: `Server: ${guild?.name}` });
}
else {
// utilize the embed directly from the message
newEmbed = EmbedBuilder.from(target.embeds[0])
.setDescription(embedContent)
.setImage(newImageUrl ?? oldImageUrl ?? null);
}

return newEmbed;
}

static async fabricateNewMsg(target: Message, newMessage: string, serverId: string) {
const { oldImageUrl, newImageUrl } = await this.getImageUrls(target, newMessage);
const newEmbed = await this.buildNewEmbed(
target,
newMessage,
serverId,
oldImageUrl,
newImageUrl,
);

// if the message being edited is in compact mode
// then we create a new embed with the new message and old reply
// else we just use the old embed and replace the description

const censoredEmbed = EmbedBuilder.from(newEmbed).setDescription(
censor(newEmbed.data.description ?? '') || null,
);
let compactMsg = newMessage;

if (oldImageUrl && newImageUrl) {
compactMsg = compactMsg.replace(oldImageUrl, newImageUrl);
}
else if (oldImageUrl && !newMessage.includes(oldImageUrl)) {
newEmbed.setImage(null);
censoredEmbed.setImage(null);
}
const censoredCmpctMsg = censor(compactMsg);

return { newEmbed, censoredEmbed, compactMsg, censoredCmpctMsg };
}
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ clusterManager.on('clusterCreate', async (cluster) => {

const voteManager = new VoteManager(clusterManager);
voteManager.on('vote', async (vote) => {
const username = await getUsername(clusterManager, vote.user) ?? undefined;
const username = (await getUsername(clusterManager, vote.user)) ?? undefined;
await voteManager.incrementUserVote(vote.user, username);
await voteManager.addVoterRole(vote.user);
await voteManager.announceVote(vote);
Expand Down
12 changes: 9 additions & 3 deletions src/managers/NetworkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { HubSettingsBitField } from '../utils/BitFields.js';
import { parseTimestampFromId, replaceLinks, wait } from '../utils/Utils.js';
import { t } from '../utils/Locale.js';
import sendMessage from '../scripts/network/sendMessage.js';
import Logger from '../utils/Logger.js';

export interface NetworkWebhookSendResult {
messageOrError: APIMessage | string;
Expand Down Expand Up @@ -67,7 +66,9 @@ export default class NetworkManager {
: await this.getAttachmentURL(message.content);

// run checks on the message to determine if it can be sent in the network
if (!(await this.runChecks(message, settings, isNetworkMessage.hubId, { attachmentURL }))) return;
if (!(await this.runChecks(message, settings, isNetworkMessage.hubId, { attachmentURL }))) {
return;
}

const allConnections = await this.fetchHubNetworks({
hubId: isNetworkMessage.hubId,
Expand Down Expand Up @@ -281,7 +282,12 @@ export default class NetworkManager {
* @param hubId - The ID of the hub the message is being sent in.
* @returns A boolean indicating whether the message passed all checks.
*/
public async runChecks(message: Message, settings: HubSettingsBitField, hubId: string, opts?: { attachmentURL?: string | null }) {
public async runChecks(
message: Message,
settings: HubSettingsBitField,
hubId: string,
opts?: { attachmentURL?: string | null },
) {
if (!message.inGuild()) return false;

const sevenDaysAgo = Date.now() - 1000 * 60 * 60 * 24 * 7;
Expand Down
4 changes: 3 additions & 1 deletion src/managers/VoteManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export class VoteManager extends EventEmitter {
this.cluster = cluster;
this.scheduler = scheduler;
this.scheduler.addRecurringTask('removeVoterRole', 60 * 60 * 1_000, async () => {
const expiredVotes = await db.userData.findMany({ where: { lastVoted: { lt: new Date().getTime() } } });
const expiredVotes = await db.userData.findMany({
where: { lastVoted: { lt: new Date().getTime() } },
});
for (const vote of expiredVotes) {
this.emit('voteExpired', vote.userId);
await this.removeVoterRole(vote.userId);
Expand Down
10 changes: 4 additions & 6 deletions src/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const getOrCreateWebhook = async (
if (!channelOrParent) return null;

const existingWebhook = await findExistingWebhook(channelOrParent);
return existingWebhook || await createWebhook(channelOrParent, avatar);
return existingWebhook || (await createWebhook(channelOrParent, avatar));
};

export const getCredits = () => {
Expand All @@ -154,8 +154,8 @@ export const disableAllComponents = (
const jsonRow = row.toJSON();
jsonRow.components.forEach((component) => {
!disableLinks &&
component.type === ComponentType.Button &&
component.style === ButtonStyle.Link
component.type === ComponentType.Button &&
component.style === ButtonStyle.Link
? (component.disabled = false) // leave link buttons enabled
: (component.disabled = true);
});
Expand Down Expand Up @@ -379,9 +379,7 @@ export const getUsername = async (client: ClusterManager, userId: Snowflake) =>
const user = await c.users.fetch(ctx.userId).catch(() => null);
return user?.username;
},
{
context: { userId },
},
{ context: { userId } },
),
);

Expand Down