diff --git a/README.md b/README.md index d37b30c..f487a9a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # thirdweb Support Bot -> **Note**: This bot is based from [thread-based-support-discord-bot](https://github.com/warengonzaga/thread-based-support-discord-bot). Converted to fully support the forum posts. - -A dedicated forum-based support Discord bot for the thirdweb community. This bot is created specifically for the community setup of thirdweb community. If you want to use the bot would recommend to use the public version of it. +A dedicated forum-based support Discord bot for the thirdweb community. This bot is created specifically for the community setup of thirdweb community. If you want to use the bot would recommend to use the [public version](https://github.com/warengonzaga/forum-based-support-discord-bot) of it. ## 🤔 How to Use diff --git a/package.json b/package.json index 8024491..ac7e0bd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "thirdweb-support-discord-bot", - "version": "1.0.0-alpha", - "description": "Discord threads-based support for the thirdweb community.", + "version": "0.6.0", + "description": "A self-hosted dedicated forum-based support Discord bot for the thirdweb community.", "main": "src/bot.ts", "author": "Waren Gonzaga", "scripts": { diff --git a/src/bot.js b/src/bot.js index 3e8f420..751225c 100644 --- a/src/bot.js +++ b/src/bot.js @@ -3,7 +3,8 @@ const { Client, ChannelType, GatewayIntentBits, - Partials } = require('discord.js'); + Partials, + EmbedBuilder } = require('discord.js'); const { GoogleSpreadsheet } = require('google-spreadsheet'); const config = require(`${__dirname}/config.json`); const moment = require('moment'); @@ -35,19 +36,45 @@ const client = new Client({ // load spreadsheet const doc = new GoogleSpreadsheet(process.env.GOOGLE_SPREADSHEET_ID); +/** + * send embed message + * @param {string} message + * @returns pre-defined embed style + */ +const sendEmbedMessage = (message) => { + return new EmbedBuilder() + .setDescription(message) + .setColor(`#f213a4`); +} + +/** + * get username from ownerid + * @param {number} id + * @returns + */ +const getUsernameFromId = async (id) => { + return (await client.users.fetch(id)).username; +} + // listen to post messages client.on('messageCreate', async (message) => { if (message.author.bot) return; // check ping if (message.content === 'ping') { - message.reply(`Pong: ${client.ws.ping}ms`); + // message.reply(`Pong: ${client.ws.ping}ms`); + message.reply({ embeds: [ + sendEmbedMessage(`Pong: ${client.ws.ping}ms`) + ] }); console.log(`[log]: responded to ping command in ${client.ws.ping}ms`); } // respond to user if the bot mentioned specifically not with everyone if (message.mentions.has(client.user) && !message.mentions.everyone) { - message.reply(config.mention_message); + // convert this to embed message.reply({config.mention_message); + message.reply({ embeds: [ + sendEmbedMessage(config.reminder_mention) + ] }); } // get the details from user who send command @@ -61,7 +88,7 @@ client.on('messageCreate', async (message) => { // check if the message is from the forum post if (typeof post.availableTags !== 'undefined') { // filter the tags to get the resolution tag name ID - const resolutionTag = post.availableTags.filter((item) => { return item.name == config.resolution_tag_name }); + const resolutionTag = post.availableTags.filter((item) => { return item.name == config.tag_name_resolve }); // get the existing tags of the post const postTags = message.channel.appliedTags; @@ -70,37 +97,60 @@ client.on('messageCreate', async (message) => { let tags = [...new Set(initialTags)]; // check if the command has the prefix and includes "close" - if (message.content.startsWith(config.command_prefix) && message.content.includes('close')) { + if (message.content.startsWith(config.command_prefix) && message.content.includes(config.command_resolve)) { await message.delete(); // delete the commmand message - // check if the channel is a thread and the user has support role + + // check if the message is in the forum post and from the support role if (message.channel.type === ChannelType.PublicThread && member.roles.cache.hasAny(...roleIDs)) { - // then archive and lock it - message.channel.edit({ - appliedTags: tags, - archived: true - }); - - // gather data - const postId = message.channel.id; - const resolutionTime = formatTime(message.createdTimestamp); - const resolvedBy = member.user.username; - - // check if there's a mentioned user - if (mention.users.first()) { - // send the data, use the mentioned user as resolvedBy - sendData({ - post_id: postId, - resolution_time: resolutionTime, - resolved_by: mention.users.first().username, - }, config.datasheet_resolve); + // check if the post has fewer tags + if (postTags.length < 5) { + + // send embed message before closing the post + message.channel.send({ embeds: [ + sendEmbedMessage(`${config.reminder_resolve}`) + ], + content: `🔔 <@${message.channel.ownerId}>` + }) + + // then archive and lock it + message.channel.edit({ + appliedTags: tags, + archived: true + }); + + // gather data + const postId = message.channel.id; + const resolutionTime = formatTime(message.createdTimestamp); + const resolvedBy = member.user.username; + + // check if there's a mentioned user + if (mention.users.first()) { + // send the data, use the mentioned user as resolvedBy + sendData({ + post_id: postId, + resolution_time: resolutionTime, + resolved_by: mention.users.first().username, + }, config.datasheet_resolve); + } else { + // send the data with the one who sends the command + sendData({ + post_id: postId, + resolution_time: resolutionTime, + resolved_by: resolvedBy + }, config.datasheet_resolve); + } + } else { - // send the data with the one who sends the command - sendData({ - post_id: postId, - resolution_time: resolutionTime, - resolved_by: resolvedBy - }, config.datasheet_resolve); + message.channel.send({ + embeds: [ + sendEmbedMessage(`${config.reminder_max_tags}`) + ], + content: `🔔 <@${message.author.id}>` + }) + .then(message => { + setTimeout(() => message.delete(), 10000) // delete message after 15s + }); } } } diff --git a/src/config.json b/src/config.json index b07f073..7c8cefd 100644 --- a/src/config.json +++ b/src/config.json @@ -1,9 +1,19 @@ { - "resolution_tag_name": "Solved", "command_prefix": "!", - "utc_offset": -8, + "command_resolve": "resolve", + "command_close": "close", + "command_escalate": "escalate", + "tag_name_resolve": "Solved", + "tag_name_close": "Closed", + "tag_name_escalate": "Escalated", "datasheet_init": "init", "datasheet_response": "response", "datasheet_resolve": "resolve", - "mention_message": "Hello there! If you need help, please read the information in <#1074862134284005396> and post your questions or issues in the <#1029543258822553680> channel. Our team and community members are always ready to help you out. Thank you for building with us!" + "utc_offset": -8, + "reminder_mention": "Hey! If you need help, please read the information in <#1074862134284005396> and post your questions or issues in the <#1029543258822553680> channel. Our team and community members are always ready to help you out. Thank you for building with us!", + "reminder_max_tags": "Max tags (5) exceeded. Please update the original post with fewer tags and try again.", + "reminder_close": "Hello! This post was closed due to inactivity, missing information, or otherwise. If you believe this was a mistake or you wish to re-open, please let us know here and we're happy to do so! For new questions, please create a new post in <#1029543258822553680>. Thank you!", + "reminder_resolve": "This post has been marked as resolved, check the pinned message here to see the solution. If you have another question, kindly create a new post in <#1029543258822553680>. Happy building!", + "reminder_reopen": "This post has been re-opened. We'll be right with you!", + "reminder_escalate": "We have escalated this to our engineering team to investigate. We'll get back to you here as soon as possible." } \ No newline at end of file