Skip to content

Commit

Permalink
feat: hub logging, reporting messages, encrypted customIds, join serv…
Browse files Browse the repository at this point in the history
…er button

* feat(hub): hub logging

wip

* Delete hub logs command

* feat(hub): add report button, logging system, join server button

* fix: fix ordering of logs selects

* feat: add server join log for /hub browse
  • Loading branch information
dev-737 authored Dec 31, 2023
1 parent 94187f8 commit f0ae9dd
Show file tree
Hide file tree
Showing 31 changed files with 1,001 additions and 499 deletions.
2 changes: 1 addition & 1 deletion locales
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dotenv": "^16.3.1",
"i18n": "^0.15.1",
"lodash": "^4.17.21",
"lz-string": "^1.5.0",
"nsfwjs": "^2.4.2",
"parse-duration": "^1.1.0",
"winston": "^3.11.0",
Expand Down
13 changes: 9 additions & 4 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ type HubModerator {
// }

type HubLogChannels {
reports String?
modLogs String?
joinLeave String?
profanity String?
modLogs String?
joinLeaves String?
profanity String?
reports hubLogReports?
}

type hubLogReports {
channelId String
roleId String?
}

type hubBlacklist {
Expand Down
10 changes: 10 additions & 0 deletions src/InterChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { stripIndents } from 'common-tags';
import { LINKS, channels, colors, emojis, mascotEmojis } from './utils/Constants.js';
import { initI18n } from './utils/Locale.js';
import HubLogsManager from './managers/HubLogsManager.js';

class InterChat extends SuperClient {
public constructor() {
Expand Down Expand Up @@ -169,8 +170,17 @@ class InterChat extends SuperClient {
if (!guild.available) return;

Logger.info(`Left ${guild.name} (${guild.id})`);

// find all connections that belong to this guild
const connections = await db.connectedList.findMany({ where: { serverId: guild.id } });
// delete them from the database
await db.connectedList.deleteMany({ where: { serverId: guild.id } });

// send server leave log to hubs
connections.forEach((connection) =>
new HubLogsManager(connection.hubId).logServerLeave(guild),
);

const count = (await this.cluster.fetchClientValues('guilds.cache.size')) as number[];
const guildCount = count.reduce((p, n) => p + n, 0);
const guildOwner = await this.users.fetch(guild.ownerId).catch(() => null);
Expand Down
14 changes: 7 additions & 7 deletions src/commands/context-menu/blacklist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import db from '../../utils/Db.js';
import parse from 'parse-duration';
import BaseCommand from '../BaseCommand.js';
import NetworkLogger from '../../utils/NetworkLogger.js';
import HubLogsManager from '../../managers/HubLogsManager.js';
import { t } from '../../utils/Locale.js';
import { colors, emojis } from '../../utils/Constants.js';
import { CustomID } from '../../utils/CustomID.js';
Expand Down Expand Up @@ -108,7 +108,7 @@ export default class Blacklist extends BaseCommand {
.setTitle('Blacklist')
.setCustomId(
new CustomID()
.setIdentifier('blacklist_modal', customId.postfix)
.setIdentifier('blacklist_modal', customId.suffix)
.addArgs(originalMsgId)
.toString(),
)
Expand Down Expand Up @@ -181,7 +181,7 @@ export default class Blacklist extends BaseCommand {
const blacklistManager = interaction.client.getBlacklistManager();

// user blacklist
if (customId.postfix === 'user') {
if (customId.suffix === 'user') {
const user = await interaction.client.users.fetch(originalMsg.authorId).catch(() => null);
successEmbed.setDescription(
t(
Expand All @@ -205,8 +205,8 @@ export default class Blacklist extends BaseCommand {
.notifyBlacklist('user', originalMsg.authorId, originalMsg.hubId, expires, reason)
.catch(() => null);

const networkLogger = new NetworkLogger(originalMsg.hubId);
await networkLogger.logBlacklist(user, interaction.user, reason, expires);
const hubLogger = await new HubLogsManager(originalMsg.hubId).init();
await hubLogger.logBlacklist(user, interaction.user, reason, expires);
}

await interaction.editReply({ embeds: [successEmbed], components: [] });
Expand Down Expand Up @@ -254,8 +254,8 @@ export default class Blacklist extends BaseCommand {
});

if (server) {
const networkLogger = new NetworkLogger(originalMsg.hubId);
await networkLogger
const hubLogger = await new HubLogsManager(originalMsg.hubId).init();
await hubLogger
.logBlacklist(server, interaction.user, reason, expires)
.catch(() => null);
}
Expand Down
142 changes: 109 additions & 33 deletions src/commands/context-menu/messageInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,25 @@ import {
ButtonBuilder,
ButtonComponent,
ButtonStyle,
CacheType,
EmbedBuilder,
MessageComponentInteraction,
MessageContextMenuCommandInteraction,
ModalBuilder,
ModalSubmitInteraction,
RESTPostAPIApplicationCommandsJSONBody,
TextInputBuilder,
TextInputStyle,
} from 'discord.js';
import db from '../../utils/Db.js';
import BaseCommand from '../BaseCommand.js';
import { profileImage } from 'discord-arts';
import { colors, emojis } from '../../utils/Constants.js';
import { REGEX, colors, emojis } from '../../utils/Constants.js';
import { CustomID } from '../../utils/CustomID.js';
import { RegisterInteractionHandler } from '../../decorators/Interaction.js';
import { t } from '../../utils/Locale.js';
import HubLogsManager from '../../managers/HubLogsManager.js';
import { simpleEmbed } from '../../utils/Utils.js';

export default class MessageInfo extends BaseCommand {
readonly data: RESTPostAPIApplicationCommandsJSONBody = {
Expand All @@ -28,10 +35,12 @@ export default class MessageInfo extends BaseCommand {

async execute(interaction: MessageContextMenuCommandInteraction) {
const target = interaction.targetMessage;
const originalMsg = (await db.broadcastedMessages.findFirst({
where: { messageId: target.id },
include: { originalMsg: { include: { hub: true, broadcastMsgs: true } } },
}))?.originalMsg;
const originalMsg = (
await db.broadcastedMessages.findFirst({
where: { messageId: target.id },
include: { originalMsg: { include: { hub: true, broadcastMsgs: true } } },
})
)?.originalMsg;

if (!originalMsg) {
await interaction.reply({
Expand All @@ -40,9 +49,6 @@ export default class MessageInfo extends BaseCommand {
});
return;
}
const guildConnected = await db.connectedList.findFirst({
where: { serverId: originalMsg.serverId },
});
const author = await interaction.client.users.fetch(originalMsg.authorId);
const server = await interaction.client.fetchGuild(originalMsg.serverId);

Expand All @@ -63,23 +69,25 @@ export default class MessageInfo extends BaseCommand {
.setThumbnail(`https://cdn.discordapp.com/icons/${server?.id}/${server?.icon}.png`)
.setColor('Random');

const buttonsArr = MessageInfo.buildButtons(target.id, interaction.user.locale);
const components = MessageInfo.buildButtons(target.id, interaction.user.locale);

const guildConnected = await db.connectedList.findFirst({
where: { serverId: originalMsg.serverId, hubId: originalMsg.hub?.id },
});

if (guildConnected?.invite) {
buttonsArr.push(
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setURL(`https://discord.gg/${guildConnected?.invite}`)
.setEmoji(emojis.join)
.setLabel('Join'),
),
components[1].addComponents(
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setURL(`https://discord.gg/${guildConnected?.invite}`)
.setEmoji(emojis.join)
.setLabel('Join Server'),
);
}

await interaction.reply({
embeds: [embed],
components: MessageInfo.buildButtons(target.id, interaction.user.locale),
components,
ephemeral: true,
});
}
Expand All @@ -90,10 +98,12 @@ export default class MessageInfo extends BaseCommand {
const customId = CustomID.parseCustomId(interaction.customId);
const messageId = customId.args[0];

const originalMsg = (await db.broadcastedMessages.findFirst({
where: { messageId },
include: { originalMsg: { include: { hub: true, broadcastMsgs: true } } },
}))?.originalMsg;
const originalMsg = (
await db.broadcastedMessages.findFirst({
where: { messageId },
include: { originalMsg: { include: { hub: true, broadcastMsgs: true } } },
})
)?.originalMsg;

if (!originalMsg) {
return await interaction.update({
Expand Down Expand Up @@ -124,7 +134,7 @@ export default class MessageInfo extends BaseCommand {
}

// button responses
switch (customId.postfix) {
switch (customId.suffix) {
// server info button
case 'serverInfo': {
if (!server) {
Expand Down Expand Up @@ -257,12 +267,70 @@ export default class MessageInfo extends BaseCommand {
break;
}

case 'report': {
const modal = new ModalBuilder()
.setCustomId(new CustomID('msgInfoModal:report', [messageId]).toString())
.setTitle('Report Message')
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId('reason')
.setLabel('Reason for report')
.setPlaceholder('Spamming text, sending NSFW content etc.')
.setStyle(TextInputStyle.Paragraph)
.setRequired(true),
),
);

await interaction.showModal(modal);
break;
}

default:
break;
}
}
}

@RegisterInteractionHandler('msgInfoModal')
async handleModals(interaction: ModalSubmitInteraction<CacheType>) {
const customId = CustomID.parseCustomId(interaction.customId);
const messageId = customId.args[0];
const messageInDb = await db.broadcastedMessages.findFirst({
where: { messageId },
include: { originalMsg: { include: { hub: true } } },
});

if (!messageInDb?.originalMsg.hub?.logChannels?.reports) {
return await interaction.reply({
embeds: [
simpleEmbed(t({ phrase: 'msgInfo.report.notEnabled', locale: interaction.user.locale })),
],
});
}

const hubLogger = await new HubLogsManager(messageInDb.originalMsg.hub.id).init();
const { authorId, serverId } = messageInDb.originalMsg;

const reason = interaction.fields.getTextInputValue('reason');
const message = await interaction.channel?.messages.fetch(messageId).catch(() => null);
const content = message?.content || message?.embeds[0].description || undefined;
const attachmentUrl = message?.embeds[0].image?.url || content?.match(REGEX.IMAGE_URL)?.at(0);

await hubLogger.logReport(authorId, serverId, reason, interaction.user, {
content,
attachmentUrl,
messageId,
});

await interaction.reply({
embeds: [
simpleEmbed(t({ phrase: 'msgInfo.report.success', locale: interaction.user.locale }, { emoji: emojis.yes })),
],
ephemeral: true,
});
}

// utility methods
static greyOutButton(buttons: ActionRowBuilder<ButtonBuilder>, disableElement: number) {
buttons.components.forEach((c) => c.setDisabled(false));
Expand All @@ -273,24 +341,32 @@ export default class MessageInfo extends BaseCommand {
return [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId(
new CustomID().setIdentifier('msgInfo', 'info').addArgs(messageId).toString(),
)
.setLabel(t({ phrase: 'msgInfo.buttons.message', locale }))
.setStyle(ButtonStyle.Secondary)
.setDisabled(true),
.setDisabled(true)
.setCustomId(
new CustomID().setIdentifier('msgInfo', 'info').addArgs(messageId).toString(),
),
new ButtonBuilder()
.setLabel(t({ phrase: 'msgInfo.buttons.server', locale }))
.setStyle(ButtonStyle.Secondary)
.setCustomId(
new CustomID().setIdentifier('msgInfo', 'serverInfo').addArgs(messageId).toString(),
)
.setLabel(t({ phrase: 'msgInfo.buttons.server', locale }))
.setStyle(ButtonStyle.Secondary),
),
new ButtonBuilder()
.setLabel(t({ phrase: 'msgInfo.buttons.user', locale }))
.setStyle(ButtonStyle.Secondary)
.setCustomId(
new CustomID().setIdentifier('msgInfo', 'userInfo').addArgs(messageId).toString(),
)
.setLabel(t({ phrase: 'msgInfo.buttons.user', locale }))
.setStyle(ButtonStyle.Secondary),
),
),
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel(t({ phrase: 'msgInfo.buttons.report', locale }))
.setStyle(ButtonStyle.Danger)
.setCustomId(
new CustomID().setIdentifier('msgInfo', 'report').addArgs(messageId).toString(),
),
),
];
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/slash/Information/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default class Stats extends BaseCommand {
async handleComponents(interaction: ButtonInteraction) {
const customId = CustomID.parseCustomId(interaction.customId);

if (customId.postfix === 'shardStats') {
if (customId.suffix === 'shardStats') {
const embed = new EmbedBuilder()
.setColor(colors.invisible)
.setDescription(
Expand Down
Loading

0 comments on commit f0ae9dd

Please sign in to comment.