diff --git a/.env.example b/.env.example index c715e9bf2..20e896ebb 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,5 @@ CLIENT_ID="bot id here" TOKEN="bot token here" MONGODB_URI="mongodb connection url here" # Format: mongodb+srv://:@uri/dbname TOPGG="your topgg bot access token>" # not required if you don't have a bot on top.gg. -TENOR_KEY="tenor api key" # required for posting gifs in global chat. \ No newline at end of file +TENOR_KEY="tenor api key" # required for posting gifs in global chat. +SENTRY_DSN="your sentry dsn link" # get it from sentry.io \ No newline at end of file diff --git a/src/Commands/Main/help.ts b/src/Commands/Main/help.ts index faa21dec5..712c925a5 100644 --- a/src/Commands/Main/help.ts +++ b/src/Commands/Main/help.ts @@ -24,7 +24,7 @@ export default { .setDescription( '[Invite](https://discord.com/api/oauth2/authorize?client_id=769921109209907241&permissions=154820537425&scope=bot%20applications.commands) • [Support](https://discord.gg/6bhXQynAPs) • [Privacy](https://discord-chatbot.gitbook.io/chatbot/important/privacy)') .setFields(interaction.client.commandsArray) - .setFooter({ text: 'Use /help command to get more info about a command.' }) + .setFooter({ text: 'Use /help to get more info about a command.' }) .setColor(colors('chatbot')) .setTimestamp(); diff --git a/src/Commands/Main/setup.ts b/src/Commands/Main/setup.ts index fbe618ea9..da6e2620c 100644 --- a/src/Commands/Main/setup.ts +++ b/src/Commands/Main/setup.ts @@ -1,6 +1,5 @@ import { SlashCommandBuilder, PermissionFlagsBits, ChatInputCommandInteraction, ChannelType } from 'discord.js'; import { getDb } from '../../Utils/functions/utils'; -import logger from '../../Utils/logger'; import init from '../../Scripts/setup/init'; import reset from '../../Scripts/setup/reset'; import displayEmbed from '../../Scripts/setup/displayEmbed'; @@ -45,7 +44,7 @@ export default { switch (subcommand) { case 'view': displayEmbed.execute(interaction, database); break; case 'reset': reset.execute(interaction, database); break; - default: init.execute(interaction, database).catch(logger.error); + default: init.execute(interaction, database); } }, }; diff --git a/src/Commands/Network/deleteMsg.ts b/src/Commands/Network/deleteMsg.ts index aaf37f65a..90fbe0309 100644 --- a/src/Commands/Network/deleteMsg.ts +++ b/src/Commands/Network/deleteMsg.ts @@ -25,7 +25,7 @@ export default { return interaction.reply({ content: `${emojis.no} You are not the author of this message.`, ephemeral: true }); } - await interaction.reply({ content: `${emojis.yes} Deletion in progess! This may take a couple of seconds.`, ephemeral: true }).catch(() => null); + await interaction.reply({ content: `${emojis.yes} Deletion in progess! This may take a few seconds.`, ephemeral: true }).catch(() => null); messageInDb.channelAndMessageIds.forEach((element) => { if (!element) return; diff --git a/src/Events/interactionCreate.ts b/src/Events/interactionCreate.ts index f8085fa69..9c110550b 100644 --- a/src/Events/interactionCreate.ts +++ b/src/Events/interactionCreate.ts @@ -16,7 +16,7 @@ export default { if ( interaction.inCachedGuild() && - !interaction.channel?.permissionsFor(interaction.guild?.members.me as GuildMember) + !interaction.channel?.permissionsFor(interaction.guild.members.me as GuildMember) .has(requiredPerms) ) { return interaction.reply({ diff --git a/src/Scripts/message/checks.ts b/src/Scripts/message/checks.ts index 7f972c466..fa32201e2 100644 --- a/src/Scripts/message/checks.ts +++ b/src/Scripts/message/checks.ts @@ -45,9 +45,7 @@ export = { } if (message.stickers.size > 0 && !message.content) { - message.reply( - 'Unfortunately, the sending of stickers within the network is not a feature that is currently available. We apologize for any inconvenience this may cause.', - ); + message.reply('Sending stickers within the network is not a feature that is currently available. We apologize for any inconvenience this may cause.'); return false; } diff --git a/src/Scripts/message/messageTypes.ts b/src/Scripts/message/messageTypes.ts index 2e651c3ce..29a83b798 100644 --- a/src/Scripts/message/messageTypes.ts +++ b/src/Scripts/message/messageTypes.ts @@ -18,7 +18,7 @@ export = { const channelInSetup = await db?.setup?.findFirst({ where: { channelId: channel?.channelId } }); const channelToSend = await message.client.channels.fetch(channel.channelId).catch(() => null) as GuildTextBasedChannel | null; - if (!channelToSend) return { unkownChannelId: channel?.channelId } as InvalidChannelId; + if (!channelToSend) return { unknownChannelId: channel?.channelId } as InvalidChannelId; const replyInDb = replyData?.channelAndMessageIds.find((msg) => msg.channelId === channel.channelId); @@ -77,9 +77,13 @@ const createWebhookOptions = (message: NetworkMessage, attachments: AttachmentBu allowedMentions: { parse: ['users'] }, }; - channelInSetup?.compact - ? webhookMessage.content = channelInSetup?.profFilter ? message.censored_content : message.content - : webhookMessage.embeds = [channelInSetup?.profFilter ? censoredEmbed : embed]; - + if (channelInSetup.compact) { + webhookMessage.content = channelInSetup?.profFilter ? message.censored_content : message.content; + } + else { + webhookMessage.embeds = [channelInSetup?.profFilter ? censoredEmbed : embed]; + webhookMessage.username = message.client.user.username; + webhookMessage.avatarURL = message.client.user.avatarURL() || undefined; + } return webhookMessage; }; diff --git a/src/Scripts/setup/displayEmbed.ts b/src/Scripts/setup/displayEmbed.ts index e9150484c..eb4c6fa18 100644 --- a/src/Scripts/setup/displayEmbed.ts +++ b/src/Scripts/setup/displayEmbed.ts @@ -64,15 +64,12 @@ export = { const guildConnected = await network.getServerData({ serverId: interaction.guild?.id }); if (!guildSetup) return interaction.followUp(`${emoji.normal.no} Server is not setup yet. Use \`/setup channel\` first.`); - if (!interaction.guild?.channels.cache.get(guildSetup?.channelId)) { - await setupCollection.delete({ where: { channelId: guildSetup?.channelId } }); - return await interaction.followUp(`${emoji.normal.no} Network channel not found. Use \`/setup channel\` to set a new one.`); - } - if (!guildConnected) setupActionButtons.components.at(-1)?.setDisabled(true); const setupMessage = await interaction.editReply({ - content: '', + content: interaction.guild?.channels.cache.get(guildSetup?.channelId) + ? '' + : `${emoji.normal.no} Automatically disconnected due to error receiving network messages. Change the channel to use the network.`, embeds: [await setupEmbed.default()], components: [customizeMenu, setupActionButtons], }); @@ -80,7 +77,7 @@ export = { const filter = (m: Interaction) => m.user.id === interaction.user.id; const buttonCollector = setupMessage.createMessageComponentCollector({ filter, - time: 60_000, + idle: 60_000, componentType: ComponentType.Button, }); @@ -97,14 +94,14 @@ export = { case 'compact': await setupCollection?.updateMany({ where: { guildId: interaction.guild?.id }, - data: { date: new Date(), compact: !guildSetup?.compact }, + data: { compact: !guildSetup?.compact }, }); break; case 'profanity': await setupCollection?.updateMany({ where: { guildId: interaction.guild?.id }, - data: { date: new Date(), profFilter: !guildSetup?.profFilter }, + data: { profFilter: !guildSetup?.profFilter }, }); break; @@ -115,7 +112,7 @@ export = { if (connectedChannel?.type !== ChannelType.GuildText) { await settingsMenu.reply({ - content: 'Cannot edit setup for selected channel. If you think this is a mistake report this to the developers.', + content: 'Cannot edit setup for selected channel. If you think this is a mistake report it to the developers.', ephemeral: true, }); break; @@ -129,7 +126,7 @@ export = { guildSetup = await setupCollection?.update({ where: { channelId: connectedChannel.id }, - data: { date: new Date(), webhook: null }, + data: { webhook: null }, }); await settingsMenu.reply({ @@ -151,10 +148,7 @@ export = { }); } catch { - interaction.reply({ - content: 'Please grant me `Manage Webhook` permissions for this to work.', - ephemeral: true, - }); + interaction.editReply('Please grant me `Manage Webhook` permissions for this to work.'); return; } @@ -163,7 +157,6 @@ export = { await setupCollection?.updateMany({ where: { guildId: interaction.guild?.id }, data: { - date: new Date(), webhook: { set: { id: webhook.id, token: `${webhook.token}`, url: webhook.url } }, }, }); @@ -249,14 +242,33 @@ export = { componentType: ComponentType.ChannelSelect, idle: 20_000, }); - newChannelSelect.once('collect', async (SelectInteraction) => { - await network.updateData({ channelId: guildSetup?.channelId }, { channelId: SelectInteraction?.values[0] }); + newChannelSelect.once('collect', async (select) => { + const oldchannel = select.guild?.channels.cache.get(`${guildSetup?.channelId}`); + const channel = select.guild?.channels.cache.get(select?.values[0]); + let webhook = undefined; + + if (oldchannel?.type !== ChannelType.GuildText || channel?.type !== ChannelType.GuildText) return; // so TS doesnt complain + + if (guildSetup?.webhook) { + // delete the old webhook + oldchannel?.fetchWebhooks().then(promisehook => { + promisehook.find((hook) => hook.owner?.id === hook.client.user?.id)?.delete().catch(() => null); + }).catch(() => null); + + // create a webhook in the new channel + webhook = await channel.createWebhook({ name: 'ChatBot Network', avatar: select.client.user.avatarURL() }); + } + + await network.updateData({ channelId: guildSetup?.channelId }, { channelId: select?.values[0] }); guildSetup = await setupCollection.update({ where: { channelId: guildSetup?.channelId }, - data: { channelId: SelectInteraction?.values[0] }, + data: { + channelId: channel.id, + webhook: webhook ? { set: { id: webhook.id, token: `${webhook.token}`, url: webhook.url } } : null, + }, }); - await SelectInteraction?.update({ + await select?.update({ content: 'Channel successfully set!', components: [], }); @@ -265,6 +277,8 @@ export = { break; } } + await setupCollection.updateMany({ where: { guildId: interaction.guild?.id }, data: { date: new Date() } }); + settingsMenu.replied || settingsMenu.deferred ? interaction.editReply({ embeds: [await setupEmbed.default()] }) : settingsMenu.update({ embeds: [await setupEmbed.default()] }); @@ -355,19 +369,19 @@ class SetupEmbedGenerator { { name: 'Network State', value: stripIndent` - **Connected:** ${status} - **Channel:** ${channel} - **Last Edited:** - `, + **Connected:** ${status} + **Channel:** ${channel || 'None.'} + **Last Edited:** + `, inline: true, }, { name: 'Style', value: stripIndent` **Compact:** ${compact} - **Profanity Filter:** ${profanity} + **Profanity Filter:** ${profanity} **Webhook Messages:** ${webhook} - `, + `, inline: true, }, { diff --git a/src/Scripts/setup/init.ts b/src/Scripts/setup/init.ts index 2fa78c306..facdb2434 100644 --- a/src/Scripts/setup/init.ts +++ b/src/Scripts/setup/init.ts @@ -38,32 +38,34 @@ export = { if (numOfConnections > 1) { await destination?.send(stripIndents` This channel has been connected to the chat network. You are currently with ${numOfConnections} other servers, Enjoy! ${emoji.normal.clipart} - **⚠️ This is not an __AI Chat__, but a chat network that allows you to connect to multiple servers and communicate with *__real__* members. ⚠️**`); + **⚠️ This is not an __AI Chat__, but a chat network that allows you to connect to multiple servers and communicate with *__real__* members. ⚠️**`); } else { await destination?.send(stripIndents` - This channel has been connected to the chat network, though no one else is there currently... *cricket noises* ${emoji.normal.clipart} - **⚠️ This is not an __AI Chat__, but a chat network that allows you to connect to multiple servers and communicate with *__real__* members. ⚠️**`); + This channel has been connected to the chat network, though no one else is there currently... *cricket noises* ${emoji.normal.clipart} + **⚠️ This is not an __AI Chat__, but a chat network that allows you to connect to multiple servers and communicate with *__real__* members. ⚠️**`); } logger.info(`${interaction.guild?.name} (${interaction.guildId}) has joined the network.`); } // eslint-disable-next-line @typescript-eslint/no-explicit-any catch (err: any) { + logger.error(err); if (err.message === 'Missing Permissions' || err.message === 'Missing Access') { - return interaction.reply('I don\'t have the required permissions and/or access to the selected channel to execute this command.'); + interaction.editReply(`${emoji.normal.no} I don't have the required permissions and/or access to the selected channel to execute this command.`); + } + else { + interaction.followUp(`An error occurred while connecting to the chat network! \`\`\`js\n${err.message}\`\`\``); } - logger.error(err); - interaction.followUp(`An error occurred while connecting to the chat network! \`\`\`js\n${err.message}\`\`\``); network.disconnect(interaction.guild?.id as string); await setupList?.delete({ where: { channelId: destination?.id } }); return; } interaction.client.sendInNetwork(stripIndents` - A new server has joined us in the Network! ${emoji.normal.clipart} + A new server has joined us in the Network! ${emoji.normal.clipart} - **Server Name:** __${interaction.guild?.name}__ - **Member Count:** __${interaction.guild?.memberCount}__ + **Server Name:** __${interaction.guild?.name}__ + **Member Count:** __${interaction.guild?.memberCount}__ `); displayEmbed.execute(interaction, database); diff --git a/src/Structures/client.ts b/src/Structures/client.ts index 03d0fc814..00f0858e3 100644 --- a/src/Structures/client.ts +++ b/src/Structures/client.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import logger from '../Utils/logger'; import emojis from '../Utils/JSON/emoji.json'; import project from '../../package.json'; -import { Client, Collection, ActivityType, EmbedBuilder, TextChannel, MessageCreateOptions } from 'discord.js'; +import { Client, Collection, ActivityType, EmbedBuilder, MessageCreateOptions, GuildTextBasedChannel } from 'discord.js'; import { join } from 'path'; import { colors, constants } from '../Utils/functions/utils'; import { prisma } from '../Utils/db'; @@ -52,10 +52,10 @@ export class ChatBot extends Client { }); } - public async sendErrorToChannel(client: Client, embedTitle: string, ErrorStack: unknown, channel?: TextChannel | null) { - const errorChannel = await client.channels.fetch(constants.channel.errorlogs); + public async sendErrorToChannel(embedTitle: string, ErrorStack: unknown, channel?: GuildTextBasedChannel) { + const errorChannel = await this.channels.fetch(constants.channel.errorlogs); const errorEmbed = new EmbedBuilder() - .setAuthor({ name: 'ChatBot Error Logs', iconURL: client.user?.avatarURL() || undefined }) + .setAuthor({ name: 'ChatBot Error Logs', iconURL: this.user?.avatarURL() || undefined }) .setTitle(embedTitle) .setDescription('```js\n' + ErrorStack + '```') .setColor(colors('invisible')) diff --git a/src/Structures/network.ts b/src/Structures/network.ts index 25399056b..9a9d69d84 100644 --- a/src/Structures/network.ts +++ b/src/Structures/network.ts @@ -29,7 +29,6 @@ export class NetworkManager { /** * Insert a guild & channel into connectedList collection. - * Returns **null** if channel is already connected */ public async connect(guild: Guild | null, channel: GuildTextBasedChannel | undefined | null) { const channelExists = await prisma.connectedList.findFirst({ @@ -38,7 +37,7 @@ export class NetworkManager { }, }); - if (channelExists) return null; + if (channelExists) return undefined; if (!guild || !channel) throw new Error('Invalid arguments provided.'); return await prisma.connectedList.create({ diff --git a/src/Utils/functions/paginator.ts b/src/Utils/functions/paginator.ts index 5a81fc4be..b60740bee 100644 --- a/src/Utils/functions/paginator.ts +++ b/src/Utils/functions/paginator.ts @@ -34,7 +34,7 @@ export async function paginate(interaction: CommandInteraction, pages: EmbedBuil }; const listMessage = interaction.replied ? await interaction.followUp(data) : await interaction.reply(data); - const col = listMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, time, componentType: ComponentType.Button }); + const col = listMessage.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id, idle: time, componentType: ComponentType.Button }); col.on('collect', (i) => { if (i.customId === '1') {--pagenumber, index--;}