Skip to content

Commit

Permalink
feat(network): thread and forum post support (#19)
Browse files Browse the repository at this point in the history
And also:
* Prevent joining same hub from different channels

* Prevent duplicate hub joins with invite

* Fixed onboarding marker deletion
  • Loading branch information
dev-737 committed Aug 29, 2023
1 parent 03d3991 commit f5296c0
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 96 deletions.
25 changes: 10 additions & 15 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
{
"ignorePatterns": ["build/"],
"env": {
"node": true,
"es2021": true
},
"ignorePatterns": ["build/", "node_modules/"],
"env": { "node": true, "es6": true },
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": [
"@typescript-eslint"
],
"parserOptions": { "ecmaVersion": "latest" },
"plugins": [ "@typescript-eslint" ],
"root": true,

"rules": {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/indent": ["error", 2],
"@typescript-eslint/no-explicit-any": "warn",

"arrow-spacing": "error",
"brace-style": [
"error",
"stroustrup",
{
"allowSingleLine": true
}
{ "allowSingleLine": true }
],
"comma-dangle": [
"error",
Expand All @@ -43,7 +39,6 @@
],
"handle-callback-err": "off",
"indent": "off",
"@typescript-eslint/indent": ["error", 2],
"keyword-spacing": "error",
"max-nested-callbacks": [
"error",
Expand Down
3 changes: 2 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ model blacklistedUsers {

model connectedList {
id String @id @default(auto()) @map("_id") @db.ObjectId
channelId String @unique
channelId String @unique // channel can be thread, or a normal channel
parentId String? // ID of the parent channel, if it's a thread @map("parentChannelId")
serverId String
connected Boolean
compact Boolean
Expand Down
8 changes: 3 additions & 5 deletions src/Commands/Main/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default {
.addChannelOption(channelOption =>
channelOption
.setName('channel')
.addChannelTypes(ChannelType.GuildText)
.addChannelTypes(ChannelType.GuildText, ChannelType.PublicThread, ChannelType.PrivateThread)
.setDescription('The channel that will be used to connect to the hub')
.setRequired(true),
)
Expand Down Expand Up @@ -242,11 +242,9 @@ export default {
async execute(interaction: ChatInputCommandInteraction) {
const subcommand = interaction.options.getSubcommand();
const subcommandGroup = interaction.options.getSubcommandGroup();
const extra = subcommand === 'leave'
const extra = subcommand === 'leave' || subcommand === 'delete'
? interaction.options.getString('hub', true)
: subcommand === 'delete'
? interaction.options.getString('hub', true)
: null;
: null;

require(`../../Scripts/hub/${subcommandGroup || subcommand}`).execute(interaction, extra);
},
Expand Down
2 changes: 2 additions & 0 deletions src/Events/messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default {
content: connection?.profFilter ? message.censored_content : message.content,
embeds: replyEmbed ? [replyEmbed] : undefined,
files: attachment ? [attachment] : [],
threadId: connection.parentId ? connection.channelId : undefined,
allowedMentions: { parse: [] },
};
}
Expand All @@ -115,6 +116,7 @@ export default {
username: message.client.user.username,
avatarURL: message.client.user.avatarURL() || undefined,
files: attachment ? [attachment] : [],
threadId: connection.parentId ? connection.channelId : undefined,
allowedMentions: { parse: [] },
};
}
Expand Down
26 changes: 13 additions & 13 deletions src/Scripts/hub/join.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { ChatInputCommandInteraction, ChannelType } from 'discord.js';
import { getDb } from '../../Utils/functions/utils';
import initialize from '../network/initialize';
import createConnection from '../network/createConnection';
import displaySettings from '../network/displaySettings';
import { connectedList, hubs } from '@prisma/client';

export async function execute(interaction: ChatInputCommandInteraction) {
if (!interaction.inCachedGuild()) return;

const db = getDb();
const name = interaction.options.getString('name') || undefined;
const invite = interaction.options.getString('invite') || undefined;
const channel = interaction.options.getChannel('channel', true, [ChannelType.GuildText]);
const channelConnected = await db.connectedList.findFirst({ where: { channelId: channel.id } });
let hubExists: hubs | null = null;
const channel = interaction.options.getChannel('channel', true, [ChannelType.GuildText, ChannelType.PublicThread, ChannelType.PrivateThread]);
let hubExists;

if (!interaction.member.permissionsIn(channel).has(['ManageChannels'])) {
return await interaction.reply({
Expand All @@ -27,9 +25,10 @@ export async function execute(interaction: ChatInputCommandInteraction) {
ephemeral: true,
});
}
const channelConnected = await db.connectedList.findFirst({ where: { channelId: channel.id } });
if (channelConnected) {
return await interaction.reply({
content: `${channel} is already connected to a hub! Please leave the hub or choose a different channel.`,
content: `${channel} is already part of a hub! Please leave the hub or choose a different channel.`,
ephemeral: true,
});
}
Expand All @@ -46,9 +45,10 @@ export async function execute(interaction: ChatInputCommandInteraction) {
ephemeral: true,
});
}
else if (inviteExists.hub.connections.find((c) => c.channelId === channel.id)) {
const guildInHub = inviteExists.hub.connections.find((c) => c.serverId === channel.guildId);
if (guildInHub) {
return await interaction.reply({
content: `This server is already connected to hub **${inviteExists.hub.name}** from another channel!`,
content: `This server has already joined hub **${inviteExists.hub.name}** from from <#${guildInHub.channelId}>! Please leave the hub from that channel first, or change the channel using \`/network manage\`.!`,
ephemeral: true,
});
}
Expand All @@ -69,9 +69,10 @@ export async function execute(interaction: ChatInputCommandInteraction) {
});
}

else if ((hubExists as hubs & { connections: connectedList}).connections.channelId === channel.id) {
const guildInHub = hubExists.connections.find(c => c.serverId === channel.guildId);
if (guildInHub) {
return await interaction.reply({
content: `This server is already connected to hub **${hubExists?.name}** from another channel!`,
content: `This server has already joined hub **${hubExists?.name}** from <#${guildInHub.channelId}>! Please leave the hub from that channel first, or change the channel using \`/network manage\`.`,
ephemeral: true,
});
}
Expand Down Expand Up @@ -110,7 +111,6 @@ export async function execute(interaction: ChatInputCommandInteraction) {
return;
}

// TODO: make an onboarding function and show them rules and stuff
initialize.execute(interaction, hubExists, channel)
.then(success => { if (success) displaySettings.execute(interaction, channel.id); });
createConnection.execute(interaction, hubExists, channel)
.then(success => { if (success) displaySettings.execute(interaction, success.channelId); });
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { stripIndents } from 'common-tags';
import { ChannelType, ChatInputCommandInteraction, Collection, GuildTextBasedChannel } from 'discord.js';
import { ChannelType, ChatInputCommandInteraction, Collection, TextChannel, ThreadChannel } from 'discord.js';
import { disconnect } from '../../Structures/network';
import { hubs } from '@prisma/client';
import logger from '../../Utils/logger';
Expand All @@ -8,13 +8,16 @@ import { getDb } from '../../Utils/functions/utils';

const onboardingInProgress = new Collection<string, string>();

export = {
async execute(interaction: ChatInputCommandInteraction, hub: hubs, networkChannel: GuildTextBasedChannel) {
export default {
async execute(interaction: ChatInputCommandInteraction, hub: hubs, networkChannel: TextChannel | ThreadChannel) {
const emoji = interaction.client.emotes.normal;

// Check if server is already attempting to join a hub
if (onboardingInProgress.has(networkChannel.id)) {
const err = `${emoji.no} There has already been an attempt to join a hub in ${networkChannel}. Please wait for that to finish before trying again!`;
const err = {
content: `${emoji.no} There has already been an attempt to join a hub in ${networkChannel}. Please wait for that to finish before trying again!`,
ephemeral: true,
};
interaction.deferred || interaction.replied
? interaction.followUp(err)
: interaction.reply(err);
Expand All @@ -24,43 +27,60 @@ export = {
onboardingInProgress.set(networkChannel.id, networkChannel.id);

// Show new users rules & info about network
const onboardingStatus = await onboarding.execute(interaction);
if (!onboardingStatus) {
onboardingInProgress.delete(networkChannel.id);
return;
}
const onboardingStatus = await onboarding.execute(interaction, hub.name);
// remove in-progress marker as onboarding has either been cancelled or completed
onboardingInProgress.delete(networkChannel.id);
// if user cancelled onboarding or didn't click any buttons, stop here
if (!onboardingStatus) return;

let createdConnection;
try {
if (networkChannel.type !== ChannelType.GuildText) {
interaction.followUp(`${emoji.no} You can only connect **text channels** to the InterChat network!`);
return;
let webhook;
if (networkChannel.isThread() && networkChannel.parent) {
const webhooks = await networkChannel.parent.fetchWebhooks();
const webhookCreated = webhooks.find(w => w.owner?.id === interaction.client.user?.id);

if (webhookCreated) {
webhook = webhookCreated;
}
else {
webhook = await networkChannel.parent.createWebhook({
name: 'InterChat Network',
avatar: interaction.client.user?.avatarURL(),
});
}
}
else if (networkChannel.type === ChannelType.GuildText) {
webhook = await networkChannel.createWebhook({
name: 'InterChat Network',
avatar: interaction.client.user?.avatarURL(),
});
}
else {
return interaction.followUp('This channel is not supported for InterChat. Please use a text channel or a thread.');
}

const webhook = await networkChannel.createWebhook({
name: 'InterChat Network',
avatar: interaction.client.user?.avatarURL(),
});

const { connectedList } = getDb();
await connectedList.create({
createdConnection = await connectedList.create({
data:{
channelId: networkChannel.id,
parentId: networkChannel.isThread() ? networkChannel.id : undefined,
serverId: networkChannel.guild.id,
webhookURL: webhook.url,
connected: true,
profFilter: true,
compact: false,
webhookURL: webhook.url,
hub: { connect: { id: hub.id } },
},
});

const numOfConnections = await connectedList.count({ where: { hub: { id: hub.id } } });
if (numOfConnections > 1) {
await networkChannel?.send(`This channel has been connected with ${hub.name}. You are currently with ${numOfConnections - 1} other servers, Enjoy! ${emoji.clipart}`);
}
else {
await networkChannel?.send(`This channel has been connected with ${hub.name}, though no one else is there currently... *cricket noises* ${emoji.clipart}`);
}
await networkChannel?.send(`This channel has been connected with **${hub.name}**. ${
numOfConnections > 1
? `You are currently with ${numOfConnections - 1} other servers, Enjoy! ${emoji.clipart}`
: `It seems no one else is there currently... *cricket noises* ${emoji.clipart}`
}`);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
catch (err: any) {
Expand All @@ -72,7 +92,7 @@ export = {
: interaction.reply(errMsg);
}
else {
const errMsg = `An error occurred while connecting to the InterChat network! \`\`\`js\n${err.message}\`\`\``;
const errMsg = `An error occurred while connecting to the InterChat network! Please report this to the developers: \`\`\`js\n${err.message}\`\`\``;
interaction.deferred || interaction.replied
? interaction.followUp(errMsg)
: interaction.reply(errMsg);
Expand All @@ -88,6 +108,8 @@ export = {
**Server Name:** __${interaction.guild?.name}__
**Member Count:** __${interaction.guild?.memberCount}__
`, { id: hub.id });
return true; // just a marker to show that the setup was successful

// return the created connection so we can use it in the next step
return createdConnection;
},
};
Loading

0 comments on commit f5296c0

Please sign in to comment.