Skip to content

Commit

Permalink
refactor(network): another scary refactor of the broadcasting part (#66)
Browse files Browse the repository at this point in the history
* refactor(network): another scary refactor of the broadcasting part

* fix some small stuff

* more fixy

* uh more ig?

* e

* move welcome embed/message to helpers.ts

* fix something else

* fix another ting
  • Loading branch information
dev-737 authored May 11, 2024
1 parent 35b3058 commit 61acfd0
Show file tree
Hide file tree
Showing 17 changed files with 338 additions and 281 deletions.
12 changes: 6 additions & 6 deletions src/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ clusterManager.on('clusterCreate', async (cluster) => {
// remove expired blacklists or set new timers for them
const serverQuery = { where: { hubs: { some: { expires: { isSet: true } } } } };
const userQuery = { where: { blacklistedFrom: { some: { expires: { isSet: true } } } } };
updateBlacklists(await db.blacklistedServers.findMany(serverQuery), scheduler)
.catch(Logger.error);
updateBlacklists(await db.blacklistedServers.findMany(serverQuery), scheduler).catch(
Logger.error,
);

updateBlacklists(await db.userData.findMany(userQuery), scheduler)
.catch(Logger.error);
updateBlacklists(await db.userData.findMany(userQuery), scheduler).catch(Logger.error);

// code must be in production to run these tasks
if (isDevBuild) return;
Expand Down Expand Up @@ -57,7 +57,7 @@ voteManager.on('vote', async (vote) => {
});

// spawn clusters and start the api that handles nsfw filter and votes
clusterManager.spawn({ timeout: -1 })
clusterManager
.spawn({ timeout: -1 })
.then(() => startApi({ voteManager }))
.catch(Logger.error);

2 changes: 1 addition & 1 deletion src/commands/slash/Main/hub/browse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export default class Browse extends Hub {
const phrase = userBlacklisted ? 'errors.userBlacklisted' : 'errors.serverBlacklisted';

await interaction.reply({
embeds: [simpleEmbed(t({ phrase, locale }, { hub: hubDetails.name, emoji: emojis.no }))],
embeds: [simpleEmbed(t({ phrase, locale }, { emoji: emojis.no }))],
ephemeral: true,
});
return;
Expand Down
2 changes: 1 addition & 1 deletion src/decorators/GatewayEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ export default function GatewayEvent(eventName: keyof ClientEvents): MethodDecor

eventMethods.set(eventName, values);
};
}
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ client.on('debug', (debug) => {
Logger.debug(debug);
});


client.start();
217 changes: 26 additions & 191 deletions src/managers/EventManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable complexity */
import {
ActionRowBuilder,
ButtonBuilder,
Expand All @@ -12,7 +11,6 @@ import {
Message,
MessageReaction,
PartialUser,
WebhookMessageCreateOptions,
Interaction,
Client,
} from 'discord.js';
Expand All @@ -22,20 +20,20 @@ import GatewayEvent from '../decorators/GatewayEvent.js';
import { stripIndents } from 'common-tags';
import getWelcomeTargets from '../scripts/guilds/getWelcomeTarget.js';
import { logGuildJoin, logGuildLeave } from '../scripts/guilds/goals.js';
import { channels, emojis, colors, LINKS, REGEX } from '../utils/Constants.js';
import { censor, check } from '../utils/Profanity.js';
import { channels, emojis, colors, LINKS } from '../utils/Constants.js';
import { check } from '../utils/Profanity.js';
import db from '../utils/Db.js';
import { t } from '../utils/Locale.js';
import storeMessageData, { NetworkWebhookSendResult } from '../scripts/network/storeMessageData.js';
import { buildNetworkEmbed, getReferredContent } from '../scripts/network/helpers.js';
import sendMessage from '../scripts/network/sendMessage.js';
import storeMessageData from '../scripts/network/storeMessageData.js';
import { getReferredMsgData, sendWelcomeMsg } from '../scripts/network/helpers.js';
import { HubSettingsBitField } from '../utils/BitFields.js';
import { getAttachmentURL, handleError, simpleEmbed, wait } from '../utils/Utils.js';
import { runChecks } from '../scripts/network/runChecks.js';
import SuperClient from '../core/Client.js';
import { addReaction, updateReactions } from '../scripts/reaction/actions.js';
import { checkBlacklists } from '../scripts/reaction/helpers.js';
import { CustomID } from '../utils/CustomID.js';
import SuperClient from '../core/Client.js';
import broadcastMessage from '../scripts/network/broadcastMessage.js';

export default abstract class EventManager {
@GatewayEvent('ready')
Expand All @@ -44,7 +42,7 @@ export default abstract class EventManager {
}

@GatewayEvent('shardReady')
async onShardReady(s: number, u: Set<string>) {
onShardReady(s: number, u: Set<string>) {
if (u) {
Logger.warn(`Shard ${s} is ready but ${u.size} guilds are unavailable.`);
}
Expand All @@ -55,7 +53,9 @@ export default abstract class EventManager {

@GatewayEvent('messageReactionAdd')
async onReactionAdd(reaction: MessageReaction, user: User | PartialUser) {
Logger.info(`${user.tag} reacted with ${reaction.emoji.name} in channel ${reaction.message.channelId} and guild ${reaction.message.guildId}.`);
Logger.info(
`${user.tag} reacted with ${reaction.emoji.name} in channel ${reaction.message.channelId} and guild ${reaction.message.guildId}.`,
);

if (user.bot || !reaction.message.inGuild()) return;

Expand Down Expand Up @@ -237,16 +237,16 @@ export default abstract class EventManager {

@GatewayEvent('messageCreate')
async onMessageCreate(message: Message): Promise<void> {
if (message.author?.bot || message.system || message.webhookId) return;
if (message.author?.bot || message.system || message.webhookId || !message.inGuild()) return;

const { connectionCache, cachePopulated } = message.client;
const { connectionCache, cachePopulated, getUserLocale } = message.client;

while (!cachePopulated) {
Logger.debug('[InterChat]: Cache not populated, retrying in 5 seconds...');
await wait(5000);
}

const locale = await message.client.getUserLocale(message.author.id);
const locale = await getUserLocale(message.author.id);
message.author.locale = locale;

// check if the message was sent in a network channel
Expand All @@ -267,198 +267,33 @@ export default abstract class EventManager {
return;
}

const censoredContent = censor(message.content);

// fetch the referred message (message being replied to) from discord
const referredMessage = message.reference
? await message.fetchReference().catch(() => undefined)
: undefined;

// check if it was sent in the network
const referenceInDb = referredMessage
? (
await db.broadcastedMessages.findFirst({
where: { messageId: referredMessage?.id },
include: { originalMsg: { include: { broadcastMsgs: true } } },
})
)?.originalMsg
: undefined;

let referredContent: string | undefined = undefined;
let referredAuthor: User | null = null;

// only assign to this variable if one of these two conditions are true, not always
if (referredMessage) {
if (referredMessage?.author.id === message.client.user.id) {
referredContent = getReferredContent(referredMessage);
referredAuthor = message.client.user;
}
else if (referenceInDb) {
referredContent = getReferredContent(referredMessage);
referredAuthor = await message.client.users.fetch(referenceInDb.authorId).catch(() => null);
}
}

const username = (
settings.has('UseNicknames')
? message.member?.displayName || message.author.displayName
: message.author.username
)
.slice(0, 35)
.replace(REGEX.BANNED_WEBHOOK_WORDS, '[censored]');
? await message.fetchReference().catch(() => null)
: null;

const servername = message.guild?.name
.slice(0, 35)
.replace(REGEX.BANNED_WEBHOOK_WORDS, '[censored]');
const { dbReferrence, referredAuthor } = await getReferredMsgData(referredMessage);

// embeds for the normal mode
const { embed, censoredEmbed } = buildNetworkEmbed(message, username, censoredContent, {
const sendResult = broadcastMessage(message, hub, hubConnections, settings, {
attachmentURL,
referredContent,
embedCol: (connection.embedColor as HexColorString) ?? undefined,
dbReferrence,
referredAuthor,
referredMessage,
embedColor: connection.embedColor as HexColorString,
});

// ---------- Broadcasting ---------
const sendResult = hubConnections.map(async (otherConnection) => {
try {
const reply = referenceInDb?.broadcastMsgs.find(
(msg) => msg.channelId === otherConnection.channelId,
);
// create a jump to reply button
const jumpButton =
reply && referredMessage?.author
? new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setEmoji(emojis.reply)
.setURL(
`https://discord.com/channels/${otherConnection.serverId}/${reply.channelId}/${reply.messageId}`,
)
.setLabel(
referredAuthor?.username && referredAuthor.username.length >= 80
? `@${referredAuthor.username.slice(0, 76)}...`
: `@${referredAuthor?.username}`,
),
)
: null;

// embed format
let messageFormat: WebhookMessageCreateOptions = {
components: jumpButton ? [jumpButton] : undefined,
embeds: [otherConnection.profFilter ? censoredEmbed : embed],
username: `${hub.name}`,
avatarURL: hub.iconUrl,
threadId: otherConnection.parentId ? otherConnection.channelId : undefined,
allowedMentions: { parse: [] },
};

if (otherConnection.compact) {
const replyContent =
otherConnection.profFilter && referredContent
? censor(referredContent)
: referredContent;

// preview embed for the message being replied to
const replyEmbed = replyContent
? new EmbedBuilder({
description: replyContent,
author: {
name: `${referredAuthor?.username?.slice(0, 30)}`,
icon_url: referredAuthor?.displayAvatarURL(),
},
}).setColor('Random')
: undefined;

// compact format (no embeds, only content)
messageFormat = {
username: `@${username}${servername}`,
avatarURL: message.author.displayAvatarURL(),
embeds: replyEmbed ? [replyEmbed] : undefined,
components: jumpButton ? [jumpButton] : undefined,
content:
(otherConnection.profFilter ? censoredContent : message.content) +
// append the attachment url if there is one
`${attachment ? `\n${attachmentURL}` : ''}`,
// username is already limited to 50 characters, server name is limited to 40 (char limit is 100)
threadId: otherConnection.parentId ? otherConnection.channelId : undefined,
allowedMentions: { parse: [] },
};
}
// send the message
const messageOrError = await sendMessage(messageFormat, otherConnection.webhookURL);

// return the message and webhook URL to store the message in the db
return {
messageOrError: messageOrError,
webhookURL: otherConnection.webhookURL,
} as NetworkWebhookSendResult;
}
catch (e) {
// return the error and webhook URL to store the message in the db
return {
messageOrError: e.message,
webhookURL: otherConnection.webhookURL,
} as NetworkWebhookSendResult;
}
});
// only delete the message if there is no attachment or if the user has already viewed the welcome message
// deleting attachments will make the image not show up in the embed (discord removes it from its cdn)
if (!attachment) message.delete().catch(() => null);

const userData = await db.userData.findFirst({
where: { userId: message.author.id, viewedNetworkWelcome: true },
});

if (!userData) {
await db.userData.upsert({
where: { userId: message.author.id },
create: {
userId: message.author.id,
username: message.author.username,
viewedNetworkWelcome: true,
},
update: { viewedNetworkWelcome: true },
});

const linkButtons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setEmoji(emojis.add_icon)
.setLabel('Invite Me!')
.setURL(LINKS.APP_DIRECTORY),
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setEmoji(emojis.code_icon)
.setLabel('Support Server')
.setURL(LINKS.SUPPORT_INVITE),
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setEmoji(emojis.docs_icon)
.setLabel('How-To Guide')
.setURL(LINKS.DOCS),
);

await message.channel
.send({
content: t(
{ phrase: 'network.welcome', locale },
{
user: message.author.toString(),
hub: hub.name,
channel: message.channel.toString(),
totalServers: hubConnections.size.toString(),
emoji: emojis.wave_anim,
rules_command: '</rules:924659340898619395>',
},
),
components: [linkButtons],
})
.catch(() => null);
}

// only delete the message if there is no attachment or if the user has already viewed the welcome message
// deleting attachments will make the image not show up in the embed (discord removes it from its cdn)
if (!attachment) message.delete().catch(() => null);
if (!userData) await sendWelcomeMsg(message, hubConnections.size.toString());

// store the message in the db
await storeMessageData(message, await Promise.all(sendResult), connection.hubId, referenceInDb);
await storeMessageData(message, await Promise.all(sendResult), connection.hubId, dbReferrence);
}

@GatewayEvent('interactionCreate')
Expand Down
6 changes: 5 additions & 1 deletion src/scripts/hub/manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { CustomID } from '../../utils/CustomID.js';
import db from '../../utils/Db.js';
import { supportedLocaleCodes, t } from '../../utils/Locale.js';

export const actionsSelect = (hubId: string, userId: string, locale: supportedLocaleCodes = 'en') => {
export const actionsSelect = (
hubId: string,
userId: string,
locale: supportedLocaleCodes = 'en',
) => {
return new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId(
Expand Down
Loading

0 comments on commit 61acfd0

Please sign in to comment.